@bastani/atomic 0.9.9-alpha.1 → 0.9.9-alpha.2

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.
Files changed (534) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +6 -0
  3. package/dist/builtin/cursor/package.json +3 -3
  4. package/dist/builtin/intercom/CHANGELOG.md +7 -0
  5. package/dist/builtin/intercom/package.json +3 -3
  6. package/dist/builtin/mcp/CHANGELOG.md +7 -0
  7. package/dist/builtin/mcp/package.json +4 -4
  8. package/dist/builtin/subagents/CHANGELOG.md +7 -0
  9. package/dist/builtin/subagents/package.json +5 -5
  10. package/dist/builtin/web-access/CHANGELOG.md +7 -0
  11. package/dist/builtin/web-access/package.json +3 -3
  12. package/dist/builtin/web-access/web-search-tool.ts +1 -2
  13. package/dist/builtin/workflows/CHANGELOG.md +7 -0
  14. package/dist/builtin/workflows/package.json +3 -3
  15. package/dist/builtin/workflows/src/shared/serializable.ts +1 -1
  16. package/dist/bun/cli.d.ts.map +1 -1
  17. package/dist/bun/internal-intercom-broker.d.ts.map +1 -1
  18. package/dist/bun/register-bedrock.d.ts.map +1 -1
  19. package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
  20. package/dist/bun/split-loader.d.ts.map +1 -1
  21. package/dist/cli/args.d.ts.map +1 -1
  22. package/dist/cli/config-selector.d.ts.map +1 -1
  23. package/dist/cli/file-processor.d.ts.map +1 -1
  24. package/dist/cli/initial-message.d.ts.map +1 -1
  25. package/dist/cli/list-models.d.ts.map +1 -1
  26. package/dist/cli/project-trust.d.ts.map +1 -1
  27. package/dist/cli/session-picker.d.ts.map +1 -1
  28. package/dist/cli/startup-ui.d.ts.map +1 -1
  29. package/dist/cli.d.ts.map +1 -1
  30. package/dist/config-self-update.d.ts.map +1 -1
  31. package/dist/config.d.ts.map +1 -1
  32. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  33. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  34. package/dist/core/agent-session-bash.d.ts.map +1 -1
  35. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  36. package/dist/core/agent-session-events.d.ts.map +1 -1
  37. package/dist/core/agent-session-export.d.ts.map +1 -1
  38. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  39. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  40. package/dist/core/agent-session-methods.d.ts.map +1 -1
  41. package/dist/core/agent-session-models.d.ts.map +1 -1
  42. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  43. package/dist/core/agent-session-retry.d.ts.map +1 -1
  44. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  45. package/dist/core/agent-session-services.d.ts.map +1 -1
  46. package/dist/core/agent-session-skill-block.d.ts.map +1 -1
  47. package/dist/core/agent-session-state.d.ts.map +1 -1
  48. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  49. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  50. package/dist/core/agent-session-tree.d.ts.map +1 -1
  51. package/dist/core/agent-session-types.d.ts.map +1 -1
  52. package/dist/core/agent-session.d.ts.map +1 -1
  53. package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
  54. package/dist/core/async/format.d.ts.map +1 -1
  55. package/dist/core/async/job-manager.d.ts.map +1 -1
  56. package/dist/core/async/session-manager.d.ts.map +1 -1
  57. package/dist/core/async/types.d.ts.map +1 -1
  58. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  59. package/dist/core/auth-guidance.d.ts.map +1 -1
  60. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  61. package/dist/core/auth-storage.d.ts +4 -0
  62. package/dist/core/auth-storage.d.ts.map +1 -1
  63. package/dist/core/bash-executor.d.ts.map +1 -1
  64. package/dist/core/builtin-packages.d.ts.map +1 -1
  65. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  66. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  67. package/dist/core/compaction/compaction-boundary.d.ts.map +1 -1
  68. package/dist/core/compaction/compaction-parameters.d.ts.map +1 -1
  69. package/dist/core/compaction/compaction-runner.d.ts.map +1 -1
  70. package/dist/core/compaction/compaction-types.d.ts.map +1 -1
  71. package/dist/core/compaction/compaction.d.ts.map +1 -1
  72. package/dist/core/compaction/deleted-ranges.d.ts.map +1 -1
  73. package/dist/core/compaction/index.d.ts.map +1 -1
  74. package/dist/core/compaction/range-planner.d.ts.map +1 -1
  75. package/dist/core/compaction/transcript-serialization.d.ts.map +1 -1
  76. package/dist/core/compaction/utils.d.ts.map +1 -1
  77. package/dist/core/context-window.d.ts.map +1 -1
  78. package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -1
  79. package/dist/core/copilot-errors.d.ts.map +1 -1
  80. package/dist/core/copilot-gemini-payload-sanitizer.d.ts.map +1 -1
  81. package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
  82. package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -1
  83. package/dist/core/copilot-hosts.d.ts.map +1 -1
  84. package/dist/core/copilot-model-catalog.d.ts.map +1 -1
  85. package/dist/core/copilot-model-static-fallbacks.d.ts.map +1 -1
  86. package/dist/core/copilot-model-synthesis.d.ts.map +1 -1
  87. package/dist/core/defaults.d.ts.map +1 -1
  88. package/dist/core/diagnostics.d.ts.map +1 -1
  89. package/dist/core/event-bus.d.ts.map +1 -1
  90. package/dist/core/exec.d.ts.map +1 -1
  91. package/dist/core/experimental.d.ts.map +1 -1
  92. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  93. package/dist/core/export-html/index.d.ts.map +1 -1
  94. package/dist/core/export-html/template-script.d.ts.map +1 -1
  95. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  96. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  97. package/dist/core/extensions/api-types.d.ts.map +1 -1
  98. package/dist/core/extensions/command-types.d.ts.map +1 -1
  99. package/dist/core/extensions/context-types.d.ts.map +1 -1
  100. package/dist/core/extensions/event-results.d.ts.map +1 -1
  101. package/dist/core/extensions/event-types.d.ts.map +1 -1
  102. package/dist/core/extensions/index.d.ts.map +1 -1
  103. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  104. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  105. package/dist/core/extensions/loader-discovery.d.ts.map +1 -1
  106. package/dist/core/extensions/loader-resources.d.ts.map +1 -1
  107. package/dist/core/extensions/loader-runtime.d.ts.map +1 -1
  108. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  109. package/dist/core/extensions/loader.d.ts.map +1 -1
  110. package/dist/core/extensions/message-types.d.ts.map +1 -1
  111. package/dist/core/extensions/provider-types.d.ts.map +1 -1
  112. package/dist/core/extensions/reactive-widget.d.ts.map +1 -1
  113. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  114. package/dist/core/extensions/runner-events.d.ts.map +1 -1
  115. package/dist/core/extensions/runner-handlers.d.ts.map +1 -1
  116. package/dist/core/extensions/runner-project-trust.d.ts.map +1 -1
  117. package/dist/core/extensions/runner-registries.d.ts.map +1 -1
  118. package/dist/core/extensions/runner-shortcuts.d.ts.map +1 -1
  119. package/dist/core/extensions/runner-ui.d.ts.map +1 -1
  120. package/dist/core/extensions/runner.d.ts.map +1 -1
  121. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  122. package/dist/core/extensions/session-events.d.ts.map +1 -1
  123. package/dist/core/extensions/tool-events.d.ts.map +1 -1
  124. package/dist/core/extensions/tool-types.d.ts.map +1 -1
  125. package/dist/core/extensions/types.d.ts.map +1 -1
  126. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  127. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  128. package/dist/core/flattened-tool-arguments.d.ts.map +1 -1
  129. package/dist/core/footer-data-provider.d.ts.map +1 -1
  130. package/dist/core/http-dispatcher.d.ts.map +1 -1
  131. package/dist/core/index.d.ts.map +1 -1
  132. package/dist/core/keybindings.d.ts.map +1 -1
  133. package/dist/core/messages.d.ts.map +1 -1
  134. package/dist/core/model-registry-auth.d.ts.map +1 -1
  135. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  136. package/dist/core/model-registry-builtins.js.map +1 -1
  137. package/dist/core/model-registry-custom-loader.d.ts.map +1 -1
  138. package/dist/core/model-registry-custom-loader.js.map +1 -1
  139. package/dist/core/model-registry-dynamic.d.ts.map +1 -1
  140. package/dist/core/model-registry-loader.d.ts.map +1 -1
  141. package/dist/core/model-registry-schemas.d.ts +65 -13
  142. package/dist/core/model-registry-schemas.d.ts.map +1 -1
  143. package/dist/core/model-registry-schemas.js +5 -1
  144. package/dist/core/model-registry-schemas.js.map +1 -1
  145. package/dist/core/model-registry-types.d.ts.map +1 -1
  146. package/dist/core/model-registry.d.ts.map +1 -1
  147. package/dist/core/model-resolver-cli.d.ts.map +1 -1
  148. package/dist/core/model-resolver-defaults.d.ts.map +1 -1
  149. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  150. package/dist/core/model-resolver-patterns.d.ts.map +1 -1
  151. package/dist/core/model-resolver-scope.d.ts.map +1 -1
  152. package/dist/core/model-resolver-types.d.ts.map +1 -1
  153. package/dist/core/model-resolver.d.ts.map +1 -1
  154. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  155. package/dist/core/output-guard.d.ts.map +1 -1
  156. package/dist/core/package-manager-auto-resources.d.ts.map +1 -1
  157. package/dist/core/package-manager-command.d.ts.map +1 -1
  158. package/dist/core/package-manager-constants.d.ts.map +1 -1
  159. package/dist/core/package-manager-env.d.ts.map +1 -1
  160. package/dist/core/package-manager-git.d.ts.map +1 -1
  161. package/dist/core/package-manager-manifest.d.ts.map +1 -1
  162. package/dist/core/package-manager-npm.d.ts.map +1 -1
  163. package/dist/core/package-manager-operations.d.ts.map +1 -1
  164. package/dist/core/package-manager-paths.d.ts.map +1 -1
  165. package/dist/core/package-manager-progress.d.ts.map +1 -1
  166. package/dist/core/package-manager-resolver.d.ts.map +1 -1
  167. package/dist/core/package-manager-resource-accumulator.d.ts.map +1 -1
  168. package/dist/core/package-manager-resource-collector.d.ts.map +1 -1
  169. package/dist/core/package-manager-resource-files.d.ts.map +1 -1
  170. package/dist/core/package-manager-resource-patterns.d.ts.map +1 -1
  171. package/dist/core/package-manager-settings.d.ts.map +1 -1
  172. package/dist/core/package-manager-source.d.ts.map +1 -1
  173. package/dist/core/package-manager-types.d.ts.map +1 -1
  174. package/dist/core/package-manager.d.ts.map +1 -1
  175. package/dist/core/project-trust.d.ts.map +1 -1
  176. package/dist/core/prompt-templates-async.d.ts.map +1 -1
  177. package/dist/core/prompt-templates.d.ts.map +1 -1
  178. package/dist/core/provider-attribution.d.ts.map +1 -1
  179. package/dist/core/provider-context-usage.d.ts.map +1 -1
  180. package/dist/core/provider-display-names.d.ts.map +1 -1
  181. package/dist/core/resolve-config-value.d.ts.map +1 -1
  182. package/dist/core/resource-loader-assets.d.ts.map +1 -1
  183. package/dist/core/resource-loader-context-files.d.ts.map +1 -1
  184. package/dist/core/resource-loader-core.d.ts.map +1 -1
  185. package/dist/core/resource-loader-discovery.d.ts.map +1 -1
  186. package/dist/core/resource-loader-extensions.d.ts.map +1 -1
  187. package/dist/core/resource-loader-helpers.d.ts.map +1 -1
  188. package/dist/core/resource-loader-internals.d.ts.map +1 -1
  189. package/dist/core/resource-loader-package-resources.d.ts.map +1 -1
  190. package/dist/core/resource-loader-paths.d.ts.map +1 -1
  191. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  192. package/dist/core/resource-loader-source-info.d.ts.map +1 -1
  193. package/dist/core/resource-loader-types.d.ts.map +1 -1
  194. package/dist/core/resource-loader.d.ts.map +1 -1
  195. package/dist/core/sdk-exports.d.ts.map +1 -1
  196. package/dist/core/sdk-types.d.ts.map +1 -1
  197. package/dist/core/sdk.d.ts.map +1 -1
  198. package/dist/core/session-cwd.d.ts.map +1 -1
  199. package/dist/core/session-entry-normalization.d.ts.map +1 -1
  200. package/dist/core/session-manager-archive.d.ts.map +1 -1
  201. package/dist/core/session-manager-classification.d.ts.map +1 -1
  202. package/dist/core/session-manager-core.d.ts.map +1 -1
  203. package/dist/core/session-manager-entries.d.ts.map +1 -1
  204. package/dist/core/session-manager-history.d.ts.map +1 -1
  205. package/dist/core/session-manager-list.d.ts.map +1 -1
  206. package/dist/core/session-manager-migrations.d.ts.map +1 -1
  207. package/dist/core/session-manager-paths.d.ts.map +1 -1
  208. package/dist/core/session-manager-storage.d.ts.map +1 -1
  209. package/dist/core/session-manager-types.d.ts.map +1 -1
  210. package/dist/core/session-manager-validation.d.ts.map +1 -1
  211. package/dist/core/session-manager.d.ts.map +1 -1
  212. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  213. package/dist/core/settings-manager-core.d.ts.map +1 -1
  214. package/dist/core/settings-manager-internals.d.ts.map +1 -1
  215. package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
  216. package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
  217. package/dist/core/settings-manager.d.ts.map +1 -1
  218. package/dist/core/settings-merge.d.ts.map +1 -1
  219. package/dist/core/settings-storage.d.ts.map +1 -1
  220. package/dist/core/settings-types.d.ts.map +1 -1
  221. package/dist/core/skills-async.d.ts.map +1 -1
  222. package/dist/core/skills.d.ts.map +1 -1
  223. package/dist/core/slash-commands.d.ts.map +1 -1
  224. package/dist/core/source-info.d.ts.map +1 -1
  225. package/dist/core/system-prompt.d.ts.map +1 -1
  226. package/dist/core/telemetry.d.ts.map +1 -1
  227. package/dist/core/thinking-blocks.d.ts.map +1 -1
  228. package/dist/core/timings.d.ts.map +1 -1
  229. package/dist/core/tools/artifact-protocol.d.ts.map +1 -1
  230. package/dist/core/tools/artifacts.d.ts.map +1 -1
  231. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  232. package/dist/core/tools/ask-user-question/config.d.ts.map +1 -1
  233. package/dist/core/tools/ask-user-question/index.d.ts.map +1 -1
  234. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts.map +1 -1
  235. package/dist/core/tools/ask-user-question/state/inline-input.d.ts.map +1 -1
  236. package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -1
  237. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  238. package/dist/core/tools/ask-user-question/state/row-intent.d.ts.map +1 -1
  239. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
  240. package/dist/core/tools/ask-user-question/state/selectors/derivations.d.ts.map +1 -1
  241. package/dist/core/tools/ask-user-question/state/selectors/focus.d.ts.map +1 -1
  242. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
  243. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  244. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
  245. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
  246. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
  247. package/dist/core/tools/ask-user-question/tool/types.d.ts.map +1 -1
  248. package/dist/core/tools/ask-user-question/tool/validate-questionnaire.d.ts.map +1 -1
  249. package/dist/core/tools/ask-user-question/view/body-residual-spacer.d.ts.map +1 -1
  250. package/dist/core/tools/ask-user-question/view/component-binding.d.ts.map +1 -1
  251. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -1
  252. package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts.map +1 -1
  253. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
  254. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts.map +1 -1
  255. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map +1 -1
  256. package/dist/core/tools/ask-user-question/view/components/preview/preview-box-renderer.d.ts.map +1 -1
  257. package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts.map +1 -1
  258. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.d.ts.map +1 -1
  259. package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts.map +1 -1
  260. package/dist/core/tools/ask-user-question/view/components/tab-bar.d.ts.map +1 -1
  261. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  262. package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts.map +1 -1
  263. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  264. package/dist/core/tools/ask-user-question/view/stateful-view.d.ts.map +1 -1
  265. package/dist/core/tools/ask-user-question/view/tab-components.d.ts.map +1 -1
  266. package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts.map +1 -1
  267. package/dist/core/tools/bash-async-execution.d.ts.map +1 -1
  268. package/dist/core/tools/bash-async-jobs.d.ts.map +1 -1
  269. package/dist/core/tools/bash-async-output.d.ts.map +1 -1
  270. package/dist/core/tools/bash-interceptor.d.ts.map +1 -1
  271. package/dist/core/tools/bash-leading-cd.d.ts.map +1 -1
  272. package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
  273. package/dist/core/tools/bash.d.ts.map +1 -1
  274. package/dist/core/tools/block-resolver.d.ts.map +1 -1
  275. package/dist/core/tools/conflict-registry.d.ts.map +1 -1
  276. package/dist/core/tools/directory-tree.d.ts.map +1 -1
  277. package/dist/core/tools/edit-diff-preserve.d.ts.map +1 -1
  278. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  279. package/dist/core/tools/edit.d.ts.map +1 -1
  280. package/dist/core/tools/fetch-url.d.ts.map +1 -1
  281. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  282. package/dist/core/tools/find.d.ts.map +1 -1
  283. package/dist/core/tools/glob-path-utils.d.ts.map +1 -1
  284. package/dist/core/tools/grep.d.ts.map +1 -1
  285. package/dist/core/tools/hashline-engine/apply.d.ts.map +1 -1
  286. package/dist/core/tools/hashline-engine/block.d.ts.map +1 -1
  287. package/dist/core/tools/hashline-engine/diff-preview.d.ts.map +1 -1
  288. package/dist/core/tools/hashline-engine/format.d.ts.map +1 -1
  289. package/dist/core/tools/hashline-engine/fs.d.ts.map +1 -1
  290. package/dist/core/tools/hashline-engine/index.d.ts.map +1 -1
  291. package/dist/core/tools/hashline-engine/input.d.ts.map +1 -1
  292. package/dist/core/tools/hashline-engine/messages.d.ts.map +1 -1
  293. package/dist/core/tools/hashline-engine/mismatch.d.ts.map +1 -1
  294. package/dist/core/tools/hashline-engine/normalize.d.ts.map +1 -1
  295. package/dist/core/tools/hashline-engine/parser.d.ts.map +1 -1
  296. package/dist/core/tools/hashline-engine/patcher.d.ts.map +1 -1
  297. package/dist/core/tools/hashline-engine/prefixes.d.ts.map +1 -1
  298. package/dist/core/tools/hashline-engine/recovery.d.ts.map +1 -1
  299. package/dist/core/tools/hashline-engine/snapshots.d.ts.map +1 -1
  300. package/dist/core/tools/hashline-engine/stream.d.ts.map +1 -1
  301. package/dist/core/tools/hashline-engine/tokenizer.d.ts.map +1 -1
  302. package/dist/core/tools/hashline-engine/types.d.ts.map +1 -1
  303. package/dist/core/tools/hashline.d.ts.map +1 -1
  304. package/dist/core/tools/index.d.ts.map +1 -1
  305. package/dist/core/tools/ls.d.ts.map +1 -1
  306. package/dist/core/tools/notebook.d.ts.map +1 -1
  307. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  308. package/dist/core/tools/oversized-tool-result.d.ts.map +1 -1
  309. package/dist/core/tools/path-utils.d.ts.map +1 -1
  310. package/dist/core/tools/read-document-extract.d.ts.map +1 -1
  311. package/dist/core/tools/read-selectors.d.ts.map +1 -1
  312. package/dist/core/tools/read-url.d.ts.map +1 -1
  313. package/dist/core/tools/read.d.ts.map +1 -1
  314. package/dist/core/tools/render-utils.d.ts.map +1 -1
  315. package/dist/core/tools/resource-selectors.d.ts.map +1 -1
  316. package/dist/core/tools/search-details.d.ts.map +1 -1
  317. package/dist/core/tools/search-line-ranges.d.ts.map +1 -1
  318. package/dist/core/tools/search-native.d.ts.map +1 -1
  319. package/dist/core/tools/search.d.ts.map +1 -1
  320. package/dist/core/tools/structured-output.d.ts.map +1 -1
  321. package/dist/core/tools/todos-execute.d.ts.map +1 -1
  322. package/dist/core/tools/todos-locks.d.ts.map +1 -1
  323. package/dist/core/tools/todos-model.d.ts.map +1 -1
  324. package/dist/core/tools/todos-mutations.d.ts.map +1 -1
  325. package/dist/core/tools/todos-paths.d.ts.map +1 -1
  326. package/dist/core/tools/todos-render.d.ts.map +1 -1
  327. package/dist/core/tools/todos-storage.d.ts.map +1 -1
  328. package/dist/core/tools/todos-types.d.ts.map +1 -1
  329. package/dist/core/tools/todos-types.js +1 -1
  330. package/dist/core/tools/todos-types.js.map +1 -1
  331. package/dist/core/tools/todos.d.ts.map +1 -1
  332. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  333. package/dist/core/tools/tool-definition-wrapper.js +6 -2
  334. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  335. package/dist/core/tools/tool-limits.d.ts.map +1 -1
  336. package/dist/core/tools/truncate.d.ts.map +1 -1
  337. package/dist/core/tools/url-ip-guards.d.ts.map +1 -1
  338. package/dist/core/tools/write.d.ts.map +1 -1
  339. package/dist/core/trust-manager.d.ts.map +1 -1
  340. package/dist/core/typebox-compat.d.ts +12 -0
  341. package/dist/core/typebox-compat.d.ts.map +1 -0
  342. package/dist/core/typebox-compat.js +10 -0
  343. package/dist/core/typebox-compat.js.map +1 -0
  344. package/dist/index-extensions.d.ts.map +1 -1
  345. package/dist/index.d.ts +1 -0
  346. package/dist/index.d.ts.map +1 -1
  347. package/dist/index.js +1 -0
  348. package/dist/index.js.map +1 -1
  349. package/dist/main-app-mode.d.ts.map +1 -1
  350. package/dist/main-deferred-startup.d.ts.map +1 -1
  351. package/dist/main-early-input.d.ts.map +1 -1
  352. package/dist/main-session-options.d.ts.map +1 -1
  353. package/dist/main-session.d.ts.map +1 -1
  354. package/dist/main-stdio.d.ts.map +1 -1
  355. package/dist/main.d.ts.map +1 -1
  356. package/dist/migrations-config-values.d.ts.map +1 -1
  357. package/dist/migrations.d.ts.map +1 -1
  358. package/dist/modes/index.d.ts.map +1 -1
  359. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
  360. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  361. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  362. package/dist/modes/interactive/components/atomic-banner.d.ts.map +1 -1
  363. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  364. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  365. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  366. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  367. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  368. package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
  369. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  370. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
  371. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts.map +1 -1
  372. package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
  373. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts.map +1 -1
  374. package/dist/modes/interactive/components/chat-session-host-types.d.ts.map +1 -1
  375. package/dist/modes/interactive/components/chat-session-host-utils.d.ts.map +1 -1
  376. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  377. package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
  378. package/dist/modes/interactive/components/compaction-boundary-message.d.ts.map +1 -1
  379. package/dist/modes/interactive/components/config-selector-list.d.ts.map +1 -1
  380. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  381. package/dist/modes/interactive/components/context-window-selector.d.ts.map +1 -1
  382. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  383. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  384. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  385. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  386. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  387. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  388. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  389. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  390. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  391. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  392. package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -1
  393. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  394. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  395. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  396. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  397. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  398. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  399. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  400. package/dist/modes/interactive/components/session-selector-delete.d.ts.map +1 -1
  401. package/dist/modes/interactive/components/session-selector-header.d.ts.map +1 -1
  402. package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
  403. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  404. package/dist/modes/interactive/components/session-selector-tree.d.ts.map +1 -1
  405. package/dist/modes/interactive/components/session-selector-types.d.ts.map +1 -1
  406. package/dist/modes/interactive/components/session-selector-utils.d.ts.map +1 -1
  407. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  408. package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
  409. package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
  410. package/dist/modes/interactive/components/settings-selector-options.d.ts.map +1 -1
  411. package/dist/modes/interactive/components/settings-selector-submenus.d.ts.map +1 -1
  412. package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
  413. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  414. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  415. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  416. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  417. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  418. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  419. package/dist/modes/interactive/components/tree-selector-component.d.ts.map +1 -1
  420. package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
  421. package/dist/modes/interactive/components/tree-selector-help.d.ts.map +1 -1
  422. package/dist/modes/interactive/components/tree-selector-label-input.d.ts.map +1 -1
  423. package/dist/modes/interactive/components/tree-selector-list.d.ts.map +1 -1
  424. package/dist/modes/interactive/components/tree-selector-model.d.ts.map +1 -1
  425. package/dist/modes/interactive/components/tree-selector-types.d.ts.map +1 -1
  426. package/dist/modes/interactive/components/tree-selector-viewport.d.ts.map +1 -1
  427. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  428. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  429. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  430. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  431. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  432. package/dist/modes/interactive/components/working-status.d.ts.map +1 -1
  433. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  434. package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
  435. package/dist/modes/interactive/interactive-auth-routing.d.ts.map +1 -1
  436. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  437. package/dist/modes/interactive/interactive-bash-compact.d.ts.map +1 -1
  438. package/dist/modes/interactive/interactive-child-ordering.d.ts.map +1 -1
  439. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  440. package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
  441. package/dist/modes/interactive/interactive-extension-context.d.ts.map +1 -1
  442. package/dist/modes/interactive/interactive-extension-custom-ui.d.ts.map +1 -1
  443. package/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
  444. package/dist/modes/interactive/interactive-extension-runtime.d.ts.map +1 -1
  445. package/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  446. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  447. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  448. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  449. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  450. package/dist/modes/interactive/interactive-mode-helpers.d.ts.map +1 -1
  451. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  452. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  453. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  454. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  455. package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -1
  456. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  457. package/dist/modes/interactive/interactive-queueing.d.ts.map +1 -1
  458. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  459. package/dist/modes/interactive/interactive-resource-disclosure.d.ts.map +1 -1
  460. package/dist/modes/interactive/interactive-resource-paths.d.ts.map +1 -1
  461. package/dist/modes/interactive/interactive-resource-rendering.d.ts.map +1 -1
  462. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  463. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  464. package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
  465. package/dist/modes/interactive/interactive-slash-commands.d.ts.map +1 -1
  466. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  467. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  468. package/dist/modes/interactive/theme/color-utils.d.ts.map +1 -1
  469. package/dist/modes/interactive/theme/export-colors.d.ts.map +1 -1
  470. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  471. package/dist/modes/interactive/theme/terminal-detection.d.ts.map +1 -1
  472. package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
  473. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  474. package/dist/modes/interactive/theme/theme-loading.d.ts.map +1 -1
  475. package/dist/modes/interactive/theme/theme-parse.d.ts.map +1 -1
  476. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  477. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  478. package/dist/modes/interactive/theme/tui-theme.d.ts.map +1 -1
  479. package/dist/modes/interactive/whimsical-messages.d.ts.map +1 -1
  480. package/dist/modes/print-mode.d.ts.map +1 -1
  481. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  482. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  483. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  484. package/dist/modes/rpc/rpc-extension-ui.d.ts.map +1 -1
  485. package/dist/modes/rpc/rpc-input.d.ts.map +1 -1
  486. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  487. package/dist/modes/rpc/rpc-responses.d.ts.map +1 -1
  488. package/dist/modes/rpc/rpc-session-binding.d.ts.map +1 -1
  489. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  490. package/dist/package-manager-cli-parser.d.ts.map +1 -1
  491. package/dist/package-manager-cli.d.ts.map +1 -1
  492. package/dist/rpc-entry.d.ts.map +1 -1
  493. package/dist/utils/ansi.d.ts.map +1 -1
  494. package/dist/utils/changelog.d.ts.map +1 -1
  495. package/dist/utils/child-process.d.ts.map +1 -1
  496. package/dist/utils/clipboard-image.d.ts.map +1 -1
  497. package/dist/utils/clipboard-native.d.ts.map +1 -1
  498. package/dist/utils/clipboard.d.ts.map +1 -1
  499. package/dist/utils/deprecation.d.ts.map +1 -1
  500. package/dist/utils/event-loop.d.ts.map +1 -1
  501. package/dist/utils/exif-orientation.d.ts.map +1 -1
  502. package/dist/utils/frontmatter.d.ts.map +1 -1
  503. package/dist/utils/fs-watch.d.ts.map +1 -1
  504. package/dist/utils/git-env.d.ts.map +1 -1
  505. package/dist/utils/git.d.ts.map +1 -1
  506. package/dist/utils/html.d.ts.map +1 -1
  507. package/dist/utils/image-convert.d.ts.map +1 -1
  508. package/dist/utils/image-process.d.ts.map +1 -1
  509. package/dist/utils/image-resize-core.d.ts.map +1 -1
  510. package/dist/utils/image-resize-worker.d.ts.map +1 -1
  511. package/dist/utils/image-resize.d.ts.map +1 -1
  512. package/dist/utils/json.d.ts.map +1 -1
  513. package/dist/utils/markit.d.ts.map +1 -1
  514. package/dist/utils/mime.d.ts.map +1 -1
  515. package/dist/utils/module-require.d.ts.map +1 -1
  516. package/dist/utils/open-browser.d.ts.map +1 -1
  517. package/dist/utils/paths.d.ts.map +1 -1
  518. package/dist/utils/photon.d.ts.map +1 -1
  519. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  520. package/dist/utils/shell.d.ts.map +1 -1
  521. package/dist/utils/sleep.d.ts.map +1 -1
  522. package/dist/utils/split-launcher.d.ts.map +1 -1
  523. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  524. package/dist/utils/tools-manager.d.ts.map +1 -1
  525. package/dist/utils/version-check.d.ts.map +1 -1
  526. package/dist/utils/windows-self-update.d.ts.map +1 -1
  527. package/docs/custom-provider.md +7 -1
  528. package/docs/extensions.md +6 -4
  529. package/examples/extensions/README.md +3 -1
  530. package/examples/extensions/subagent/schemas.ts +1 -1
  531. package/examples/extensions/tic-tac-toe.ts +1 -2
  532. package/examples/extensions/todo.ts +1 -2
  533. package/npm-shrinkwrap.json +214 -98
  534. package/package.json +12 -12
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/core/skills.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA2D9E,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,KAAK;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,sBAAsB,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,WAAW,EAAE,kBAAkB,EAAE,CAAC;CAClC;AA2CD,MAAM,WAAW,wBAAwB;IACxC,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;CACf;AA0BD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,gBAAgB,CAGrF;AA4JD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CA0B7D;AAWD,MAAM,WAAW,iBAAiB;IACjC,kDAAkD;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,0CAA0C;IAC1C,eAAe,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAoGvE","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport ignore from \"ignore\";\nimport { basename, dirname, join, relative, resolve, sep } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.ts\";\nimport { parseFrontmatter } from \"../utils/frontmatter.ts\";\nimport { canonicalizePath, resolvePath } from \"../utils/paths.ts\";\nimport type { ResourceDiagnostic } from \"./diagnostics.ts\";\nimport { createSyntheticSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\n/** Max name length per spec */\nconst MAX_NAME_LENGTH = 64;\n\n/** Max description length per spec */\nconst MAX_DESCRIPTION_LENGTH = 1024;\n\nconst IGNORE_FILE_NAMES = [\".gitignore\", \".ignore\", \".fdignore\"];\n\ntype IgnoreMatcher = ReturnType<typeof ignore>;\n\nfunction toPosixPath(p: string): string {\n\treturn p.split(sep).join(\"/\");\n}\n\nfunction prefixIgnorePattern(line: string, prefix: string): string | null {\n\tconst trimmed = line.trim();\n\tif (!trimmed) return null;\n\tif (trimmed.startsWith(\"#\") && !trimmed.startsWith(\"\\\\#\")) return null;\n\n\tlet pattern = line;\n\tlet negated = false;\n\n\tif (pattern.startsWith(\"!\")) {\n\t\tnegated = true;\n\t\tpattern = pattern.slice(1);\n\t} else if (pattern.startsWith(\"\\\\!\")) {\n\t\tpattern = pattern.slice(1);\n\t}\n\n\tif (pattern.startsWith(\"/\")) {\n\t\tpattern = pattern.slice(1);\n\t}\n\n\tconst prefixed = prefix ? `${prefix}${pattern}` : pattern;\n\treturn negated ? `!${prefixed}` : prefixed;\n}\n\nfunction addIgnoreRules(ig: IgnoreMatcher, dir: string, rootDir: string): void {\n\tconst relativeDir = relative(rootDir, dir);\n\tconst prefix = relativeDir ? `${toPosixPath(relativeDir)}/` : \"\";\n\n\tfor (const filename of IGNORE_FILE_NAMES) {\n\t\tconst ignorePath = join(dir, filename);\n\t\tif (!existsSync(ignorePath)) continue;\n\t\ttry {\n\t\t\tconst content = readFileSync(ignorePath, \"utf-8\");\n\t\t\tconst patterns = content\n\t\t\t\t.split(/\\r?\\n/)\n\t\t\t\t.map((line) => prefixIgnorePattern(line, prefix))\n\t\t\t\t.filter((line): line is string => Boolean(line));\n\t\t\tif (patterns.length > 0) {\n\t\t\t\tig.add(patterns);\n\t\t\t}\n\t\t} catch {}\n\t}\n}\n\nexport interface SkillFrontmatter {\n\tname?: string;\n\tdescription?: string;\n\t\"disable-model-invocation\"?: boolean;\n\t[key: string]: unknown;\n}\n\nexport interface Skill {\n\tname: string;\n\tdescription: string;\n\tfilePath: string;\n\tbaseDir: string;\n\tsourceInfo: SourceInfo;\n\tdisableModelInvocation: boolean;\n}\n\nexport interface LoadSkillsResult {\n\tskills: Skill[];\n\tdiagnostics: ResourceDiagnostic[];\n}\n\n/**\n * Validate skill name per Agent Skills spec.\n * Returns array of validation error messages (empty if valid).\n */\nfunction validateName(name: string): string[] {\n\tconst errors: string[] = [];\n\n\tif (name.length > MAX_NAME_LENGTH) {\n\t\terrors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);\n\t}\n\n\tif (!/^[a-z0-9-]+$/.test(name)) {\n\t\terrors.push(`name contains invalid characters (must be lowercase a-z, 0-9, hyphens only)`);\n\t}\n\n\tif (name.startsWith(\"-\") || name.endsWith(\"-\")) {\n\t\terrors.push(`name must not start or end with a hyphen`);\n\t}\n\n\tif (name.includes(\"--\")) {\n\t\terrors.push(`name must not contain consecutive hyphens`);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validate description per Agent Skills spec.\n */\nfunction validateDescription(description: string | undefined): string[] {\n\tconst errors: string[] = [];\n\n\tif (!description || description.trim() === \"\") {\n\t\terrors.push(\"description is required\");\n\t} else if (description.length > MAX_DESCRIPTION_LENGTH) {\n\t\terrors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);\n\t}\n\n\treturn errors;\n}\n\nexport interface LoadSkillsFromDirOptions {\n\t/** Directory to scan for skills */\n\tdir: string;\n\t/** Source identifier for these skills */\n\tsource: string;\n}\n\nfunction createSkillSourceInfo(filePath: string, baseDir: string, source: string): SourceInfo {\n\tswitch (source) {\n\t\tcase \"user\":\n\t\t\treturn createSyntheticSourceInfo(filePath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"user\",\n\t\t\t\tbaseDir,\n\t\t\t});\n\t\tcase \"project\":\n\t\t\treturn createSyntheticSourceInfo(filePath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"project\",\n\t\t\t\tbaseDir,\n\t\t\t});\n\t\tcase \"path\":\n\t\t\treturn createSyntheticSourceInfo(filePath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tbaseDir,\n\t\t\t});\n\t\tdefault:\n\t\t\treturn createSyntheticSourceInfo(filePath, { source, baseDir });\n\t}\n}\n\n/**\n * Load skills from a directory.\n *\n * Discovery rules:\n * - if a directory contains SKILL.md, treat it as a skill root and do not recurse further\n * - otherwise, load direct .md children in the root\n * - recurse into subdirectories to find SKILL.md\n */\nexport function loadSkillsFromDir(options: LoadSkillsFromDirOptions): LoadSkillsResult {\n\tconst { dir, source } = options;\n\treturn loadSkillsFromDirInternal(dir, source, true);\n}\n\nfunction loadSkillsFromDirInternal(\n\tdir: string,\n\tsource: string,\n\tincludeRootFiles: boolean,\n\tignoreMatcher?: IgnoreMatcher,\n\trootDir?: string,\n): LoadSkillsResult {\n\tconst skills: Skill[] = [];\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn { skills, diagnostics };\n\t}\n\n\tconst root = rootDir ?? dir;\n\tconst ig = ignoreMatcher ?? ignore();\n\taddIgnoreRules(ig, dir, root);\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.name !== \"SKILL.md\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tisFile = statSync(fullPath).isFile();\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst relPath = toPosixPath(relative(root, fullPath));\n\t\t\tif (!isFile || ig.ignores(relPath)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst result = loadSkillFromFile(fullPath, source);\n\t\t\tif (result.skill) {\n\t\t\t\tskills.push(result.skill);\n\t\t\t}\n\t\t\tdiagnostics.push(...result.diagnostics);\n\t\t\treturn { skills, diagnostics };\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.name.startsWith(\".\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip node_modules to avoid scanning dependencies\n\t\t\tif (entry.name === \"node_modules\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a directory and follow them\n\t\t\tlet isDirectory = entry.isDirectory();\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisDirectory = stats.isDirectory();\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst relPath = toPosixPath(relative(root, fullPath));\n\t\t\tconst ignorePath = isDirectory ? `${relPath}/` : relPath;\n\t\t\tif (ig.ignores(ignorePath)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (isDirectory) {\n\t\t\t\tconst subResult = loadSkillsFromDirInternal(fullPath, source, false, ig, root);\n\t\t\t\tskills.push(...subResult.skills);\n\t\t\t\tdiagnostics.push(...subResult.diagnostics);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!isFile || !includeRootFiles || !entry.name.endsWith(\".md\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst result = loadSkillFromFile(fullPath, source);\n\t\t\tif (result.skill) {\n\t\t\t\tskills.push(result.skill);\n\t\t\t}\n\t\t\tdiagnostics.push(...result.diagnostics);\n\t\t}\n\t} catch {}\n\n\treturn { skills, diagnostics };\n}\n\nfunction loadSkillFromFile(\n\tfilePath: string,\n\tsource: string,\n): { skill: Skill | null; diagnostics: ResourceDiagnostic[] } {\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter } = parseFrontmatter<SkillFrontmatter>(rawContent);\n\t\tconst skillDir = dirname(filePath);\n\t\tconst parentDirName = basename(skillDir);\n\n\t\t// Validate description\n\t\tconst descErrors = validateDescription(frontmatter.description);\n\t\tfor (const error of descErrors) {\n\t\t\tdiagnostics.push({ type: \"warning\", message: error, path: filePath });\n\t\t}\n\n\t\t// Use name from frontmatter, or fall back to parent directory name\n\t\tconst name = frontmatter.name || parentDirName;\n\n\t\t// Validate name\n\t\tconst nameErrors = validateName(name);\n\t\tfor (const error of nameErrors) {\n\t\t\tdiagnostics.push({ type: \"warning\", message: error, path: filePath });\n\t\t}\n\n\t\t// Still load the skill even with warnings (unless description is completely missing)\n\t\tif (!frontmatter.description || frontmatter.description.trim() === \"\") {\n\t\t\treturn { skill: null, diagnostics };\n\t\t}\n\n\t\treturn {\n\t\t\tskill: {\n\t\t\t\tname,\n\t\t\t\tdescription: frontmatter.description,\n\t\t\t\tfilePath,\n\t\t\t\tbaseDir: skillDir,\n\t\t\t\tsourceInfo: createSkillSourceInfo(filePath, skillDir, source),\n\t\t\t\tdisableModelInvocation: frontmatter[\"disable-model-invocation\"] === true,\n\t\t\t},\n\t\t\tdiagnostics,\n\t\t};\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"failed to parse skill file\";\n\t\tdiagnostics.push({ type: \"warning\", message, path: filePath });\n\t\treturn { skill: null, diagnostics };\n\t}\n}\n\n/**\n * Format skills for inclusion in a system prompt.\n * Uses XML format per Agent Skills standard.\n * See: https://agentskills.io/integrate-skills\n *\n * Skills with disableModelInvocation=true are excluded from the prompt\n * (they can only be invoked explicitly via /skill:name commands).\n */\nexport function formatSkillsForPrompt(skills: Skill[]): string {\n\tconst visibleSkills = skills.filter((s) => !s.disableModelInvocation);\n\n\tif (visibleSkills.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst lines = [\n\t\t\"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n\t\t\"Use the read tool to load a skill's file when the task matches its description.\",\n\t\t\"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\",\n\t\t\"\",\n\t\t\"<available_skills>\",\n\t];\n\n\tfor (const skill of visibleSkills) {\n\t\tlines.push(\" <skill>\");\n\t\tlines.push(` <name>${escapeXml(skill.name)}</name>`);\n\t\tlines.push(` <description>${escapeXml(skill.description)}</description>`);\n\t\tlines.push(` <location>${escapeXml(skill.filePath)}</location>`);\n\t\tlines.push(\" </skill>\");\n\t}\n\n\tlines.push(\"</available_skills>\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n\nexport interface LoadSkillsOptions {\n\t/** Working directory for project-local skills. */\n\tcwd: string;\n\t/** Agent config directory for global skills. */\n\tagentDir: string;\n\t/** Explicit skill paths (files or directories) */\n\tskillPaths: string[];\n\t/** Include default skills directories. */\n\tincludeDefaults: boolean;\n}\n\n/**\n * Load skills from all configured locations.\n * Returns skills and any validation diagnostics.\n */\nexport function loadSkills(options: LoadSkillsOptions): LoadSkillsResult {\n\tconst { agentDir, skillPaths, includeDefaults } = options;\n\n\t// Resolve agentDir - if not provided, use default from config\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(agentDir ?? getAgentDir());\n\n\tconst skillMap = new Map<string, Skill>();\n\tconst realPathSet = new Set<string>();\n\tconst allDiagnostics: ResourceDiagnostic[] = [];\n\tconst collisionDiagnostics: ResourceDiagnostic[] = [];\n\n\tfunction addSkills(result: LoadSkillsResult) {\n\t\tallDiagnostics.push(...result.diagnostics);\n\t\tfor (const skill of result.skills) {\n\t\t\t// Resolve symlinks to detect duplicate files\n\t\t\tconst realPath = canonicalizePath(skill.filePath);\n\n\t\t\t// Skip silently if we've already loaded this exact file (via symlink)\n\t\t\tif (realPathSet.has(realPath)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst existing = skillMap.get(skill.name);\n\t\t\tif (existing) {\n\t\t\t\tcollisionDiagnostics.push({\n\t\t\t\t\ttype: \"collision\",\n\t\t\t\t\tmessage: `name \"${skill.name}\" collision`,\n\t\t\t\t\tpath: skill.filePath,\n\t\t\t\t\tcollision: {\n\t\t\t\t\t\tresourceType: \"skill\",\n\t\t\t\t\t\tname: skill.name,\n\t\t\t\t\t\twinnerPath: existing.filePath,\n\t\t\t\t\t\tloserPath: skill.filePath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tskillMap.set(skill.name, skill);\n\t\t\t\trealPathSet.add(realPath);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (includeDefaults) {\n\t\taddSkills(loadSkillsFromDirInternal(join(resolvedAgentDir, \"skills\"), \"user\", true));\n\t\taddSkills(loadSkillsFromDirInternal(resolve(resolvedCwd, CONFIG_DIR_NAME, \"skills\"), \"project\", true));\n\t}\n\n\tconst userSkillsDir = join(resolvedAgentDir, \"skills\");\n\tconst projectSkillsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, \"skills\");\n\n\tconst isUnderPath = (target: string, root: string): boolean => {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t};\n\n\tconst getSource = (resolvedPath: string): \"user\" | \"project\" | \"path\" => {\n\t\tif (!includeDefaults) {\n\t\t\tif (isUnderPath(resolvedPath, userSkillsDir)) return \"user\";\n\t\t\tif (isUnderPath(resolvedPath, projectSkillsDir)) return \"project\";\n\t\t}\n\t\treturn \"path\";\n\t};\n\n\tfor (const rawPath of skillPaths) {\n\t\tconst resolvedPath = resolvePath(rawPath, resolvedCwd, { trim: true });\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tallDiagnostics.push({ type: \"warning\", message: \"skill path does not exist\", path: resolvedPath });\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stats = statSync(resolvedPath);\n\t\t\tconst source = getSource(resolvedPath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\taddSkills(loadSkillsFromDirInternal(resolvedPath, source, true));\n\t\t\t} else if (stats.isFile() && resolvedPath.endsWith(\".md\")) {\n\t\t\t\tconst result = loadSkillFromFile(resolvedPath, source);\n\t\t\t\tif (result.skill) {\n\t\t\t\t\taddSkills({ skills: [result.skill], diagnostics: result.diagnostics });\n\t\t\t\t} else {\n\t\t\t\t\tallDiagnostics.push(...result.diagnostics);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tallDiagnostics.push({ type: \"warning\", message: \"skill path is not a markdown file\", path: resolvedPath });\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read skill path\";\n\t\t\tallDiagnostics.push({ type: \"warning\", message, path: resolvedPath });\n\t\t}\n\t}\n\n\treturn {\n\t\tskills: Array.from(skillMap.values()),\n\t\tdiagnostics: [...allDiagnostics, ...collisionDiagnostics],\n\t};\n}\n"]}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/core/skills.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA2D9E,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,KAAK;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,sBAAsB,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,WAAW,EAAE,kBAAkB,EAAE,CAAC;CAClC;AA2CD,MAAM,WAAW,wBAAwB;IACxC,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;CACf;AA0BD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,gBAAgB,CAGrF;AA4JD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CA0B7D;AAWD,MAAM,WAAW,iBAAiB;IACjC,kDAAkD;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,0CAA0C;IAC1C,eAAe,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAoGvE"}
@@ -1 +1 @@
1
- {"version":3,"file":"slash-commands.d.ts","sourceRoot":"","sources":["../../src/core/slash-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAO/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB,CAAC,EAAE,CACxB,cAAc,EAAE,MAAM,KAClB,gBAAgB,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,CAAC;CACpE;AA4FD,wBAAgB,qCAAqC,CAAC,cAAc,EAAE,MAAM,GAAG,gBAAgB,EAAE,GAAG,IAAI,CA4CvG;AAED,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,mBAAmB,CA8BrE,CAAC;AAEF,eAAO,MAAM,gCAAgC,EAAE,aAAa,CAAC,mBAAmB,CAkB/E,CAAC","sourcesContent":["import type { AutocompleteItem } from \"@earendil-works/pi-tui\";\nimport { APP_NAME } from \"../config.ts\";\nimport {\n\tATOMIC_GUIDE_COMMAND_DESCRIPTION,\n\tATOMIC_GUIDE_COMMAND_NAME,\n\tgetAtomicGuideArgumentCompletions,\n} from \"./atomic-guide-command.ts\";\nimport type { SourceInfo } from \"./source-info.ts\";\n\nexport type SlashCommandSource = \"extension\" | \"prompt\" | \"skill\";\n\nexport interface SlashCommandInfo {\n\tname: string;\n\tdescription?: string;\n\tsource: SlashCommandSource;\n\tsourceInfo: SourceInfo;\n}\n\nexport interface BuiltinSlashCommand {\n\tname: string;\n\tdescription: string;\n\tgetArgumentCompletions?: (\n\t\targumentPrefix: string,\n\t) => AutocompleteItem[] | null | Promise<AutocompleteItem[] | null>;\n}\n\ntype WorkflowInputCompletionMetadata = {\n\tdescription: string;\n\tkind?: \"boolean\" | \"number\" | \"string\";\n};\n\ntype WorkflowCompletionMetadata = {\n\tname: string;\n\tdescription: string;\n\tinputs: Record<string, WorkflowInputCompletionMetadata>;\n};\n\nconst WORKFLOW_ADMIN_COMPLETIONS: AutocompleteItem[] = [\n\t{ value: \"connect \", label: \"connect\", description: \"Attach to a run (picker if no id)\" },\n\t{ value: \"attach \", label: \"attach\", description: \"Open the in-place attach pane on a node\" },\n\t{ value: \"list \", label: \"list\", description: \"List registered workflows\" },\n\t{ value: \"status \", label: \"status\", description: \"List current-session active and retained terminal runs\" },\n\t{ value: \"interrupt \", label: \"interrupt\", description: \"Interrupt a run\" },\n\t{ value: \"kill \", label: \"kill\", description: \"Kill and retain a run\" },\n\t{ value: \"pause \", label: \"pause\", description: \"Pause a run or stage\" },\n\t{ value: \"resume \", label: \"resume\", description: \"Re-open overlay for a run\" },\n\t{ value: \"inputs \", label: \"inputs\", description: \"Show a workflow's input schema\" },\n\t{ value: \"reload \", label: \"reload\", description: \"Reload workflow resources\" },\n];\n\nconst BUNDLED_WORKFLOW_COMPLETION_METADATA: WorkflowCompletionMetadata[] = [\n\t{\n\t\tname: \"deep-research-codebase\",\n\t\tdescription: \"Scout + research-history chain → parallel specialist waves → aggregator for deep codebase research.\",\n\t\tinputs: {\n\t\t\tprompt: { description: \"Research question or investigation focus for the codebase.\", kind: \"string\" },\n\t\t\tmax_partitions: { description: \"Maximum number of codebase partitions to explore in parallel.\", kind: \"number\" },\n\t\t\tmax_concurrency: { description: \"Maximum number of workflow stages to run concurrently during deep research.\", kind: \"number\" },\n\t\t},\n\t},\n\t{\n\t\tname: \"goal\",\n\t\tdescription: \"Goal Runner workflow with bounded LM turns, acceptance criteria, ledger artifacts, reviewers, and reducer-gated completion.\",\n\t\tinputs: {\n\t\t\tobjective: { description: \"The objective or delta for this Goal Runner workflow run.\", kind: \"string\" },\n\t\t\tacceptance_criteria: { description: \"Original immutable task contract this run must remain consistent with.\", kind: \"string\" },\n\t\t\tmax_turns: { description: \"Maximum worker/review turns before Goal Runner stops as needs_human.\", kind: \"number\" },\n\t\t\tbase_branch: { description: \"Optional branch reviewers compare the current code delta against.\", kind: \"string\" },\n\t\t\tgit_worktree_dir: { description: \"Optional Git worktree path.\", kind: \"string\" },\n\t\t\tcreate_pr: { description: \"Whether to run the final pull-request creation stage after approval.\", kind: \"boolean\" },\n\t\t},\n\t},\n\t{\n\t\tname: \"open-claude-design\",\n\t\tdescription: \"AI-powered design workflow: discovery, reference research, HTML generation, refinement, and handoff.\",\n\t\tinputs: {\n\t\t\tprompt: { description: \"What to design, such as a dashboard, page, component, or prototype.\", kind: \"string\" },\n\t\t\tdiscover_references: { description: \"Discover current reference designs and feed them to generation.\", kind: \"boolean\" },\n\t\t\tmax_refinements: { description: \"Maximum generate/user-feedback loop iterations.\", kind: \"number\" },\n\t\t},\n\t},\n\t{\n\t\tname: \"ralph\",\n\t\tdescription: \"Raw prompt → research → orchestrate → multi-model parallel review loop with bounded iteration.\",\n\t\tinputs: {\n\t\t\tprompt: { description: \"The task or goal to research, execute, and refine.\", kind: \"string\" },\n\t\t\tacceptance_criteria: { description: \"Original immutable task contract this run must remain consistent with.\", kind: \"string\" },\n\t\t\tmax_loops: { description: \"Maximum research/orchestrate/review iterations.\", kind: \"number\" },\n\t\t\tbase_branch: { description: \"Branch reviewers compare the current code delta against.\", kind: \"string\" },\n\t\t\tgit_worktree_dir: { description: \"Optional Git worktree path.\", kind: \"string\" },\n\t\t\tcreate_pr: { description: \"Whether to run the final pull-request creation stage.\", kind: \"boolean\" },\n\t\t},\n\t},\n];\n\nfunction completeWorkflowToken(argumentText: string, candidates: AutocompleteItem[]): AutocompleteItem[] | null {\n\tconst tokenStart = /\\s$/.test(argumentText)\n\t\t? argumentText.length\n\t\t: Math.max(argumentText.lastIndexOf(\" \"), argumentText.lastIndexOf(\"\\t\")) + 1;\n\tconst head = argumentText.slice(0, tokenStart);\n\tconst token = argumentText.slice(tokenStart);\n\tconst normalizedToken = token.trimEnd();\n\tconst filtered = candidates\n\t\t.filter((candidate) => candidate.value.startsWith(token) && candidate.value.trimEnd() !== normalizedToken)\n\t\t.map((candidate) => ({ ...candidate, value: `${head}${candidate.value}` }));\n\treturn filtered.length > 0 ? filtered : null;\n}\n\nfunction bundledWorkflowNameItems(): AutocompleteItem[] {\n\treturn BUNDLED_WORKFLOW_COMPLETION_METADATA.map((workflow) => ({\n\t\tvalue: `${workflow.name} `,\n\t\tlabel: workflow.name,\n\t\tdescription: `Run workflow: ${workflow.name}`,\n\t}));\n}\n\nexport function getBundledWorkflowArgumentCompletions(argumentPrefix: string): AutocompleteItem[] | null {\n\tconst parts = argumentPrefix.trim().split(/\\s+/).filter(Boolean);\n\tconst subcommand = parts[0] ?? \"\";\n\tconst workflowItems = bundledWorkflowNameItems();\n\tif (!argumentPrefix.includes(\" \")) {\n\t\treturn completeWorkflowToken(argumentPrefix, [...WORKFLOW_ADMIN_COMPLETIONS, ...workflowItems]);\n\t}\n\tif (subcommand === \"inputs\") return completeWorkflowToken(argumentPrefix, workflowItems);\n\tif (subcommand === \"interrupt\" || subcommand === \"kill\") {\n\t\tconst verb = subcommand === \"kill\" ? \"Kill and retain\" : \"Interrupt\";\n\t\treturn completeWorkflowToken(argumentPrefix, [\n\t\t\t{ value: \"--all \", label: \"--all\", description: `${verb} all in-flight runs` },\n\t\t\t{ value: \"--yes \", label: \"--yes\", description: \"Skip confirmation\" },\n\t\t\t{ value: \"-y \", label: \"-y\", description: \"Skip confirmation\" },\n\t\t]);\n\t}\n\tif (!subcommand) return completeWorkflowToken(argumentPrefix, [...WORKFLOW_ADMIN_COMPLETIONS, ...workflowItems]);\n\n\tconst workflow = BUNDLED_WORKFLOW_COMPLETION_METADATA.find((candidate) => candidate.name === subcommand);\n\tif (!workflow) return null;\n\tconst tokenStart = /\\s$/.test(argumentPrefix)\n\t\t? argumentPrefix.length\n\t\t: Math.max(argumentPrefix.lastIndexOf(\" \"), argumentPrefix.lastIndexOf(\"\\t\")) + 1;\n\tconst token = argumentPrefix.slice(tokenStart);\n\tconst equalsIndex = token.indexOf(\"=\");\n\tif (equalsIndex > 0) {\n\t\tconst inputName = token.slice(0, equalsIndex);\n\t\tconst input = workflow.inputs[inputName];\n\t\tif (input?.kind !== \"boolean\") return null;\n\t\treturn completeWorkflowToken(argumentPrefix, [\n\t\t\t{ value: `${inputName}=true `, label: \"true\", description: inputName },\n\t\t\t{ value: `${inputName}=false `, label: \"false\", description: inputName },\n\t\t]);\n\t}\n\n\treturn completeWorkflowToken(argumentPrefix, [\n\t\t{ value: \"--no-picker \", label: \"--no-picker\", description: \"Skip interactive input picker\" },\n\t\t{ value: \"--help \", label: \"--help\", description: \"Show this workflow's input schema\" },\n\t\t...Object.entries(workflow.inputs).map(([name, input]) => ({\n\t\t\tvalue: `${name}=`,\n\t\t\tlabel: name,\n\t\t\tdescription: input.description,\n\t\t})),\n\t]);\n}\n\nexport const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [\n\t{ name: \"settings\", description: \"Open settings menu\" },\n\t{ name: \"model\", description: \"Select model (opens selector UI)\" },\n\t{ name: \"scoped-models\", description: \"Enable/disable models for ctrl+p cycling\" },\n\t{ name: \"fast\", description: \"Configure Codex fast mode for chat and workflows\" },\n\t{ name: \"export\", description: \"Export session (HTML default, or specify path: .html/.jsonl)\" },\n\t{ name: \"import\", description: \"Import and resume a session from a JSONL file\" },\n\t{ name: \"share\", description: \"Share session as a secret GitHub gist\" },\n\t{ name: \"copy\", description: \"Copy last agent message to clipboard\" },\n\t{ name: \"name\", description: \"Set session display name\" },\n\t{ name: \"session\", description: \"Show session info and stats\" },\n\t{ name: \"changelog\", description: \"Show changelog entries\" },\n\t{\n\t\tname: ATOMIC_GUIDE_COMMAND_NAME,\n\t\tdescription: ATOMIC_GUIDE_COMMAND_DESCRIPTION,\n\t\tgetArgumentCompletions: getAtomicGuideArgumentCompletions,\n\t},\n\t{ name: \"hotkeys\", description: \"Show all keyboard shortcuts\" },\n\t{ name: \"fork\", description: \"Create a new fork from a previous user message\" },\n\t{ name: \"clone\", description: \"Duplicate the current session at the current position\" },\n\t{ name: \"tree\", description: \"Navigate session tree (switch branches)\" },\n\t{ name: \"trust\", description: \"Save project trust decision for future sessions\" },\n\t{ name: \"login\", description: \"Configure provider authentication\" },\n\t{ name: \"logout\", description: \"Remove provider authentication\" },\n\t{ name: \"new\", description: \"Start a new session\" },\n\t{ name: \"compact\", description: \"Compact older context with verbatim line ranges\" },\n\t{ name: \"resume\", description: \"Resume a different session\" },\n\t{ name: \"reload\", description: \"Reload keybindings, extensions, skills, prompts, and themes\" },\n\t{ name: \"exit\", description: `Exit ${APP_NAME}` },\n\t{ name: \"quit\", description: `Quit ${APP_NAME}` },\n];\n\nexport const BUNDLED_EXTENSION_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [\n\t{\n\t\tname: \"workflow\",\n\t\tdescription: \"Run or inspect Atomic workflows. Usage: /workflow <name> [key=value…] | /workflow [list|status|connect|attach|interrupt|kill|pause|resume|inputs|reload] [args]\",\n\t\tgetArgumentCompletions: getBundledWorkflowArgumentCompletions,\n\t},\n\t{ name: \"run\", description: \"Run a subagent directly: /run agent[output=file] [task] [--bg] [--fork]\" },\n\t{ name: \"chain\", description: \"Run agents in sequence: /chain scout task -> planner [--bg] [--fork]\" },\n\t{ name: \"run-chain\", description: \"Run a saved chain: /run-chain chainName -- task [--bg] [--fork]\" },\n\t{ name: \"parallel\", description: \"Run agents in parallel: /parallel scout task1 -> reviewer task2 [--bg] [--fork]\" },\n\t{ name: \"subagents-doctor\", description: \"Show subagent diagnostics\" },\n\t{ name: \"mcp\", description: \"Show MCP server status\" },\n\t{ name: \"mcp-auth\", description: \"Authenticate with an MCP server (OAuth)\" },\n\t{ name: \"curator\", description: \"Toggle or configure the search curator workflow\" },\n\t{ name: \"google-account\", description: \"Show the active Google account for Gemini Web\" },\n\t{ name: \"search\", description: \"Browse stored web search results\" },\n\t{ name: \"websearch\", description: \"Open web search curator\" },\n\t{ name: \"intercom\", description: \"Open session intercom overlay\" },\n];\n"]}
1
+ {"version":3,"file":"slash-commands.d.ts","sourceRoot":"","sources":["../../src/core/slash-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAO/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB,CAAC,EAAE,CACxB,cAAc,EAAE,MAAM,KAClB,gBAAgB,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,CAAC;CACpE;AA4FD,wBAAgB,qCAAqC,CAAC,cAAc,EAAE,MAAM,GAAG,gBAAgB,EAAE,GAAG,IAAI,CA4CvG;AAED,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,mBAAmB,CA8BrE,CAAC;AAEF,eAAO,MAAM,gCAAgC,EAAE,aAAa,CAAC,mBAAmB,CAkB/E,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"source-info.d.ts","sourceRoot":"","sources":["../../src/core/source-info.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;AAC3D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,WAAW,CAAC;AAEnD,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,UAAU,CAQjF;AAED,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IACR,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GACC,UAAU,CAQZ","sourcesContent":["import type { PathMetadata } from \"./package-manager.ts\";\n\nexport type SourceScope = \"user\" | \"project\" | \"temporary\";\nexport type SourceOrigin = \"package\" | \"top-level\";\n\nexport interface SourceInfo {\n\tpath: string;\n\tsource: string;\n\tscope: SourceScope;\n\torigin: SourceOrigin;\n\tbaseDir?: string;\n}\n\nexport function createSourceInfo(path: string, metadata: PathMetadata): SourceInfo {\n\treturn {\n\t\tpath,\n\t\tsource: metadata.source,\n\t\tscope: metadata.scope,\n\t\torigin: metadata.origin,\n\t\tbaseDir: metadata.baseDir,\n\t};\n}\n\nexport function createSyntheticSourceInfo(\n\tpath: string,\n\toptions: {\n\t\tsource: string;\n\t\tscope?: SourceScope;\n\t\torigin?: SourceOrigin;\n\t\tbaseDir?: string;\n\t},\n): SourceInfo {\n\treturn {\n\t\tpath,\n\t\tsource: options.source,\n\t\tscope: options.scope ?? \"temporary\",\n\t\torigin: options.origin ?? \"top-level\",\n\t\tbaseDir: options.baseDir,\n\t};\n}\n"]}
1
+ {"version":3,"file":"source-info.d.ts","sourceRoot":"","sources":["../../src/core/source-info.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;AAC3D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,WAAW,CAAC;AAEnD,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,UAAU,CAQjF;AAED,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IACR,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GACC,UAAU,CAQZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAyB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAahE,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4GAA4G;IAC5G,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,wFAAwF;IACxF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,sEAAsE;IACtE,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,+DAA+D;IAC/D,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gCAAgC;IAChC,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,yBAAyB;IACzB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CAClB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,CAyK3E","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\nconst DEFAULT_PROMPT_TOOLS = [\n \"read\",\n \"bash\",\n \"edit\",\n \"write\",\n \"find\",\n \"search\",\n \"ask_user_question\",\n \"todo\",\n] as const;\n\nexport interface SystemPromptModel {\n /** Provider identifier for the selected model. */\n provider: string;\n /** Stable provider-specific model identifier. */\n id: string;\n /** Human-readable model name, when available. */\n name?: string;\n}\n\nexport interface BuildSystemPromptOptions {\n /** Custom system prompt (replaces default). */\n customPrompt?: string;\n /** Tools to include in prompt. Default: [read, bash, edit, write, find, search, ask_user_question, todo] */\n selectedTools?: string[];\n /** Tool names explicitly excluded by the caller and omitted from generated guidance. */\n excludedTools?: string[];\n /** Optional one-line tool snippets keyed by tool name. */\n toolSnippets?: Record<string, string>;\n /** Additional guideline bullets appended to the default system prompt guidelines. */\n promptGuidelines?: string[];\n /** Text to append to system prompt. */\n appendSystemPrompt?: string;\n /** Working directory. */\n cwd: string;\n /** Currently selected model, used for model-aware prompt metadata. */\n selectedModel?: SystemPromptModel;\n /** Current reasoning/thinking level for the selected model. */\n selectedThinkingLevel?: string;\n /** Pre-loaded context files. */\n contextFiles?: Array<{ path: string; content: string }>;\n /** Pre-loaded skills. */\n skills?: Skill[];\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n const {\n customPrompt,\n selectedTools,\n excludedTools,\n toolSnippets,\n promptGuidelines,\n appendSystemPrompt,\n cwd,\n selectedModel,\n selectedThinkingLevel,\n contextFiles: providedContextFiles,\n skills: providedSkills,\n } = options;\n const resolvedCwd = cwd;\n const promptCwd = resolvedCwd.replace(/\\\\/g, \"/\");\n\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, \"0\");\n const day = String(now.getDate()).padStart(2, \"0\");\n const date = `${year}-${month}-${day}`;\n\n const appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n const modelName =\n selectedModel?.name?.trim() || selectedModel?.id || \"unknown\";\n const modelReasoningLevel = selectedThinkingLevel?.trim() || \"off\";\n\n const contextFiles = providedContextFiles ?? [];\n const skills = providedSkills ?? [];\n const explicitlyExcludedTools = new Set(excludedTools ?? []);\n const isPromptToolAvailable = (name: string): boolean =>\n (!selectedTools || selectedTools.includes(name)) &&\n !explicitlyExcludedTools.has(name);\n\n if (customPrompt) {\n let prompt = customPrompt;\n\n if (appendSection) {\n prompt += appendSection;\n }\n\n // Append project context files\n if (contextFiles.length > 0) {\n prompt += \"\\n\\n# Project Context\\n\\n\";\n prompt += \"Project-specific instructions and guidelines:\\n\\n\";\n for (const { path: filePath, content } of contextFiles) {\n prompt += `<context_file path=\\\"${filePath}\\\">\\n${content}\\n</context_file>\\n\\n`;\n }\n }\n\n // Append skills section (only if read tool is available)\n if (isPromptToolAvailable(\"read\") && skills.length > 0) {\n prompt += formatSkillsForPrompt(skills);\n }\n\n // Add model metadata, date, and working directory last\n prompt += `\\nModel name (used for commit attribution): ${modelName}`;\n prompt += `\\nModel reasoning level: ${modelReasoningLevel}`;\n prompt += `\\nCurrent date: ${date}`;\n prompt += `\\nCurrent working directory: ${promptCwd}`;\n\n return prompt;\n }\n\n // Get absolute paths to documentation and examples\n const readmePath = getReadmePath();\n const docsPath = getDocsPath();\n const examplesPath = getExamplesPath();\n\n // Build tools list based on selected tools.\n // A tool appears in Available tools only when the caller provides a one-line snippet.\n const tools = (selectedTools ?? DEFAULT_PROMPT_TOOLS).filter(\n (name) => !explicitlyExcludedTools.has(name),\n );\n const visibleTools = tools.filter((name) => !!toolSnippets?.[name]);\n const toolsList =\n visibleTools.length > 0\n ? visibleTools\n .map((name) => `- ${name}: ${toolSnippets![name]}`)\n .join(\"\\n\")\n : \"(none)\";\n\n // Build guidelines based on which tools are actually available\n const guidelinesList: string[] = [];\n const guidelinesSet = new Set<string>();\n const addGuideline = (guideline: string): void => {\n if (guidelinesSet.has(guideline)) {\n return;\n }\n guidelinesSet.add(guideline);\n guidelinesList.push(guideline);\n };\n\n const hasBash = tools.includes(\"bash\");\n const hasFind = tools.includes(\"find\");\n const hasLs = tools.includes(\"ls\");\n const hasRead = tools.includes(\"read\");\n const shouldIncludeAskUserFallbackGuidance =\n selectedTools !== undefined &&\n tools.length > 0 &&\n !tools.includes(\"ask_user_question\") &&\n !explicitlyExcludedTools.has(\"ask_user_question\");\n\n // File exploration guidelines\n if (hasBash && !hasFind && !hasLs) {\n addGuideline(\"Use bash for file operations like ls, rg, find\");\n }\n if (shouldIncludeAskUserFallbackGuidance) {\n addGuideline(\"Clarify ambiguous requirements using the ask_user_question tool if available.\");\n }\n\n for (const guideline of promptGuidelines ?? []) {\n const normalized = guideline.trim();\n if (normalized.length > 0) {\n addGuideline(normalized);\n }\n }\n\n // Always include these\n addGuideline(\"Be concise in your responses\");\n addGuideline(\"Show file paths clearly when working with files\");\n\n const guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n let prompt = `You are an expert coding assistant operating named Atomic, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nAtomic documentation (read only when the user asks about customizing Atomic itself, its SDK, creating workflows, packages, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- Docs/examples references above must be resolved against these absolute roots; e.g. docs/foo.md means ${docsPath}/foo.md and examples/bar means ${examplesPath}/bar.\n- When asked about: atomic workflows (docs/workflows.md), extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), atomic packages (docs/packages.md)\n- When working on Atomic topics, read the docs and examples, and follow .md cross-references before implementing\n- Always read Atomic .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n if (appendSection) {\n prompt += appendSection;\n }\n\n // Append project context files\n if (contextFiles.length > 0) {\n prompt += \"\\n\\n# Project Context\\n\\n\";\n prompt += \"Project-specific instructions and guidelines:\\n\\n\";\n for (const { path: filePath, content } of contextFiles) {\n prompt += `<context_file path=\\\"${filePath}\\\">\\n${content}\\n</context_file>\\n\\n`;\n }\n }\n\n // Append skills section (only if read tool is available)\n if (hasRead && skills.length > 0) {\n prompt += formatSkillsForPrompt(skills);\n }\n\n // Add model metadata, date, and working directory last\n prompt += `\\nModel name (used for commit attribution): ${modelName}`;\n prompt += `\\nModel reasoning level: ${modelReasoningLevel}`;\n prompt += `\\nCurrent date: ${date}`;\n prompt += `\\nCurrent working directory: ${promptCwd}`;\n\n return prompt;\n}\n"]}
1
+ {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAyB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAahE,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4GAA4G;IAC5G,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,wFAAwF;IACxF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,sEAAsE;IACtE,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,+DAA+D;IAC/D,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gCAAgC;IAChC,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,yBAAyB;IACzB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CAClB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,CAyK3E"}
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/core/telemetry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAO7D,wBAAgB,yBAAyB,CACxC,eAAe,EAAE,eAAe,EAChC,YAAY,GAAE,MAAM,GAAG,SAAsC,GAC3D,OAAO,CAET","sourcesContent":["import { ENV_TELEMETRY, getEnvValue } from \"../config.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nfunction isTruthyEnvFlag(value: string | undefined): boolean {\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\nexport function isInstallTelemetryEnabled(\n\tsettingsManager: SettingsManager,\n\ttelemetryEnv: string | undefined = getEnvValue(ENV_TELEMETRY),\n): boolean {\n\treturn telemetryEnv !== undefined ? isTruthyEnvFlag(telemetryEnv) : settingsManager.getEnableInstallTelemetry();\n}\n"]}
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/core/telemetry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAO7D,wBAAgB,yBAAyB,CACxC,eAAe,EAAE,eAAe,EAChC,YAAY,GAAE,MAAM,GAAG,SAAsC,GAC3D,OAAO,CAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"thinking-blocks.d.ts","sourceRoot":"","sources":["../../src/core/thinking-blocks.ts"],"names":[],"mappings":"AAAA,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU,GAAG,mBAAmB,CAEpG;AAED,wBAAgB,qCAAqC,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAK1F;AAED,wBAAgB,uCAAuC,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAI/G","sourcesContent":["export function isAssistantThinkingBlockType(type: unknown): type is \"thinking\" | \"redacted_thinking\" {\n\treturn type === \"thinking\" || type === \"redacted_thinking\";\n}\n\nexport function contentArrayHasAssistantThinkingBlock(content: readonly unknown[]): boolean {\n\treturn content.some((block) => {\n\t\tif (!block || typeof block !== \"object\") return false;\n\t\treturn isAssistantThinkingBlockType((block as { type?: unknown }).type);\n\t});\n}\n\nexport function messageHasAssistantThinkingContentBlock(message: { role?: unknown; content?: unknown }): boolean {\n\treturn message.role === \"assistant\" &&\n\t\tArray.isArray(message.content) &&\n\t\tcontentArrayHasAssistantThinkingBlock(message.content);\n}\n"]}
1
+ {"version":3,"file":"thinking-blocks.d.ts","sourceRoot":"","sources":["../../src/core/thinking-blocks.ts"],"names":[],"mappings":"AAAA,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU,GAAG,mBAAmB,CAEpG;AAED,wBAAgB,qCAAqC,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAK1F;AAED,wBAAgB,uCAAuC,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAI/G"}
@@ -1 +1 @@
1
- {"version":3,"file":"timings.d.ts","sourceRoot":"","sources":["../../src/core/timings.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,YAAY,CAAC;AAIhD,MAAM,WAAW,UAAU;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,WAAW,CAAC;CACvB;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAYD,wBAAgB,YAAY,CAAC,SAAS,GAAE,WAAoB,GAAG,IAAI,CAIlE;AAED,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,WAAoB,GAAG,IAAI,CAMzE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,WAAoB,GAAG,UAAU,GAAG,IAAI,CAGjG;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAM3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,GAAE,WAAoB,GAAG,IAAI,CAI7F;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,WAAoB,GAAG,IAAI,CAIzF;AAaD,wBAAgB,YAAY,IAAI,IAAI,CAKnC","sourcesContent":["/**\n * Central timing instrumentation for startup profiling.\n * Enable with the app-specific timing environment variable (for Atomic, ATOMIC_TIMING=1).\n */\n\nimport { ENV_TIMING, getEnvValue } from \"../config.ts\";\n\nconst ENABLED = getEnvValue(ENV_TIMING) === \"1\";\n\ninterface TimingNamespace {\n\ttimings: Array<{ label: string; ms: number }>;\n\tlastTime: number;\n\tresetTime: number;\n}\n\nexport type TimingLabel = \"main\" | \"extensions\";\n\nconst timingNamespaces = new Map<TimingLabel, TimingNamespace>();\n\nexport interface TimingSpan {\n\tlabel: string;\n\tstart: number;\n\tnamespace: TimingLabel;\n}\n\nexport function isTimingEnabled(): boolean {\n\treturn ENABLED;\n}\n\nfunction ensureNamespace(namespace: TimingLabel): TimingNamespace {\n\tlet entry = timingNamespaces.get(namespace);\n\tif (!entry) {\n\t\tconst now = Date.now();\n\t\tentry = { timings: [], lastTime: now, resetTime: now };\n\t\ttimingNamespaces.set(namespace, entry);\n\t}\n\treturn entry;\n}\n\nexport function resetTimings(namespace: TimingLabel = \"main\"): void {\n\tif (!ENABLED) return;\n\tconst now = Date.now();\n\ttimingNamespaces.set(namespace, { timings: [], lastTime: now, resetTime: now });\n}\n\nexport function time(label: string, namespace: TimingLabel = \"main\"): void {\n\tif (!ENABLED) return;\n\tconst now = Date.now();\n\tconst timingNamespace = ensureNamespace(namespace);\n\ttimingNamespace.timings.push({ label, ms: now - timingNamespace.lastTime });\n\ttimingNamespace.lastTime = now;\n}\n\nexport function startTimingSpan(label: string, namespace: TimingLabel = \"main\"): TimingSpan | null {\n\tif (!ENABLED) return null;\n\treturn { label, start: Date.now(), namespace };\n}\n\nexport function endTimingSpan(span: TimingSpan | null): void {\n\tif (!ENABLED || !span) return;\n\tconst now = Date.now();\n\tconst timingNamespace = ensureNamespace(span.namespace);\n\ttimingNamespace.timings.push({ label: span.label, ms: now - span.start });\n\ttimingNamespace.lastTime = now;\n}\n\nexport function recordTiming(label: string, ms: number, namespace: TimingLabel = \"main\"): void {\n\tif (!ENABLED) return;\n\tconst timingNamespace = ensureNamespace(namespace);\n\ttimingNamespace.timings.push({ label, ms });\n}\n\nexport function recordTimeSinceReset(label: string, namespace: TimingLabel = \"main\"): void {\n\tif (!ENABLED) return;\n\tconst timingNamespace = ensureNamespace(namespace);\n\ttimingNamespace.timings.push({ label, ms: Date.now() - timingNamespace.resetTime });\n}\n\nfunction printTimingGroup(title: string, namespace: TimingNamespace): void {\n\tconst printableTimings = namespace.timings.filter((timing) => timing.ms >= 0);\n\tif (printableTimings.length === 0) return;\n\tconsole.error(`\\n--- ${title} ---`);\n\tfor (const t of printableTimings) {\n\t\tconsole.error(` ${t.label}: ${t.ms}ms`);\n\t}\n\tconsole.error(` TOTAL initialization time: ${Date.now() - namespace.resetTime}ms`);\n\tconsole.error(`${\"-\".repeat(title.length + 8)}\\n`);\n}\n\nexport function printTimings(): void {\n\tif (!ENABLED) return;\n\tfor (const [namespace, timingNamespace] of timingNamespaces) {\n\t\tprintTimingGroup(`Startup Timings: ${namespace}`, timingNamespace);\n\t}\n}\n"]}
1
+ {"version":3,"file":"timings.d.ts","sourceRoot":"","sources":["../../src/core/timings.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,YAAY,CAAC;AAIhD,MAAM,WAAW,UAAU;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,WAAW,CAAC;CACvB;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAYD,wBAAgB,YAAY,CAAC,SAAS,GAAE,WAAoB,GAAG,IAAI,CAIlE;AAED,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,WAAoB,GAAG,IAAI,CAMzE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,WAAoB,GAAG,UAAU,GAAG,IAAI,CAGjG;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAM3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,GAAE,WAAoB,GAAG,IAAI,CAI7F;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,WAAoB,GAAG,IAAI,CAIzF;AAaD,wBAAgB,YAAY,IAAI,IAAI,CAKnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-protocol.d.ts","sourceRoot":"","sources":["../../../src/core/tools/artifact-protocol.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAItE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAErD;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGjE;AAMD,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,GAAG,SAAS,CActG;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,SAAS,MAAM,EAAE,GAAG,sBAAsB,CAanG;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAIjF","sourcesContent":["/**\n * artifact:// protocol resolution, mirrored from oh-my-pi's\n * `packages/coding-agent/src/internal-urls/artifact-protocol.ts` at 15b5c1397fc.\n *\n * `artifact://<numericId>` resolves to an artifact file by matching the\n * `${id}.` prefix. The calling session's artifacts dir is searched first,\n * then any other registered active dirs (so subagents can see parent\n * artifacts). Artifacts are immutable reads.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { getArtifactManager } from \"./artifacts.ts\";\nimport type { InternalResourceRouter } from \"./resource-selectors.ts\";\n\nconst activeArtifactDirs = new Set<string>();\n\nexport function registerArtifactDir(dir: string): void {\n\tactiveArtifactDirs.add(dir);\n}\n\nexport function unregisterArtifactDir(dir: string): void {\n\tactiveArtifactDirs.delete(dir);\n}\n\nexport function artifactIdFromUrl(url: string): string | undefined {\n\tconst match = url.match(/^artifact:\\/\\/([^/]+)/i);\n\treturn match?.[1];\n}\n\nfunction isValidId(id: string): boolean {\n\treturn /^\\d+$/.test(id);\n}\n\n/** Resolve an artifact:// url to a backing file path across known dirs. */\nexport function resolveArtifactUrl(url: string, pinnedDirs: readonly string[] = []): string | undefined {\n\tconst id = artifactIdFromUrl(url);\n\tif (id === undefined) throw new Error(\"artifact:// URL requires a numeric ID: artifact://0\");\n\tif (!isValidId(id)) throw new Error(`artifact:// ID must be numeric, got: ${id}`);\n\tconst ordered = [...pinnedDirs, ...activeArtifactDirs];\n\tconst seen = new Set<string>();\n\tfor (const dir of ordered) {\n\t\tif (seen.has(dir)) continue;\n\t\tseen.add(dir);\n\t\tconst resolved = getArtifactManager(dir).resolve(id);\n\t\tif (resolved) return resolved;\n\t}\n\tconst available = [...new Set([...pinnedDirs, ...activeArtifactDirs])].flatMap((dir) => getArtifactManager(dir).list());\n\tthrow new Error(`Artifact ${id} not found. Available: ${available.join(\", \") || \"none\"}`);\n}\n\n/** Build an InternalResourceRouter that resolves/reads artifact:// urls. */\nexport function createArtifactRouter(getPinnedDirs: () => readonly string[]): InternalResourceRouter {\n\treturn {\n\t\tresolve: (url) => {\n\t\t\tif (!/^artifact:\\/\\//i.test(url)) return undefined;\n\t\t\ttry { return resolveArtifactUrl(url, getPinnedDirs()); } catch { return undefined; }\n\t\t},\n\t\tread: (url) => {\n\t\t\tif (!/^artifact:\\/\\//i.test(url)) return undefined;\n\t\t\tconst path = resolveArtifactUrl(url, getPinnedDirs());\n\t\t\tif (!path || !existsSync(path)) throw new Error(`Artifact not found: ${url}`);\n\t\t\treturn readFileSync(path, \"utf8\");\n\t\t},\n\t};\n}\n\n/** Resolve and read an artifact directly from a context's artifact dirs. */\nexport function readArtifactUrl(url: string, dirs: readonly string[] = []): string {\n\tconst path = resolveArtifactUrl(url, dirs);\n\tif (!path || !existsSync(path)) throw new Error(`Artifact not found: ${url}`);\n\treturn readFileSync(path, \"utf8\");\n}\n"]}
1
+ {"version":3,"file":"artifact-protocol.d.ts","sourceRoot":"","sources":["../../../src/core/tools/artifact-protocol.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAItE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAErD;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGjE;AAMD,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,GAAG,SAAS,CActG;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,SAAS,MAAM,EAAE,GAAG,sBAAsB,CAanG;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAIjF"}
@@ -1 +1 @@
1
- {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../../src/core/tools/artifacts.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACX;AAED,qBAAa,eAAe;;IAK3B,YAAY,GAAG,EAAE,MAAM,EAEtB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAkBD,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAK5C;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM9C;IAED,mFAAmF;IACnF,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMtC;IAED,IAAI,IAAI,MAAM,EAAE,CASf;CACD;AAID,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,CAOxE;AAED,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD","sourcesContent":["/**\n * Session artifact storage, mirrored from oh-my-pi's\n * `packages/coding-agent/src/session/artifacts.ts` at 15b5c1397fc.\n *\n * Artifacts are persisted as `<numericId>.<toolType>.log` files inside a\n * session artifacts directory. IDs are sequential and scan-initialized from\n * existing files so a resumed session keeps a contiguous id space. Subagents\n * that share a session dir share the same manager/id space.\n */\nimport { existsSync, mkdirSync, readdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface AllocatedArtifact {\n\tpath: string;\n\tid: string;\n}\n\nexport class ArtifactManager {\n\t#nextId = 0;\n\treadonly #dir: string;\n\t#initialized = false;\n\n\tconstructor(dir: string) {\n\t\tthis.#dir = dir;\n\t}\n\n\tget dir(): string {\n\t\treturn this.#dir;\n\t}\n\n\t#init(): void {\n\t\tif (this.#initialized) return;\n\t\tthis.#initialized = true;\n\t\tlet max = -1;\n\t\tif (existsSync(this.#dir)) {\n\t\t\tfor (const name of readdirSync(this.#dir)) {\n\t\t\t\tconst match = name.match(/^(\\d+)\\..*\\.log$/);\n\t\t\t\tif (match) {\n\t\t\t\t\tconst id = Number.parseInt(match[1] ?? \"0\", 10);\n\t\t\t\t\tif (id > max) max = id;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.#nextId = max + 1;\n\t}\n\n\tallocate(toolType: string): AllocatedArtifact {\n\t\tthis.#init();\n\t\tconst id = String(this.#nextId++);\n\t\tconst path = join(this.#dir, `${id}.${toolType}.log`);\n\t\treturn { path, id };\n\t}\n\n\tsave(content: string, toolType: string): string {\n\t\tthis.#init();\n\t\tconst { path, id } = this.allocate(toolType);\n\t\tif (!existsSync(this.#dir)) mkdirSync(this.#dir, { recursive: true });\n\t\twriteFileSync(path, content, \"utf8\");\n\t\treturn id;\n\t}\n\n\t/** Resolve a numeric id to its artifact file path, matching by `${id}.` prefix. */\n\tresolve(id: string): string | undefined {\n\t\tthis.#init();\n\t\tif (!existsSync(this.#dir)) return undefined;\n\t\tconst prefix = `${id}.`;\n\t\tfor (const name of readdirSync(this.#dir)) if (name.startsWith(prefix) && name.endsWith(\".log\")) return join(this.#dir, name);\n\t\treturn undefined;\n\t}\n\n\tlist(): string[] {\n\t\tthis.#init();\n\t\tif (!existsSync(this.#dir)) return [];\n\t\tconst ids: number[] = [];\n\t\tfor (const name of readdirSync(this.#dir)) {\n\t\t\tconst match = name.match(/^(\\d+)\\..*\\.log$/);\n\t\t\tif (match) ids.push(Number.parseInt(match[1] ?? \"0\", 10));\n\t\t}\n\t\treturn ids.sort((a, b) => a - b).map(String);\n\t}\n}\n\nconst managers = new Map<string, ArtifactManager>();\n\n/** Returns a shared ArtifactManager for a session artifacts dir (cached per dir). */\nexport function getArtifactManager(artifactsDir: string): ArtifactManager {\n\tlet manager = managers.get(artifactsDir);\n\tif (!manager) {\n\t\tmanager = new ArtifactManager(artifactsDir);\n\t\tmanagers.set(artifactsDir, manager);\n\t}\n\treturn manager;\n}\n\nexport function resetArtifactManagerCache(): void {\n\tmanagers.clear();\n}\n"]}
1
+ {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../../src/core/tools/artifacts.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACX;AAED,qBAAa,eAAe;;IAK3B,YAAY,GAAG,EAAE,MAAM,EAEtB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAkBD,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAK5C;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM9C;IAED,mFAAmF;IACnF,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMtC;IAED,IAAI,IAAI,MAAM,EAAE,CASf;CACD;AAID,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,CAOxE;AAED,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"ask-user-question.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/ask-user-question/ask-user-question.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EAIN,KAAK,YAAY,EAGjB,oBAAoB,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAI/E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,kBAAkB,EAAE,CAWlF;AAED,eAAO,MAAM,sBAAsB,iGAA0I,CAAC;AAC9K,eAAO,MAAM,yBAAyB,EAAE,MAAM,EAK7C,CAAC;AAEF,wBAAgB,mCAAmC,IAAI,cAAc,CAAC,OAAO,oBAAoB,EAAE,OAAO,CAAC,CA4E1G;AAED,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,CAAC","sourcesContent":["import type { ToolDefinition } from \"../../extensions/types.ts\";\nimport { loadConfig, validateGuidanceFields } from \"./config.ts\";\nimport { QuestionnaireSession } from \"./state/questionnaire-session.ts\";\nimport { ROW_INTENT_META, sentinelsToAppend } from \"./state/row-intent.ts\";\nimport { buildQuestionnaireResponse, buildToolResult } from \"./tool/response-envelope.ts\";\nimport {\n\tMAX_OPTIONS,\n\tMAX_QUESTIONS,\n\tMIN_OPTIONS,\n\ttype QuestionData,\n\ttype QuestionnaireResult,\n\ttype QuestionParams,\n\tQuestionParamsSchema,\n} from \"./tool/types.ts\";\nimport { validateQuestionnaire } from \"./tool/validate-questionnaire.ts\";\nimport type { WrappingSelectItem } from \"./view/components/wrapping-select.ts\";\n\nconst ERROR_NO_UI = \"Error: UI not available (running in non-interactive mode)\";\n\nexport function buildItemsForQuestion(question: QuestionData): WrappingSelectItem[] {\n\tconst items: WrappingSelectItem[] = question.options.map((o) => ({\n\t\tkind: \"option\",\n\t\tlabel: o.label,\n\t\tdescription: o.description,\n\t}));\n\tconst hasAnyPreview = question.options.some((o) => typeof o.preview === \"string\" && o.preview.length > 0);\n\tfor (const kind of sentinelsToAppend(question, hasAnyPreview)) {\n\t\titems.push({ kind, label: ROW_INTENT_META[kind].label });\n\t}\n\treturn items;\n}\n\nexport const DEFAULT_PROMPT_SNIPPET = `Ask the user up to ${MAX_QUESTIONS} structured questions (${MIN_OPTIONS}-${MAX_OPTIONS} options each) when requirements are ambiguous`;\nexport const DEFAULT_PROMPT_GUIDELINES: string[] = [\n\t`Use ask_user_question whenever the user's request is underspecified and you cannot proceed without concrete decisions — you can ask up to ${MAX_QUESTIONS} questions per invocation.`,\n\t`Each question MUST have ${MIN_OPTIONS}-${MAX_OPTIONS} options. Every option requires a concise label (1-5 words) and a description explaining what the choice means or its trade-offs. The user can additionally type a custom answer (\"Type something.\" row is appended automatically to single-select questions) or pick \"Chat about this\" to abandon the questionnaire.`,\n\t`Set multiSelect: true when multiple answers are valid; this suppresses the \"Type something.\" row. Provide an options[].preview markdown string when an option benefits from richer side-by-side context (mockups, code snippets, diagrams, configs) — single-select only. NOTE: any non-empty preview on a single-select question ALSO suppresses the \"Type something.\" row (no room in the side-by-side layout); \"Chat about this\" remains the escape hatch. If you recommend a specific option, make it the first option and append \"(Recommended)\" to its label.`,\n\t\"Do not stack multiple ask_user_question calls back-to-back — group all clarifying questions into one invocation.\",\n];\n\nexport function createAskUserQuestionToolDefinition(): ToolDefinition<typeof QuestionParamsSchema, unknown> {\n\tconst guidance = validateGuidanceFields(loadConfig().guidance);\n\treturn {\n\t\tname: \"ask_user_question\",\n\t\tlabel: \"Ask User Question\",\n\t\tdescription: `Ask the user one or more structured questions during execution. Use when you need to:\n1. Gather user preferences or requirements\n2. Clarify ambiguous instructions\n3. Get decisions on implementation choices as you work\n4. Offer choices to the user about what direction to take\n\nUsage notes:\n- Users will always be able to type a custom answer (\"Type something.\" row is appended automatically to every single-select question) or pick \"Chat about this\" to abandon the questionnaire and continue in free-form conversation. Do NOT author \"Other\" / \"Type something.\" / \"Chat about this\" labels yourself — duplicates are rejected at runtime.\n- Use multiSelect: true to allow multiple answers to be selected for a question. The \"Type something.\" row is suppressed on multi-select questions, and is ALSO suppressed on single-select questions where any option carries a \\`preview\\` (the side-by-side layout has no room for inline custom text — \"Chat about this\" remains as the free-form escape hatch).\n- If you recommend a specific option, make that the first option in the list and add \"(Recommended)\" at the end of the label.\n\nPreview feature:\nUse the optional \\`preview\\` field on options when presenting concrete artifacts that users need to visually compare:\n- ASCII mockups of UI layouts or components\n- Code snippets showing different implementations\n- Diagram variations\n- Configuration examples\n\nPreview content is rendered as markdown in a monospace box. Multi-line text with newlines is supported. When any option has a preview, the UI switches to a side-by-side layout with a vertical option list on the left and preview on the right. Do not use previews for simple preference questions where labels and descriptions suffice. Note: previews are only supported for single-select questions (not multiSelect).`,\n\t\tpromptSnippet: guidance.promptSnippet ?? DEFAULT_PROMPT_SNIPPET,\n\t\tpromptGuidelines: guidance.promptGuidelines ?? DEFAULT_PROMPT_GUIDELINES,\n\t\tparameters: QuestionParamsSchema,\n\n\t\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\t\tconst typed = params as unknown as QuestionParams;\n\t\t\tif (!ctx.hasUI) return buildToolResult(ERROR_NO_UI, { answers: [], cancelled: true, error: \"no_ui\" });\n\n\t\t\tconst validation = validateQuestionnaire(typed);\n\t\t\tif (!validation.ok) {\n\t\t\t\treturn buildToolResult(validation.message, {\n\t\t\t\t\tanswers: [],\n\t\t\t\t\tcancelled: true,\n\t\t\t\t\terror: validation.error,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst itemsByTab: WrappingSelectItem[][] = typed.questions.map((q) => buildItemsForQuestion(q));\n\n\t\t\t// Suspend the animated working loader for the lifetime of the blocking dialog.\n\t\t\t//\n\t\t\t// The questionnaire mounts inline, directly below the status/working-loader row.\n\t\t\t// That loader ticks every ~80ms and calls `requestRender()` on each frame. On a\n\t\t\t// short terminal the (tall) side-by-side dialog pushes the loader line above the\n\t\t\t// viewport top, so pi-tui's differential renderer sees `firstChanged < viewportTop`\n\t\t\t// and falls back to a full clear+replay (`\\x1b[2J\\x1b[H\\x1b[3J`) on every tick. The\n\t\t\t// transcript is replayed before the dialog each time, which is the flicker. The\n\t\t\t// loader conveys nothing while we're blocked on human input, so hide it for the\n\t\t\t// duration and restore it once the dialog closes (on every path).\n\t\t\t//\n\t\t\t// Guarded: some hosts (e.g. the workflow stage-UI broker) pass a minimal UI\n\t\t\t// context that only implements `custom`, so treat a missing loader control as a\n\t\t\t// no-op rather than throwing.\n\t\t\tctx.ui.setWorkingVisible?.(false);\n\t\t\ttry {\n\t\t\t\tconst result = await ctx.ui.custom<QuestionnaireResult>((tui, theme, _kb, done) => {\n\t\t\t\t\tconst session = new QuestionnaireSession({\n\t\t\t\t\t\ttui,\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tparams: typed,\n\t\t\t\t\t\titemsByTab,\n\t\t\t\t\t\tdone,\n\t\t\t\t\t});\n\t\t\t\t\treturn session.component;\n\t\t\t\t}, { signal });\n\n\t\t\t\treturn buildQuestionnaireResponse(result, typed);\n\t\t\t} finally {\n\t\t\t\tctx.ui.setWorkingVisible?.(true);\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport { buildQuestionnaireResponse, buildToolResult };\n"]}
1
+ {"version":3,"file":"ask-user-question.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/ask-user-question/ask-user-question.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EAIN,KAAK,YAAY,EAGjB,oBAAoB,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAI/E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,kBAAkB,EAAE,CAWlF;AAED,eAAO,MAAM,sBAAsB,iGAA0I,CAAC;AAC9K,eAAO,MAAM,yBAAyB,EAAE,MAAM,EAK7C,CAAC;AAEF,wBAAgB,mCAAmC,IAAI,cAAc,CAAC,OAAO,oBAAoB,EAAE,OAAO,CAAC,CA4E1G;AAED,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/ask-user-question/config.ts"],"names":[],"mappings":"AAOA,UAAU,cAAc;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,UAAU,qBAAqB;IAC9B,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,wBAAgB,UAAU,IAAI,qBAAqB,CASlD;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,cAAc,CAetE","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst CONFIG_DIR = join(homedir(), \".config\", \"rpiv-ask-user-question\");\nconst CONFIG_PATH = join(CONFIG_DIR, \"config.json\");\n\ninterface GuidanceFields {\n\tpromptSnippet?: string;\n\tpromptGuidelines?: string[];\n}\n\ninterface AskUserQuestionConfig {\n\tguidance?: GuidanceFields;\n}\n\nexport function loadConfig(): AskUserQuestionConfig {\n\tif (!existsSync(CONFIG_PATH)) return {};\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(CONFIG_PATH, \"utf-8\")) as unknown;\n\t\tif (parsed === null || typeof parsed !== \"object\") return {};\n\t\treturn parsed as AskUserQuestionConfig;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nexport function validateGuidanceFields(fields: unknown): GuidanceFields {\n\tif (!fields || typeof fields !== \"object\") return {};\n\tconst g = fields as Record<string, unknown>;\n\tconst result: GuidanceFields = {};\n\tif (typeof g.promptSnippet === \"string\" && g.promptSnippet.length > 0) {\n\t\tresult.promptSnippet = g.promptSnippet;\n\t}\n\tif (\n\t\tArray.isArray(g.promptGuidelines) &&\n\t\tg.promptGuidelines.length > 0 &&\n\t\tg.promptGuidelines.every((s) => typeof s === \"string\" && s.length > 0)\n\t) {\n\t\tresult.promptGuidelines = g.promptGuidelines;\n\t}\n\treturn result;\n}\n"]}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/ask-user-question/config.ts"],"names":[],"mappings":"AAOA,UAAU,cAAc;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,UAAU,qBAAqB;IAC9B,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,wBAAgB,UAAU,IAAI,qBAAqB,CASlD;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,cAAc,CAetE"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/ask-user-question/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,mCAAmC,EAAE,MAAM,wBAAwB,CAAC","sourcesContent":["/**\n * `ask_user_question` base tool — registered alongside read/bash/edit/write.\n *\n * Upstream: https://github.com/juicesharp/rpiv-mono/tree/main/packages/rpiv-ask-user-question\n * Upstream copyright: (c) 2026 juicesharp — MIT (see ./LICENSE.upstream).\n *\n * Differences from upstream:\n * - i18n is removed entirely. The upstream `state/i18n-bridge.ts`,\n * `locales/*.json`, and the optional `@juicesharp/rpiv-i18n` peer\n * dependency are gone. All UI copy is plain English string literals\n * or local module-level constants.\n * - Exposed as a base tool definition (no extension registration).\n *\n * cross-ref:\n * - ../index.ts (registers via createAllToolDefinitions)\n * - ./ask-user-question.ts (tool definition + execute pipeline)\n */\nexport { createAskUserQuestionToolDefinition } from \"./ask-user-question.ts\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/ask-user-question/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,mCAAmC,EAAE,MAAM,wBAAwB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"build-questionnaire.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/build-questionnaire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,8CAA8C,CAAC;AAC5F,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgBrD,OAAO,KAAK,EAAE,kBAAkB,EAAuB,MAAM,uCAAuC,CAAC;AAErG,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAerE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,wBAAwB;IACxC,GAAG,EAAE;QAAE,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,aAAa,IAAI,IAAI,CAAA;KAAE,CAAC;IAC9D,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IACzD,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,kBAAkB,CAAC;IACjC,aAAa,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,yBAAyB,CAAC;IACnC,UAAU,EAAE,KAAK,CAAC;IAClB,WAAW,EAAE,KAAK,CAAC;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,IAAI,CAAC;CACvB;AAwBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,kBAAkB,CAEvF","sourcesContent":["import { getMarkdownTheme, type Theme } from \"../../../../modes/interactive/theme/theme.ts\";\nimport { Input } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../tool/types.ts\";\nimport {\n\ttype BoundGlobalBinding,\n\ttype BoundPerTabBinding,\n\tglobalBinding,\n\tperTabBinding,\n} from \"../view/component-binding.ts\";\nimport { ChatRowView } from \"../view/components/chat-row-view.ts\";\nimport { MultiSelectView } from \"../view/components/multi-select-view.ts\";\nimport { OptionListView } from \"../view/components/option-list-view.ts\";\nimport { PreviewBlockRenderer } from \"../view/components/preview/preview-block-renderer.ts\";\nimport { crossTabLeftWidthWithDonation } from \"../view/components/preview/preview-layout-decider.ts\";\nimport type { PreviewPaneProps } from \"../view/components/preview/preview-pane.ts\";\nimport { PreviewPane } from \"../view/components/preview/preview-pane.ts\";\nimport { SubmitPicker } from \"../view/components/submit-picker.ts\";\nimport { TabBar } from \"../view/components/tab-bar.ts\";\nimport type { WrappingSelectItem, WrappingSelectTheme } from \"../view/components/wrapping-select.ts\";\nimport { DialogView } from \"../view/dialog-builder.ts\";\nimport { QuestionnairePropsAdapter } from \"../view/props-adapter.ts\";\nimport type { StatefulView } from \"../view/stateful-view.ts\";\nimport type { TabBodyHeights, TabComponents } from \"../view/tab-components.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport type { PerTabSelector } from \"./selectors/contract.ts\";\nimport { selectActivePreviewPaneIndex } from \"./selectors/derivations.ts\";\nimport {\n\tselectChatRowProps,\n\tselectDialogProps,\n\tselectMultiSelectProps,\n\tselectOptionListProps,\n\tselectPreviewPaneProps,\n\tselectSubmitPickerProps,\n\tselectTabBarProps,\n} from \"./selectors/projections.ts\";\nimport type { QuestionnaireState } from \"./state.ts\";\n\nexport interface QuestionnaireBuildConfig {\n\ttui: { terminal: { columns: number }; requestRender(): void };\n\ttheme: Theme;\n\tquestions: readonly QuestionData[];\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n\tisMulti: boolean;\n\tinitialState: QuestionnaireState;\n\tgetCurrentTab: () => number;\n}\n\nexport interface QuestionnaireBuilt {\n\tadapter: QuestionnairePropsAdapter;\n\tnotesInput: Input;\n\tinlineInput: Input;\n\trender: (width: number) => string[];\n\tinvalidate: () => void;\n}\n\ninterface HeightComputers {\n\tglobal: (width: number) => number;\n\tcurrent: (width: number) => number;\n}\n\nfunction previewBodyHeights(pane: PreviewPane): (width: number) => TabBodyHeights {\n\treturn (width) => ({\n\t\tcurrent: pane.naturalHeight(width),\n\t\tmax: pane.maxNaturalHeight(width),\n\t});\n}\n\nfunction multiSelectBodyHeights(view: MultiSelectView): (width: number) => TabBodyHeights {\n\treturn (width) => {\n\t\tconst h = view.naturalHeight(width);\n\t\treturn { current: h, max: h };\n\t};\n}\n\nconst isActiveTab: PerTabSelector<boolean> = (s, ctx) =>\n\tctx.i === selectActivePreviewPaneIndex(s.currentTab, ctx.totalQuestions);\n\n/**\n * Pure factory: assembles every TUI component, the props adapter, and a\n * lifecycle handle. Session-state dependencies arrive via `getCurrentTab` and\n * the `inputBuffer` cell. Initial paint is delegated to\n * `adapter.apply(initialState)` (called by the session at construction-end);\n * no selector is invoked here.\n */\nexport function buildQuestionnaire(config: QuestionnaireBuildConfig): QuestionnaireBuilt {\n\treturn new QuestionnaireBuilder(config).build();\n}\n\n/**\n * Programming-by-intention assembly: each private method names one\n * construction step from `build()`. Read top-down; the class is build-only and\n * discarded once the handle is returned.\n */\nclass QuestionnaireBuilder {\n\tprivate readonly tui: QuestionnaireBuildConfig[\"tui\"];\n\tprivate readonly theme: Theme;\n\tprivate readonly questions: readonly QuestionData[];\n\tprivate readonly itemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n\tprivate readonly isMulti: boolean;\n\tprivate readonly initialState: QuestionnaireState;\n\tprivate readonly getCurrentTab: () => number;\n\n\tprivate readonly selectTheme: WrappingSelectTheme;\n\tprivate readonly markdownTheme = getMarkdownTheme();\n\tprivate readonly chatRow: ChatRowView;\n\tprivate readonly notesInput = new Input();\n\tprivate readonly inlineInput = new Input();\n\tprivate readonly getTerminalWidth = () => this.tui.terminal.columns;\n\n\tconstructor(config: QuestionnaireBuildConfig) {\n\t\tthis.tui = config.tui;\n\t\tthis.theme = config.theme;\n\t\tthis.questions = config.questions;\n\t\tthis.itemsByTab = config.itemsByTab;\n\t\tthis.isMulti = config.isMulti;\n\t\tthis.initialState = config.initialState;\n\t\tthis.getCurrentTab = config.getCurrentTab;\n\n\t\tthis.selectTheme = this.makeSelectTheme();\n\t\tthis.chatRow = new ChatRowView({\n\t\t\titem: { kind: \"chat\", label: ROW_INTENT_META.chat.label },\n\t\t\ttheme: this.selectTheme,\n\t\t});\n\t}\n\n\tbuild(): QuestionnaireBuilt {\n\t\tconst tabs = this.buildTabComponents();\n\t\tthis.injectGlobalLeftWidth(tabs);\n\t\tconst submitPicker = this.buildSubmitPicker();\n\t\tconst tabBar = this.buildTabBar();\n\t\tconst heights = this.buildHeightComputers(tabs);\n\t\tconst dialog = this.buildDialog(tabs, submitPicker, tabBar, heights);\n\t\tconst globalBindings = this.buildGlobalBindings(dialog, submitPicker, tabBar);\n\t\tconst perTabBindings = this.buildPerTabBindings();\n\t\tconst adapter = this.buildAdapter(tabs, globalBindings, perTabBindings);\n\t\treturn this.handle(adapter, dialog);\n\t}\n\n\tprivate makeSelectTheme(): WrappingSelectTheme {\n\t\tconst t = this.theme;\n\t\treturn {\n\t\t\tselectedText: (s) => t.fg(\"accent\", t.bold(s)),\n\t\t\tdescription: (s) => t.fg(\"muted\", s),\n\t\t\tscrollInfo: (s) => t.fg(\"dim\", s),\n\t\t};\n\t}\n\n\tprivate buildTabComponents(): ReadonlyArray<TabComponents> {\n\t\treturn this.questions.map((q, i) => this.buildTabFor(q, i));\n\t}\n\n\tprivate buildTabFor(question: QuestionData, index: number): TabComponents {\n\t\tconst optionList = new OptionListView({\n\t\t\titems: this.itemsByTab[index] ?? [],\n\t\t\ttheme: this.selectTheme,\n\t\t});\n\t\tconst previewBlock = new PreviewBlockRenderer({\n\t\t\tquestion,\n\t\t\ttheme: this.theme,\n\t\t\tmarkdownTheme: this.markdownTheme,\n\t\t});\n\t\tconst preview = new PreviewPane({\n\t\t\tquestion,\n\t\t\tgetTerminalWidth: this.getTerminalWidth,\n\t\t\toptionListView: optionList,\n\t\t\tpreviewBlock,\n\t\t});\n\t\tconst multiSelect = question.multiSelect ? new MultiSelectView(this.theme, question) : undefined;\n\t\tconst bodyHeights = this.buildBodyHeights(question, preview, multiSelect);\n\t\treturn { optionList, preview, multiSelect, bodyHeights };\n\t}\n\n\tprivate buildBodyHeights(\n\t\tquestion: QuestionData,\n\t\tpreview: PreviewPane,\n\t\tmultiSelect: MultiSelectView | undefined,\n\t): (width: number) => TabBodyHeights {\n\t\treturn question.multiSelect ? multiSelectBodyHeights(multiSelect!) : previewBodyHeights(preview);\n\t}\n\n\t/**\n\t * Compute cross-tab max adaptive left width and inject into each PreviewPane.\n\t * Mirrors buildHeightComputers pattern — iterates all tabs, takes max.\n\t * Called after buildTabComponents, before buildDialog (so setGlobalLeftWidth\n\t * is set before any rendering occurs).\n\t */\n\tprivate injectGlobalLeftWidth(tabs: ReadonlyArray<TabComponents>): void {\n\t\tconst questions = this.questions;\n\t\tconst itemsByTab = this.itemsByTab;\n\t\tconst tabsDescriptor = questions.map((q) => ({ multiSelect: q.multiSelect }));\n\t\tconst globalLeftWidth = (paneWidth: number): number =>\n\t\t\tcrossTabLeftWidthWithDonation(tabsDescriptor, itemsByTab, questions, paneWidth);\n\t\tfor (const tab of tabs) {\n\t\t\ttab.preview.setGlobalLeftWidth(globalLeftWidth);\n\t\t}\n\t}\n\n\tprivate buildSubmitPicker(): SubmitPicker | undefined {\n\t\treturn this.isMulti ? new SubmitPicker(this.theme) : undefined;\n\t}\n\n\tprivate buildTabBar(): TabBar | undefined {\n\t\treturn this.isMulti ? new TabBar(this.theme) : undefined;\n\t}\n\n\tprivate buildHeightComputers(tabs: ReadonlyArray<TabComponents>): HeightComputers {\n\t\tconst global = (width: number): number => {\n\t\t\tlet max = 0;\n\t\t\tfor (const tab of tabs) {\n\t\t\t\tconst h = tab.bodyHeights(width).max;\n\t\t\t\tif (h > max) max = h;\n\t\t\t}\n\t\t\treturn Math.max(1, max);\n\t\t};\n\t\tconst current = (width: number): number => {\n\t\t\tconst idx = Math.min(this.getCurrentTab(), tabs.length - 1);\n\t\t\treturn Math.max(0, tabs[idx]?.bodyHeights(width).current ?? 0);\n\t\t};\n\t\treturn { global, current };\n\t}\n\n\tprivate pickInitialActivePreview(tabs: ReadonlyArray<TabComponents>): StatefulView<PreviewPaneProps> {\n\t\tconst idx = selectActivePreviewPaneIndex(this.initialState.currentTab, this.questions.length);\n\t\treturn tabs[idx]?.preview ?? tabs[0]!.preview;\n\t}\n\n\tprivate buildDialog(\n\t\ttabs: ReadonlyArray<TabComponents>,\n\t\tsubmitPicker: SubmitPicker | undefined,\n\t\ttabBar: TabBar | undefined,\n\t\theights: HeightComputers,\n\t): DialogView {\n\t\treturn new DialogView(\n\t\t\t{\n\t\t\t\ttheme: this.theme,\n\t\t\t\tquestions: this.questions,\n\t\t\t\ttabBar,\n\t\t\t\tnotesInput: this.notesInput,\n\t\t\t\tchatRow: this.chatRow,\n\t\t\t\tisMulti: this.isMulti,\n\t\t\t\ttabsByIndex: tabs,\n\t\t\t\tsubmitPicker,\n\t\t\t\tgetBodyHeight: heights.global,\n\t\t\t\tgetCurrentBodyHeight: heights.current,\n\t\t\t},\n\t\t\t{ state: this.initialState, activePreviewPane: this.pickInitialActivePreview(tabs) },\n\t\t);\n\t}\n\n\tprivate buildGlobalBindings(\n\t\tdialog: DialogView,\n\t\tsubmitPicker: SubmitPicker | undefined,\n\t\ttabBar: TabBar | undefined,\n\t): ReadonlyArray<BoundGlobalBinding> {\n\t\treturn [\n\t\t\tglobalBinding({ component: dialog, select: selectDialogProps }),\n\t\t\tglobalBinding({ component: this.chatRow, select: selectChatRowProps }),\n\t\t\t...(submitPicker ? [globalBinding({ component: submitPicker, select: selectSubmitPickerProps })] : []),\n\t\t\t...(tabBar ? [globalBinding({ component: tabBar, select: selectTabBarProps })] : []),\n\t\t];\n\t}\n\n\tprivate buildPerTabBindings(): ReadonlyArray<BoundPerTabBinding> {\n\t\treturn [\n\t\t\tperTabBinding({\n\t\t\t\tresolve: (tab) => tab.optionList,\n\t\t\t\tpredicate: isActiveTab,\n\t\t\t\tselect: selectOptionListProps,\n\t\t\t}),\n\t\t\tperTabBinding({\n\t\t\t\tresolve: (tab) => tab.preview,\n\t\t\t\tpredicate: isActiveTab,\n\t\t\t\tselect: selectPreviewPaneProps,\n\t\t\t}),\n\t\t\tperTabBinding({\n\t\t\t\tresolve: (tab) => tab.multiSelect,\n\t\t\t\tselect: selectMultiSelectProps,\n\t\t\t}),\n\t\t];\n\t}\n\n\tprivate buildAdapter(\n\t\ttabs: ReadonlyArray<TabComponents>,\n\t\tglobalBindings: ReadonlyArray<BoundGlobalBinding>,\n\t\tperTabBindings: ReadonlyArray<BoundPerTabBinding>,\n\t): QuestionnairePropsAdapter {\n\t\treturn new QuestionnairePropsAdapter({\n\t\t\ttui: this.tui,\n\t\t\tquestions: this.questions,\n\t\t\titemsByTab: this.itemsByTab,\n\t\t\ttabsByIndex: tabs,\n\t\t\tinlineInput: this.inlineInput,\n\t\t\tglobalBindings,\n\t\t\tperTabBindings,\n\t\t\textraInvalidatables: [this.notesInput],\n\t\t});\n\t}\n\n\tprivate handle(adapter: QuestionnairePropsAdapter, dialog: DialogView): QuestionnaireBuilt {\n\t\treturn {\n\t\t\tadapter,\n\t\t\tnotesInput: this.notesInput,\n\t\t\tinlineInput: this.inlineInput,\n\t\t\trender: (w) => dialog.render(w),\n\t\t\tinvalidate: () => adapter.invalidate(),\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"build-questionnaire.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/build-questionnaire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,8CAA8C,CAAC;AAC5F,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgBrD,OAAO,KAAK,EAAE,kBAAkB,EAAuB,MAAM,uCAAuC,CAAC;AAErG,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAerE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,wBAAwB;IACxC,GAAG,EAAE;QAAE,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,aAAa,IAAI,IAAI,CAAA;KAAE,CAAC;IAC9D,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IACzD,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,kBAAkB,CAAC;IACjC,aAAa,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,yBAAyB,CAAC;IACnC,UAAU,EAAE,KAAK,CAAC;IAClB,WAAW,EAAE,KAAK,CAAC;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,IAAI,CAAC;CACvB;AAwBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,kBAAkB,CAEvF"}
@@ -1 +1 @@
1
- {"version":3,"file":"inline-input.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/inline-input.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvE;;;;;;;;;;;;GAYG;AAEH,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAEtG;AAED,mGAAmG;AACnG,wBAAgB,eAAe,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAEtG;AAED,6FAA6F;AAC7F,wBAAgB,eAAe,CAC9B,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACX,kBAAkB,CAapB;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAUlG","sourcesContent":["import { ROW_INTENT_META } from \"./row-intent.ts\";\nimport type { InlineInputOwner, QuestionnaireState } from \"./state.ts\";\n\n/**\n * Shared owner-branching accessors/mutator for the two per-owner inline-input\n * map pairs on `QuestionnaireState`:\n * - `\"chat\"` → `chatDraftByTab` / `chatCaretByTab`\n * - `\"other\"` → `customDraftByTab` / `customCaretByTab`\n *\n * Both the pure reducer (`state-reducer.ts`) and the runtime\n * (`questionnaire-session.ts`) read and write these maps. Hoisting the helpers\n * here keeps the `owner === \"chat\"` branch in exactly one place instead of\n * copying it into each caller (previously `withInlineDraft` was duplicated\n * verbatim and the accessor pairs were near-duplicates split across the two\n * files).\n */\n\n/** Raw draft text persisted for `owner` at the current tab, or undefined when none is stored. */\nexport function readInlineDraft(state: QuestionnaireState, owner: InlineInputOwner): string | undefined {\n\treturn owner === \"chat\" ? state.chatDraftByTab.get(state.currentTab) : state.customDraftByTab.get(state.currentTab);\n}\n\n/** Raw caret offset persisted for `owner` at the current tab, or undefined when none is stored. */\nexport function readInlineCaret(state: QuestionnaireState, owner: InlineInputOwner): number | undefined {\n\treturn owner === \"chat\" ? state.chatCaretByTab.get(state.currentTab) : state.customCaretByTab.get(state.currentTab);\n}\n\n/** Immutably persist `value`/`caret` into `owner`'s draft+caret maps for the current tab. */\nexport function withInlineDraft(\n\tstate: QuestionnaireState,\n\towner: InlineInputOwner,\n\tvalue: string,\n\tcaret: number,\n): QuestionnaireState {\n\tif (owner === \"chat\") {\n\t\tconst chatDraftByTab = new Map(state.chatDraftByTab);\n\t\tconst chatCaretByTab = new Map(state.chatCaretByTab);\n\t\tchatDraftByTab.set(state.currentTab, value);\n\t\tchatCaretByTab.set(state.currentTab, caret);\n\t\treturn { ...state, chatDraftByTab, chatCaretByTab };\n\t}\n\tconst customDraftByTab = new Map(state.customDraftByTab);\n\tconst customCaretByTab = new Map(state.customCaretByTab);\n\tcustomDraftByTab.set(state.currentTab, value);\n\tcustomCaretByTab.set(state.currentTab, caret);\n\treturn { ...state, customDraftByTab, customCaretByTab };\n}\n\n/**\n * Draft text used to hydrate the inline editor when (re)focusing `owner`: the\n * in-flight draft when present, otherwise the prior committed answer for that\n * owner. The `\"chat\"` owner excludes the reserved sentinel label so a prior\n * signal-only chat (`\"Chat about this\"`) does not re-hydrate as editable text.\n */\nexport function resolveInlineDraftValue(state: QuestionnaireState, owner: InlineInputOwner): string {\n\tconst draft = readInlineDraft(state, owner);\n\tif (draft !== undefined) return draft;\n\tconst prior = state.answers.get(state.currentTab);\n\tif (owner === \"other\") {\n\t\treturn prior?.kind === \"custom\" && typeof prior.answer === \"string\" ? prior.answer : \"\";\n\t}\n\treturn prior?.kind === \"chat\" && typeof prior.answer === \"string\" && prior.answer !== ROW_INTENT_META.chat.label\n\t\t? prior.answer\n\t\t: \"\";\n}\n"]}
1
+ {"version":3,"file":"inline-input.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/inline-input.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvE;;;;;;;;;;;;GAYG;AAEH,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAEtG;AAED,mGAAmG;AACnG,wBAAgB,eAAe,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAEtG;AAED,6FAA6F;AAC7F,wBAAgB,eAAe,CAC9B,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACX,kBAAkB,CAapB;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAUlG"}
@@ -1 +1 @@
1
- {"version":3,"file":"key-router.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/key-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAU3E,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE;AACxB;;;GAGG;GACD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEtB,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7C;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAM7F;AAgHD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAsIpH","sourcesContent":["import { Key, matchesKey } from \"@earendil-works/pi-tui\";\nimport type { QuestionAnswer } from \"../tool/types.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport type { QuestionnaireRuntime, QuestionnaireState } from \"./state.ts\";\n\nconst KEYBIND_UP = \"tui.select.up\";\nconst KEYBIND_DOWN = \"tui.select.down\";\nconst KEYBIND_CONFIRM = \"tui.select.confirm\";\nconst KEYBIND_CANCEL = \"tui.select.cancel\";\n\nconst NOTES_ACTIVATE_KEY = \"n\";\nconst SPACE_KEY = \" \";\n\nexport type QuestionnaireAction =\n\t| { kind: \"nav\"; nextIndex: number }\n\t| { kind: \"tab_switch\"; nextTab: number }\n\t| { kind: \"confirm\"; answer: QuestionAnswer; autoAdvanceTab?: number }\n\t| { kind: \"toggle\"; index: number }\n\t| { kind: \"multi_confirm\"; selected: string[]; autoAdvanceTab?: number }\n\t| { kind: \"cancel\" }\n\t| { kind: \"notes_enter\" }\n\t| { kind: \"notes_exit\" }\n\t| { kind: \"submit\" }\n\t| { kind: \"submit_nav\"; nextIndex: 0 | 1 }\n\t| { kind: \"focus_chat\" }\n\t/**\n\t * Carries the target index so UP/DOWN form a continuous cycle through\n\t * `[chat, option0, …, optionLast]`.\n\t */\n\t| { kind: \"focus_options\"; optionIndex: number }\n\t| { kind: \"notes_forward\"; data: string }\n\t| { kind: \"ignore\" };\n\nexport interface QuestionnaireKeybindings {\n\tmatches(data: string, name: string): boolean;\n}\n\nexport function wrapTab(index: number, total: number): number {\n\tif (total <= 0) return 0;\n\treturn ((index % total) + total) % total;\n}\n\nexport function allAnswered(state: QuestionnaireState, runtime: QuestionnaireRuntime): boolean {\n\tif (runtime.questions.length === 0) return false;\n\tfor (let i = 0; i < runtime.questions.length; i++) {\n\t\tif (!state.answers.has(i)) return false;\n\t}\n\treturn true;\n}\n\nfunction totalTabs(runtime: QuestionnaireRuntime): number {\n\treturn runtime.isMulti ? runtime.questions.length + 1 : 1;\n}\n\nfunction computeAutoAdvanceTab(state: QuestionnaireState, runtime: QuestionnaireRuntime): number | undefined {\n\tif (!runtime.isMulti) return undefined;\n\tif (state.currentTab < runtime.questions.length - 1) return state.currentTab + 1;\n\treturn runtime.questions.length;\n}\n\n/**\n * Resolve the chat answer text at confirm time. Reads the LIVE `runtime.inputBuffer`\n * rather than a persisted draft because confirm only reaches here while chat is the\n * focused row and therefore owns the live inline editor (`inlineInputOwner === \"chat\"`),\n * so the in-flight buffer is authoritative. The blank-vs-typed decision uses `trim()`,\n * but the returned text is left UNTRIMMED to preserve the user's literal message; a\n * blank/whitespace buffer falls back to the reserved sentinel label (signal-only chat).\n */\nfunction chatAnswerValue(state: QuestionnaireState, runtime: QuestionnaireRuntime, sentinelLabel: string): string {\n\tif (state.inlineInputOwner !== \"chat\") return sentinelLabel;\n\treturn runtime.inputBuffer.trim().length > 0 ? runtime.inputBuffer : sentinelLabel;\n}\n\nfunction buildSingleSelectAnswer(state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionAnswer | null {\n\tconst q = runtime.questions[state.currentTab];\n\tif (!q) return null;\n\n\t// Chat sentinel takes priority over inputMode: when chatFocused=true, the host overrides\n\t// currentItem() to return the chat sentinel even though inputMode is true for the shared\n\t// inline editor. Non-blank chat drafts become the chat answer; blank drafts preserve the\n\t// legacy sentinel-only \"Chat about this\" answer.\n\tconst item = runtime.currentItem;\n\tif (item?.kind === \"chat\") {\n\t\treturn {\n\t\t\tquestionIndex: state.currentTab,\n\t\t\tquestion: q.question,\n\t\t\tkind: \"chat\",\n\t\t\tanswer: chatAnswerValue(state, runtime, item.label),\n\t\t};\n\t}\n\n\tif (state.inputMode && state.inlineInputOwner === \"other\") {\n\t\tconst label = runtime.inputBuffer;\n\t\treturn {\n\t\t\tquestionIndex: state.currentTab,\n\t\t\tquestion: q.question,\n\t\t\tkind: \"custom\",\n\t\t\tanswer: label.length > 0 ? label : null,\n\t\t};\n\t}\n\tif (!item) return null;\n\tif (item.kind === \"other\") {\n\t\treturn null;\n\t}\n\tif (item.kind === \"next\") {\n\t\treturn null;\n\t}\n\treturn {\n\t\tquestionIndex: state.currentTab,\n\t\tquestion: q.question,\n\t\tkind: \"option\",\n\t\tanswer: item.label,\n\t};\n}\n\nfunction buildMultiSelected(state: QuestionnaireState, runtime: QuestionnaireRuntime): string[] {\n\tconst q = runtime.questions[state.currentTab];\n\tif (!q) return [];\n\tconst out: string[] = [];\n\tfor (let i = 0; i < q.options.length; i++) {\n\t\tif (state.multiSelectChecked.has(i)) {\n\t\t\tconst label = q.options[i]?.label;\n\t\t\tif (typeof label === \"string\") out.push(label);\n\t\t}\n\t}\n\treturn out;\n}\n\nfunction tabSwitchAction(\n\tdata: string,\n\tstate: QuestionnaireState,\n\truntime: QuestionnaireRuntime,\n): QuestionnaireAction | null {\n\tif (!runtime.isMulti) return null;\n\tconst total = totalTabs(runtime);\n\tif (matchesKey(data, Key.tab) || matchesKey(data, Key.right)) {\n\t\treturn { kind: \"tab_switch\", nextTab: wrapTab(state.currentTab + 1, total) };\n\t}\n\tif (matchesKey(data, Key.shift(\"tab\")) || matchesKey(data, Key.left)) {\n\t\treturn { kind: \"tab_switch\", nextTab: wrapTab(state.currentTab - 1, total) };\n\t}\n\treturn null;\n}\n\n// DOWN at the last item emits focus_chat so the cycle [chat, option0, …, optionLast] wraps.\nfunction nextNavOnDown(state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionnaireAction {\n\tif (runtime.items.length > 0 && state.optionIndex === runtime.items.length - 1) {\n\t\treturn { kind: \"focus_chat\" };\n\t}\n\treturn { kind: \"nav\", nextIndex: wrapTab(state.optionIndex + 1, Math.max(1, runtime.items.length)) };\n}\n\n// UP at the top item emits focus_chat (symmetric with nextNavOnDown).\nfunction prevNavOnUp(state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionnaireAction {\n\tif (runtime.items.length > 0 && state.optionIndex === 0) {\n\t\treturn { kind: \"focus_chat\" };\n\t}\n\treturn { kind: \"nav\", nextIndex: wrapTab(state.optionIndex - 1, Math.max(1, runtime.items.length)) };\n}\n\nexport function routeKey(data: string, state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionnaireAction {\n\tconst kb = runtime.keybindings;\n\n\tif (state.notesVisible) {\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"notes_exit\" };\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) return { kind: \"notes_exit\" };\n\t\treturn { kind: \"notes_forward\", data };\n\t}\n\n\tif (state.chatFocused) {\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\tconst answer = buildSingleSelectAnswer(state, runtime);\n\t\t\tif (!answer) return { kind: \"ignore\" };\n\t\t\treturn { kind: \"confirm\", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };\n\t\t}\n\t\t// Continuous cycle: UP from chat → bottom of options (last navigable row), DOWN from\n\t\t// chat → top of options (option 0). Symmetric with UP-at-top → focus_chat and\n\t\t// DOWN-at-bottom → focus_chat below; together they form one wrapping cycle through\n\t\t// `[chat, option0, …, optionLast]`.\n\t\tif (kb.matches(data, KEYBIND_UP)) {\n\t\t\tconst last = Math.max(0, runtime.items.length - 1);\n\t\t\treturn { kind: \"focus_options\", optionIndex: last };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_DOWN)) {\n\t\t\treturn { kind: \"focus_options\", optionIndex: 0 };\n\t\t}\n\t\t// Left/Right guard is intentionally asymmetric — it exists for the chat owner but\n\t\t// NOT the \"other\" inline editor. The chatFocused branch falls through to\n\t\t// `tabSwitchAction` below, which in multi-question mode consumes Left/Right for tab\n\t\t// switching; swallowing them here keeps Left/Right as in-editor caret movement for\n\t\t// the chat input. The \"other\" inline editor flows through the separate\n\t\t// `state.inputMode` branch (further down), which never calls `tabSwitchAction`, so it\n\t\t// needs no equivalent guard. Do not \"simplify\" by unifying the two paths.\n\t\tif (\n\t\t\tstate.inputMode &&\n\t\t\tstate.inlineInputOwner === \"chat\" &&\n\t\t\t(matchesKey(data, Key.left) || matchesKey(data, Key.right))\n\t\t) {\n\t\t\treturn { kind: \"ignore\" };\n\t\t}\n\t\tconst tab = tabSwitchAction(data, state, runtime);\n\t\tif (tab) return tab;\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tif (state.inputMode) {\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\tconst answer = buildSingleSelectAnswer(state, runtime);\n\t\t\tif (!answer) return { kind: \"ignore\" };\n\t\t\treturn { kind: \"confirm\", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\tif (kb.matches(data, KEYBIND_UP)) {\n\t\t\treturn prevNavOnUp(state, runtime);\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_DOWN)) {\n\t\t\treturn nextNavOnDown(state, runtime);\n\t\t}\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tif (runtime.isMulti && state.currentTab === runtime.questions.length) {\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\tconst tab = tabSwitchAction(data, state, runtime);\n\t\tif (tab) return tab;\n\t\tif (kb.matches(data, KEYBIND_UP) || kb.matches(data, KEYBIND_DOWN)) {\n\t\t\tconst delta = kb.matches(data, KEYBIND_DOWN) ? 1 : -1;\n\t\t\tconst next = wrapTab(state.submitChoiceIndex + delta, 2);\n\t\t\treturn { kind: \"submit_nav\", nextIndex: (next === 1 ? 1 : 0) as 0 | 1 };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\t// D1 (revised): Submit always submits; Cancel always cancels. The warning header\n\t\t\t// is informational only — `allAnswered(state)` no longer gates submission. Partial\n\t\t\t// answers flow through `orderedAnswers()` in the host.\n\t\t\treturn state.submitChoiceIndex === 1 ? { kind: \"cancel\" } : { kind: \"submit\" };\n\t\t}\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tconst tab = tabSwitchAction(data, state, runtime);\n\tif (tab) return tab;\n\n\tconst q = runtime.questions[state.currentTab];\n\tif (!q) return { kind: \"ignore\" };\n\n\tif (data === NOTES_ACTIVATE_KEY && !q.multiSelect && state.focusedOptionHasPreview) {\n\t\treturn { kind: \"notes_enter\" };\n\t}\n\n\tif (kb.matches(data, KEYBIND_UP)) {\n\t\treturn prevNavOnUp(state, runtime);\n\t}\n\tif (kb.matches(data, KEYBIND_DOWN)) {\n\t\treturn nextNavOnDown(state, runtime);\n\t}\n\n\tif (q.multiSelect) {\n\t\tconst focusedKind = runtime.currentItem?.kind;\n\t\tconst focusedMeta = focusedKind ? ROW_INTENT_META[focusedKind] : undefined;\n\t\t// Space toggles the focused row's checkbox. Suppressed on rows whose META declares\n\t\t// `blocksMultiToggle` (the Next sentinel) — Next is not a real option and has no\n\t\t// checked/unchecked state.\n\t\tif (data === SPACE_KEY) {\n\t\t\tif (focusedMeta?.blocksMultiToggle) return { kind: \"ignore\" };\n\t\t\treturn { kind: \"toggle\", index: state.optionIndex };\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\t\t// Enter on a regular row toggles (matching Space) — committing the question is now\n\t\t\t// gated behind explicit focus on a row whose META declares `autoSubmitsInMulti`\n\t\t\t// (the Next sentinel), so Enter on options is a no-cost way to flip checkboxes\n\t\t\t// without leaving the keyboard home row.\n\t\t\tif (!focusedMeta?.autoSubmitsInMulti) return { kind: \"toggle\", index: state.optionIndex };\n\t\t\t// Enter on Next: carry autoAdvanceTab so the host can advance to the next tab in\n\t\t\t// multi-question mode, OR submit the dialog in single-question mode\n\t\t\t// (autoAdvanceTab === undefined when !isMulti). Without this, a single multi-select\n\t\t\t// question would have no way to commit at all.\n\t\t\treturn {\n\t\t\t\tkind: \"multi_confirm\",\n\t\t\t\tselected: buildMultiSelected(state, runtime),\n\t\t\t\tautoAdvanceTab: computeAutoAdvanceTab(state, runtime),\n\t\t\t};\n\t\t}\n\t\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\t\treturn { kind: \"ignore\" };\n\t}\n\n\tif (kb.matches(data, KEYBIND_CONFIRM)) {\n\t\tconst answer = buildSingleSelectAnswer(state, runtime);\n\t\tif (!answer) return { kind: \"ignore\" };\n\t\treturn { kind: \"confirm\", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };\n\t}\n\tif (kb.matches(data, KEYBIND_CANCEL)) return { kind: \"cancel\" };\n\treturn { kind: \"ignore\" };\n}\n"]}
1
+ {"version":3,"file":"key-router.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/key-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAU3E,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE;AACxB;;;GAGG;GACD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEtB,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7C;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAM7F;AAgHD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAsIpH"}
@@ -1 +1 @@
1
- {"version":3,"file":"questionnaire-session.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/questionnaire-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8CAA8C,CAAC;AAE1E,OAAO,KAAK,EAAgB,mBAAmB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAuBhF,MAAM,WAAW,0BAA0B;IAC1C,GAAG,EAAE;QAAE,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,aAAa,IAAI,IAAI,CAAA;KAAE,CAAC;IAC9D,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,EAAE,kBAAkB,EAAE,EAAE,CAAC;IACnC,IAAI,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,6BAA6B;IAC7C,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAChC,UAAU,IAAI,IAAI,CAAC;IACnB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAuBD;;;;;GAKG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,KAAK,CAAsC;IAEnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IAEpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4B;IAExD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoC;IACxD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqC;IAE1D,QAAQ,CAAC,SAAS,EAAE,6BAA6B,CAAC;IAElD,YAAY,MAAM,EAAE,0BAA0B,EA8B7C;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAO3B;IAED,OAAO,CAAC,MAAM;IASd,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,SAAS;IAqBjB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,WAAW;CAMnB","sourcesContent":["import type { Theme } from \"../../../../modes/interactive/theme/theme.ts\";\nimport { getKeybindings, type Input } from \"@earendil-works/pi-tui\";\nimport type { QuestionData, QuestionnaireResult, QuestionParams } from \"../tool/types.ts\";\nimport type { WrappingSelectItem } from \"../view/components/wrapping-select.ts\";\nimport type { QuestionnairePropsAdapter } from \"../view/props-adapter.ts\";\nimport { buildQuestionnaire } from \"./build-questionnaire.ts\";\nimport { readInlineCaret, readInlineDraft, withInlineDraft } from \"./inline-input.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport { type QuestionnaireAction, routeKey } from \"./key-router.ts\";\nimport { computeFocusedOptionHasPreview } from \"./selectors/derivations.ts\";\nimport type { QuestionnaireRuntime, QuestionnaireState } from \"./state.ts\";\nimport { type ApplyContext, type Effect, reduce } from \"./state-reducer.ts\";\n\nfunction readInputCursor(input: Input): number {\n\tconst value = input.getValue();\n\tconst raw = Reflect.get(input, \"cursor\");\n\treturn typeof raw === \"number\" && Number.isFinite(raw)\n\t\t? Math.max(0, Math.min(raw, value.length))\n\t\t: value.length;\n}\n\nfunction writeInputCursor(input: Input, cursor: number): void {\n\tconst value = input.getValue();\n\tReflect.set(input, \"cursor\", Math.max(0, Math.min(cursor, value.length)));\n}\n\nexport interface QuestionnaireSessionConfig {\n\ttui: { terminal: { columns: number }; requestRender(): void };\n\ttheme: Theme;\n\tparams: QuestionParams;\n\titemsByTab: WrappingSelectItem[][];\n\tdone: (result: QuestionnaireResult) => void;\n}\n\nexport interface QuestionnaireSessionComponent {\n\trender(width: number): string[];\n\tinvalidate(): void;\n\thandleInput(data: string): void;\n}\n\nfunction initialState(): QuestionnaireState {\n\treturn {\n\t\tcurrentTab: 0,\n\t\toptionIndex: 0,\n\t\tinputMode: false,\n\t\tinlineInputOwner: null,\n\t\tnotesVisible: false,\n\t\tchatFocused: false,\n\t\tanswers: new Map(),\n\t\tmultiSelectChecked: new Set(),\n\t\tnotesByTab: new Map(),\n\t\tfocusedOptionHasPreview: false,\n\t\tsubmitChoiceIndex: 0,\n\t\tnotesDraft: \"\",\n\t\tcustomDraftByTab: new Map(),\n\t\tcustomCaretByTab: new Map(),\n\t\tchatDraftByTab: new Map(),\n\t\tchatCaretByTab: new Map(),\n\t};\n}\n\n/**\n * Slim runtime: owns the canonical state cell, the input-buffer cell, the\n * two-pass `notesVisible` dispatch loop, and the effect runner. State\n * transitions go through the pure `reduce` reducer; UI fan-out goes through\n * the `QuestionnairePropsAdapter` produced by `buildQuestionnaire`.\n */\nexport class QuestionnaireSession {\n\tprivate state: QuestionnaireState = initialState();\n\n\tprivate readonly questions: readonly QuestionData[];\n\tprivate readonly isMulti: boolean;\n\tprivate readonly itemsByTab: WrappingSelectItem[][];\n\n\tprivate readonly notesInput: Input;\n\tprivate readonly inlineInput: Input;\n\tprivate readonly viewAdapter: QuestionnairePropsAdapter;\n\n\tprivate readonly tui: QuestionnaireSessionConfig[\"tui\"];\n\tprivate readonly done: QuestionnaireSessionConfig[\"done\"];\n\n\treadonly component: QuestionnaireSessionComponent;\n\n\tconstructor(config: QuestionnaireSessionConfig) {\n\t\tthis.tui = config.tui;\n\t\tthis.done = config.done;\n\t\tthis.questions = config.params.questions;\n\t\tthis.isMulti = this.questions.length > 1;\n\t\tthis.itemsByTab = config.itemsByTab;\n\t\t// Seed from the focused option at start; the reducer keeps it in sync via withFocusedOptionHasPreview.\n\t\tthis.state = { ...this.state, focusedOptionHasPreview: computeFocusedOptionHasPreview(this.questions, 0, 0) };\n\n\t\tconst built = buildQuestionnaire({\n\t\t\ttui: this.tui,\n\t\t\ttheme: config.theme,\n\t\t\tquestions: this.questions,\n\t\t\titemsByTab: this.itemsByTab,\n\t\t\tisMulti: this.isMulti,\n\t\t\tinitialState: this.state,\n\t\t\tgetCurrentTab: () => this.state.currentTab,\n\t\t});\n\n\t\tthis.notesInput = built.notesInput;\n\t\tthis.inlineInput = built.inlineInput;\n\t\tthis.viewAdapter = built.adapter;\n\n\t\tthis.component = {\n\t\t\trender: built.render,\n\t\t\tinvalidate: built.invalidate,\n\t\t\thandleInput: (data) => this.dispatch(data),\n\t\t};\n\n\t\tthis.viewAdapter.apply(this.state);\n\t}\n\n\tdispatch(data: string): void {\n\t\tconst action = routeKey(data, this.state, this.runtime());\n\t\tif (action.kind === \"ignore\") {\n\t\t\tthis.handleIgnoreInline(data);\n\t\t\treturn;\n\t\t}\n\t\tthis.commit(action);\n\t}\n\n\tprivate commit(action: QuestionnaireAction): void {\n\t\tthis.state = this.mirrorInlineInputDraft(this.state);\n\t\tconst result = reduce(this.state, action, this.applyContext());\n\t\tthis.state = result.state;\n\t\tfor (const effect of result.effects) this.runEffect(effect);\n\t\tthis.state = this.mirrorNotesDraft(this.state);\n\t\tthis.viewAdapter.apply(this.state);\n\t}\n\n\tprivate mirrorNotesDraft(s: QuestionnaireState): QuestionnaireState {\n\t\tconst draft = this.notesInput.getValue();\n\t\treturn s.notesDraft === draft ? s : { ...s, notesDraft: draft };\n\t}\n\n\tprivate mirrorInlineInputDraft(s: QuestionnaireState): QuestionnaireState {\n\t\tif (!s.inputMode || !s.inlineInputOwner) return s;\n\t\tconst owner = s.inlineInputOwner;\n\t\tconst value = this.inlineInput.getValue();\n\t\tconst caret = readInputCursor(this.inlineInput);\n\t\tif (readInlineDraft(s, owner) === value && readInlineCaret(s, owner) === caret) {\n\t\t\treturn s;\n\t\t}\n\t\treturn withInlineDraft(s, owner, value, caret);\n\t}\n\n\tprivate runEffect(effect: Effect): void {\n\t\tswitch (effect.kind) {\n\t\t\tcase \"set_input_buffer\":\n\t\t\t\tthis.inlineInput.setValue(effect.value);\n\t\t\t\twriteInputCursor(this.inlineInput, effect.caret);\n\t\t\t\treturn;\n\t\t\tcase \"set_notes_value\":\n\t\t\t\tthis.notesInput.setValue(effect.value);\n\t\t\t\treturn;\n\t\t\tcase \"set_notes_focused\":\n\t\t\t\tthis.notesInput.focused = effect.focused;\n\t\t\t\treturn;\n\t\t\tcase \"forward_notes_keystroke\":\n\t\t\t\tthis.notesInput.handleInput(effect.data);\n\t\t\t\treturn;\n\t\t\tcase \"done\":\n\t\t\t\tthis.done(effect.result);\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\t/**\n\t * Per-keystroke `ignore` fast path: delegates to the headless `inlineInput`\n\t * Input so bracketed-paste accumulator (`input.js:33-63`) and Kitty CSI-u\n\t * decode (`input.js:155-163`) take effect. Cursor is NOT force-reset here —\n\t * doing so would corrupt split-chunk pastes (a `\\x05` byte mid-paste lands\n\t * verbatim in `pasteBuffer` and survives `handlePaste`'s narrow strip).\n\t * Cursor advances naturally via `insertCharacter` on typing/paste; cursor-\n\t * movement keys (Left/Right/Home/End/word-jumps) are now functional, and\n\t * the live buffer/caret are mirrored into canonical state so rendering can\n\t * draw the same thick cursor at the editing caret. `viewAdapter.apply` is\n\t * called directly without a reducer round-trip — preserves the D3 fast-path\n\t * latency profile from Phase 11.\n\t */\n\tprivate handleIgnoreInline(data: string): void {\n\t\tif (!this.state.inputMode || !this.state.inlineInputOwner) return;\n\t\tthis.inlineInput.handleInput(data);\n\t\tthis.state = this.mirrorInlineInputDraft(this.state);\n\t\tthis.viewAdapter.apply(this.state);\n\t}\n\n\tprivate runtime(): QuestionnaireRuntime {\n\t\treturn {\n\t\t\tkeybindings: getKeybindings(),\n\t\t\tinputBuffer: this.inlineInput.getValue(),\n\t\t\tquestions: this.questions,\n\t\t\tisMulti: this.isMulti,\n\t\t\tcurrentItem: this.currentItem(),\n\t\t\titems: this.itemsByTab[this.state.currentTab] ?? [],\n\t\t};\n\t}\n\n\tprivate applyContext(): ApplyContext {\n\t\treturn {\n\t\t\tquestions: this.questions,\n\t\t\titemsByTab: this.itemsByTab,\n\t\t};\n\t}\n\n\tprivate currentItem(): WrappingSelectItem | undefined {\n\t\tif (this.state.chatFocused) return { kind: \"chat\", label: ROW_INTENT_META.chat.label };\n\t\tconst arr = this.itemsByTab[this.state.currentTab] ?? [];\n\t\tif (this.state.optionIndex < arr.length) return arr[this.state.optionIndex];\n\t\treturn { kind: \"chat\", label: ROW_INTENT_META.chat.label };\n\t}\n}\n"]}
1
+ {"version":3,"file":"questionnaire-session.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/questionnaire-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8CAA8C,CAAC;AAE1E,OAAO,KAAK,EAAgB,mBAAmB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAuBhF,MAAM,WAAW,0BAA0B;IAC1C,GAAG,EAAE;QAAE,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,aAAa,IAAI,IAAI,CAAA;KAAE,CAAC;IAC9D,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,EAAE,kBAAkB,EAAE,EAAE,CAAC;IACnC,IAAI,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,6BAA6B;IAC7C,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAChC,UAAU,IAAI,IAAI,CAAC;IACnB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAuBD;;;;;GAKG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,KAAK,CAAsC;IAEnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IAEpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4B;IAExD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoC;IACxD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqC;IAE1D,QAAQ,CAAC,SAAS,EAAE,6BAA6B,CAAC;IAElD,YAAY,MAAM,EAAE,0BAA0B,EA8B7C;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAO3B;IAED,OAAO,CAAC,MAAM;IASd,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,SAAS;IAqBjB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,WAAW;CAMnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"row-intent.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/row-intent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAEhF;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAEjD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AACtD,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAA8B,CAAC;AAEjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iCAAiC,EAAE,OAAO,CAAC;IAC3C,uBAAuB,EAAE,OAAO,CAAC;CACjC;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,OAAO,EAAE,aAAa,CA6C1D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE;IAAE,QAAQ,EAAE,CAAC,IAAI,YAAY,GAAG,MAAM;CAIlE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAGjD,CAAC;AAEH;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,GAAG,YAAY,EAAE,CAYhG","sourcesContent":["import type { QuestionData } from \"../tool/types.ts\";\nimport type { WrappingSelectItem } from \"../view/components/wrapping-select.ts\";\n\n/**\n * Row kind discriminator. Single source of truth — derived from the runtime\n * `WrappingSelectItem` union (`wrapping-select.ts:18-22`) so adding a new\n * variant there forces a `ROW_INTENT_META` entry here (compile-time\n * exhaustiveness via `Record<RowKind, …>`).\n */\nexport type RowKind = WrappingSelectItem[\"kind\"];\n\n/**\n * Sentinel kinds — the subset of `RowKind` representing protocol-driven rows\n * (vs author-defined `option` rows). The auto-append walker, the reserved-label\n * derivation, and `LABELS_BY_KIND` consumers iterate this list.\n */\nexport type SentinelKind = Exclude<RowKind, \"option\">;\nexport const SENTINEL_KINDS: readonly SentinelKind[] = [\"other\", \"chat\", \"next\"];\n\n/**\n * Per-kind static metadata. Pure data — no closures, no per-kind handler\n * functions. Behavior-bearing branches (answer construction in\n * `key-router.ts:69-99`, the Next-row render block in `multi-select-view.ts`,\n * and the inline-Input render branch in `wrapping-select.ts`) keep their\n * exhaustive switches and READ flags from this table.\n *\n * Adding a new sentinel:\n * 1. Add the variant to `WrappingSelectItem` (`wrapping-select.ts:18-22`).\n * 2. Add an entry here. Compile fails until both edits are present.\n * 3. (If user-facing) author wherever the row is synthesized — typically\n * `buildItemsForQuestion` for main-list residents, or the `chatList`\n * construction site for non-main-list rows.\n *\n * Field semantics:\n * - `label` — user-facing string. For `option` it's an empty placeholder\n * (per-instance labels come from `QuestionData.options[i].label`); every\n * sentinel uses its META entry as the single source of truth.\n * - `reserved` — author-facing labels matching this string trigger\n * `reserved_label` at validation time. `RESERVED_LABEL_SET` is derived.\n * - `livesInMainList` — true iff the row appears in `itemsByTab[i]`. Chat\n * lives in its own `chatList` (`questionnaire-session.ts:95`) so its row\n * never enters the main list.\n * - `numbered` — true iff the row contributes to the main-list numbering\n * offset that `chatNumberingFor` reads. Multi-select Next is the only\n * numberable=false row that lives in the list; chat is numbered=true but\n * `livesInMainList=false` so the flag is moot for chat.\n * - `activatesInputMode` — true iff focusing the row should toggle\n * `state.inputMode = true` and hydrate that row's shared inline-input\n * owner. Read by `state-reducer.ts` `nav` / `focus_options` /\n * `focus_chat` cases.\n * - `blocksMultiToggle` — in multiSelect mode, Space (and Enter-as-toggle)\n * on this row is suppressed. The Next sentinel is the only true.\n * - `autoSubmitsInMulti` — in multiSelect mode, Enter on this row commits\n * the question (emits `multi_confirm`). The Next sentinel is the only true.\n * - `autoAppendOnSingleSelectNoPreview` — `buildItemsForQuestion` appends\n * this row when the question is single-select AND no option carries a\n * `preview`. The \"other\" sentinel is the only true.\n * - `autoAppendOnMultiSelect` — `buildItemsForQuestion` appends this row\n * when the question is multi-select. The Next sentinel is the only true.\n */\nexport interface RowIntentMeta {\n\tlabel: string;\n\treserved: boolean;\n\tlivesInMainList: boolean;\n\tnumbered: boolean;\n\tactivatesInputMode: boolean;\n\tblocksMultiToggle: boolean;\n\tautoSubmitsInMulti: boolean;\n\tautoAppendOnSingleSelectNoPreview: boolean;\n\tautoAppendOnMultiSelect: boolean;\n}\n\nexport const ROW_INTENT_META: Record<RowKind, RowIntentMeta> = {\n\toption: {\n\t\tlabel: \"\",\n\t\treserved: false,\n\t\tlivesInMainList: true,\n\t\tnumbered: true,\n\t\tactivatesInputMode: false,\n\t\tblocksMultiToggle: false,\n\t\tautoSubmitsInMulti: false,\n\t\tautoAppendOnSingleSelectNoPreview: false,\n\t\tautoAppendOnMultiSelect: false,\n\t},\n\tother: {\n\t\tlabel: \"Type something.\",\n\t\treserved: true,\n\t\tlivesInMainList: true,\n\t\tnumbered: true,\n\t\tactivatesInputMode: true,\n\t\tblocksMultiToggle: false,\n\t\tautoSubmitsInMulti: false,\n\t\tautoAppendOnSingleSelectNoPreview: true,\n\t\tautoAppendOnMultiSelect: false,\n\t},\n\tchat: {\n\t\tlabel: \"Chat about this\",\n\t\treserved: true,\n\t\tlivesInMainList: false,\n\t\tnumbered: true,\n\t\tactivatesInputMode: true,\n\t\tblocksMultiToggle: false,\n\t\tautoSubmitsInMulti: false,\n\t\tautoAppendOnSingleSelectNoPreview: false,\n\t\tautoAppendOnMultiSelect: false,\n\t},\n\tnext: {\n\t\tlabel: \"Next\",\n\t\treserved: true,\n\t\tlivesInMainList: true,\n\t\tnumbered: false,\n\t\tactivatesInputMode: false,\n\t\tblocksMultiToggle: true,\n\t\tautoSubmitsInMulti: true,\n\t\tautoAppendOnSingleSelectNoPreview: false,\n\t\tautoAppendOnMultiSelect: true,\n\t},\n};\n\n/**\n * Kind-keyed label view. `option` is excluded — its label is per-instance,\n * not per-kind. `types.ts#SENTINEL_LABELS` re-sources from here.\n */\nexport const LABELS_BY_KIND: { readonly [K in SentinelKind]: string } = {\n\tother: ROW_INTENT_META.other.label,\n\tchat: ROW_INTENT_META.chat.label,\n\tnext: ROW_INTENT_META.next.label,\n};\n\n/**\n * Reserved-label set for runtime validation. Includes \"Other\" (a model-conditioned\n * label that has no runtime kind) plus every sentinel with `reserved: true`.\n */\nexport const RESERVED_LABEL_SET: ReadonlySet<string> = new Set<string>([\n\t\"Other\",\n\t...SENTINEL_KINDS.filter((k) => ROW_INTENT_META[k].reserved).map((k) => ROW_INTENT_META[k].label),\n]);\n\n/**\n * Walk the META table to synthesize sentinel rows for one question. The two\n * append predicates are mutually exclusive in practice (`multiSelect` vs\n * `!multiSelect && !hasAnyPreview`) but the walker doesn't enforce that —\n * adding a third bucket only requires a new META flag.\n *\n * Returns sentinel descriptors in declaration order of `SENTINEL_KINDS`. The\n * caller wraps each with the `WrappingSelectItem` shape (kind + label).\n */\nexport function sentinelsToAppend(question: QuestionData, hasAnyPreview: boolean): SentinelKind[] {\n\tconst out: SentinelKind[] = [];\n\tfor (const k of SENTINEL_KINDS) {\n\t\tconst meta = ROW_INTENT_META[k];\n\t\tif (!meta.livesInMainList) continue;\n\t\tif (question.multiSelect === true) {\n\t\t\tif (meta.autoAppendOnMultiSelect) out.push(k);\n\t\t} else {\n\t\t\tif (meta.autoAppendOnSingleSelectNoPreview && !hasAnyPreview) out.push(k);\n\t\t}\n\t}\n\treturn out;\n}\n"]}
1
+ {"version":3,"file":"row-intent.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/row-intent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAEhF;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAEjD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AACtD,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAA8B,CAAC;AAEjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iCAAiC,EAAE,OAAO,CAAC;IAC3C,uBAAuB,EAAE,OAAO,CAAC;CACjC;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,OAAO,EAAE,aAAa,CA6C1D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE;IAAE,QAAQ,EAAE,CAAC,IAAI,YAAY,GAAG,MAAM;CAIlE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAGjD,CAAC;AAEH;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,GAAG,YAAY,EAAE,CAYhG"}
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/state/selectors/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IAC5C,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IAClE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,iBAAiB,EAAE,YAAY,CAAC,gBAAgB,CAAC,CAAC;CAC3D;AAED,MAAM,WAAW,oBAAqB,SAAQ,cAAc;IAC3D,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;IAC5B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,cAAc,KAAK,CAAC,CAAC;AACtF,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,oBAAoB,KAAK,CAAC,CAAC","sourcesContent":["import type { QuestionData } from \"../../tool/types.ts\";\nimport type { PreviewPaneProps } from \"../../view/components/preview/preview-pane.ts\";\nimport type { WrappingSelectItem } from \"../../view/components/wrapping-select.ts\";\nimport type { ActiveView, StatefulView } from \"../../view/stateful-view.ts\";\nimport type { TabComponents } from \"../../view/tab-components.ts\";\nimport type { QuestionnaireState } from \"../state.ts\";\n\nexport interface BindingContext {\n\treadonly questions: readonly QuestionData[];\n\treadonly itemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n\treadonly totalQuestions: number;\n\treadonly activeView: ActiveView;\n\treadonly inputBuffer: string;\n\treadonly inputCaret: number;\n\treadonly chatInputBuffer: string;\n\treadonly chatInputCaret: number;\n\treadonly activePreviewPane: StatefulView<PreviewPaneProps>;\n}\n\nexport interface PerTabBindingContext extends BindingContext {\n\treadonly tab: TabComponents;\n\treadonly i: number;\n}\n\nexport type GlobalSelector<P> = (state: QuestionnaireState, ctx: BindingContext) => P;\nexport type PerTabSelector<P> = (state: QuestionnaireState, ctx: PerTabBindingContext) => P;\n"]}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/state/selectors/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IAC5C,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IAClE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,iBAAiB,EAAE,YAAY,CAAC,gBAAgB,CAAC,CAAC;CAC3D;AAED,MAAM,WAAW,oBAAqB,SAAQ,cAAc;IAC3D,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;IAC5B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,cAAc,KAAK,CAAC,CAAC;AACtF,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,oBAAoB,KAAK,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"derivations.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/state/selectors/derivations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAGnF;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC7C,SAAS,EAAE,SAAS,YAAY,EAAE,EAClC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GACjB,OAAO,CAKT;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,GAAG;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd,CAOA;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACvC,SAAS,EAAE,SAAS,YAAY,EAAE,EAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,EAC5C,KAAK,EAAE,SAAS,kBAAkB,EAAE,GAClC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAcvD;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAG/F;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,EACxD,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GACpB,SAAS,kBAAkB,EAAE,CAG/B","sourcesContent":["import type { QuestionAnswer, QuestionData } from \"../../tool/types.ts\";\nimport type { WrappingSelectItem } from \"../../view/components/wrapping-select.ts\";\nimport { ROW_INTENT_META } from \"../row-intent.ts\";\n\n/**\n * Pure derivation: does the option focused by `(currentTab, optionIndex)` carry a\n * non-empty `preview` string? Mode gates (chat focus, notes mode, multiSelect) layer\n * on top via dispatch branches; this predicate is intentionally mode-agnostic.\n */\nexport function computeFocusedOptionHasPreview(\n\tquestions: readonly QuestionData[],\n\tcurrentTab: number,\n\toptionIndex: number,\n): boolean {\n\tconst q = questions[currentTab];\n\tif (!q) return false;\n\tconst opt = q.options[optionIndex];\n\treturn !!opt && typeof opt.preview === \"string\" && opt.preview.length > 0;\n}\n\n/**\n * Numbering for the chat row's WrappingSelect, computed from the active tab's items.\n *\n * The chat row lives in its own one-item WrappingSelect; the host calls this on every tab\n * switch / selection update to keep the chat row's `N. ` label continuous with the visible\n * numbered rows of the active tab. The shape `{ offset, total }` mirrors\n * `WrappingSelect.setNumbering(numberStartOffset, totalItemsForNumbering)` directly.\n */\nexport function chatNumberingFor(items: readonly WrappingSelectItem[]): {\n\toffset: number;\n\ttotal: number;\n} {\n\t// Count only the visible-numbered rows. The Next sentinel renders without a number\n\t// (see MultiSelectView), so it must NOT advance the chat row's number — otherwise\n\t// chat reads as \"6.\" next to options labeled 1-4. Sourced from `ROW_INTENT_META[kind].numbered`\n\t// so adding a new non-numbered kind is a single META edit.\n\tconst count = items.filter((i) => ROW_INTENT_META[i.kind].numbered).length;\n\treturn { offset: count, total: count + 1 };\n}\n\n/**\n * Which row in the active tab should be marked as \"previously confirmed\"? Drives the\n * `WrappingSelect` confirmed-row indicator (label + ` ✔`) when the user navigates back\n * to a question they already answered. Returns `undefined` when no marker should be drawn —\n * multi-select handles its own `[✔]` boxes via `multiSelectChecked`, `kind: \"chat\"` ends the\n * dialog so the row can never be re-entered, and a missing/non-matching answer (defensive)\n * silently skips the marker.\n */\nexport function selectConfirmedIndicator(\n\tquestions: readonly QuestionData[],\n\tcurrentTab: number,\n\tanswers: ReadonlyMap<number, QuestionAnswer>,\n\titems: readonly WrappingSelectItem[],\n): { index: number; labelOverride?: string } | undefined {\n\tconst q = questions[currentTab];\n\tif (!q || q.multiSelect === true) return undefined;\n\tconst prior = answers.get(currentTab);\n\tif (!prior || prior.kind === \"chat\") return undefined;\n\tif (prior.kind === \"custom\") {\n\t\tconst otherIndex = items.findIndex((it) => it.kind === \"other\");\n\t\tif (otherIndex < 0) return undefined;\n\t\treturn { index: otherIndex, labelOverride: prior.answer ?? \"\" };\n\t}\n\tif (prior.kind !== \"option\" || typeof prior.answer !== \"string\") return undefined;\n\tconst index = items.findIndex((it) => it.kind === \"option\" && it.label === prior.answer);\n\tif (index < 0) return undefined;\n\treturn { index };\n}\n\n/**\n * Index of the preview pane to display for the current tab. The Submit tab (currentTab ===\n * questions.length) reuses the last question's pane purely for layout — the strategy\n * machinery picks the right body component independently. Defensive against `totalQuestions === 0`.\n */\nexport function selectActivePreviewPaneIndex(currentTab: number, totalQuestions: number): number {\n\tif (totalQuestions <= 0) return 0;\n\treturn Math.min(currentTab, totalQuestions - 1);\n}\n\n/**\n * Items array for the active tab, with the same Submit-tab clamp as `selectActivePreviewPaneIndex`.\n * Falls back to an empty array if the index lands outside the items array (defensive).\n */\nexport function selectActiveTabItems(\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tcurrentTab: number,\n\ttotalQuestions: number,\n): readonly WrappingSelectItem[] {\n\tconst idx = selectActivePreviewPaneIndex(currentTab, totalQuestions);\n\treturn itemsByTab[idx] ?? [];\n}\n"]}
1
+ {"version":3,"file":"derivations.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/state/selectors/derivations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAGnF;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC7C,SAAS,EAAE,SAAS,YAAY,EAAE,EAClC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GACjB,OAAO,CAKT;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,GAAG;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd,CAOA;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACvC,SAAS,EAAE,SAAS,YAAY,EAAE,EAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,EAC5C,KAAK,EAAE,SAAS,kBAAkB,EAAE,GAClC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAcvD;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAG/F;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,EACxD,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GACpB,SAAS,kBAAkB,EAAE,CAG/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"focus.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/state/selectors/focus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE;IAAE,YAAY,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAC1E,cAAc,EAAE,MAAM,GACpB,UAAU,CAKZ","sourcesContent":["import type { ActiveView } from \"../../view/stateful-view.ts\";\n\n/**\n * Discriminated focus selector — single source of truth for \"which view owns\n * focus this tick?\" Priority order matches the dispatcher cascade\n * (`key-router.ts:151-178`) and the reducer's defensive clears\n * (`state-reducer.ts:104-126`).\n *\n * Priority: notes > submit > chat > options.\n */\nexport function selectActiveView(\n\tstate: { notesVisible: boolean; chatFocused: boolean; currentTab: number },\n\ttotalQuestions: number,\n): ActiveView {\n\tif (state.notesVisible) return \"notes\";\n\tif (state.currentTab === totalQuestions) return \"submit\";\n\tif (state.chatFocused) return \"chat\";\n\treturn \"options\";\n}\n"]}
1
+ {"version":3,"file":"focus.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/state/selectors/focus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE;IAAE,YAAY,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAC1E,cAAc,EAAE,MAAM,GACpB,UAAU,CAKZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"projections.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/state/selectors/projections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAC3G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpE,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,oBAAoB,CAevE,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,mBAAmB,CAWrE,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAAc,CAAC,iBAAiB,CAQrE,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,gBAAgB,CAIlE,CAAC;AAEH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,WAAW,CAazD,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,gBAAgB,CAQ/D,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,WAAW,CAGxD,CAAC","sourcesContent":["import type { ChatRowViewProps } from \"../../view/components/chat-row-view.ts\";\nimport { MULTI_SUBMIT_LABEL, type MultiSelectViewProps } from \"../../view/components/multi-select-view.ts\";\nimport type { OptionListViewProps } from \"../../view/components/option-list-view.ts\";\nimport type { PreviewPaneProps } from \"../../view/components/preview/preview-pane.ts\";\nimport type { SubmitPickerProps } from \"../../view/components/submit-picker.ts\";\nimport type { TabBarProps } from \"../../view/components/tab-bar.ts\";\nimport type { DialogProps } from \"../../view/dialog-builder.ts\";\nimport { ROW_INTENT_META } from \"../row-intent.ts\";\nimport type { GlobalSelector, PerTabSelector } from \"./contract.ts\";\nimport { chatNumberingFor, selectActiveTabItems, selectConfirmedIndicator } from \"./derivations.ts\";\n\nexport const selectMultiSelectProps: PerTabSelector<MultiSelectViewProps> = (state, ctx) => {\n\tconst question = ctx.questions[ctx.i];\n\tif (!question) return { rows: [], nextActive: false, nextLabel: ROW_INTENT_META.next.label };\n\tconst focused = ctx.activeView === \"options\";\n\tconst rows: { checked: boolean; active: boolean }[] = [];\n\tfor (let i = 0; i < question.options.length; i++) {\n\t\trows.push({\n\t\t\tchecked: state.multiSelectChecked.has(i),\n\t\t\tactive: focused && i === state.optionIndex,\n\t\t});\n\t}\n\tconst nextActive = focused && state.optionIndex === question.options.length;\n\tconst isLastQuestion = ctx.i === ctx.questions.length - 1;\n\tconst nextLabel = isLastQuestion ? MULTI_SUBMIT_LABEL : ROW_INTENT_META.next.label;\n\treturn { rows, nextActive, nextLabel };\n};\n\nexport const selectOptionListProps: PerTabSelector<OptionListViewProps> = (state, ctx) => {\n\tconst items = ctx.itemsByTab[ctx.i] ?? [];\n\tconst focused = ctx.activeView === \"options\";\n\tconst confirmed = selectConfirmedIndicator(ctx.questions, state.currentTab, state.answers, items);\n\treturn {\n\t\tselectedIndex: state.optionIndex,\n\t\tfocused,\n\t\tinputBuffer: ctx.inputBuffer,\n\t\tinputCaret: ctx.inputCaret,\n\t\t...(confirmed ? { confirmed } : {}),\n\t};\n};\n\nexport const selectSubmitPickerProps: GlobalSelector<SubmitPickerProps> = (state, ctx) => {\n\tconst focused = ctx.activeView === \"submit\";\n\treturn {\n\t\trows: [\n\t\t\t{ active: focused && state.submitChoiceIndex === 0 },\n\t\t\t{ active: focused && state.submitChoiceIndex === 1 },\n\t\t],\n\t};\n};\n\nexport const selectPreviewPaneProps: PerTabSelector<PreviewPaneProps> = (state, ctx) => ({\n\tnotesVisible: state.notesVisible,\n\tselectedIndex: state.optionIndex,\n\tfocused: ctx.activeView === \"options\",\n});\n\nexport const selectTabBarProps: GlobalSelector<TabBarProps> = (state, ctx) => {\n\tconst tabs = ctx.questions.map((q, i) => ({\n\t\tlabel: q.header && q.header.length > 0 ? q.header : `Q${i + 1}`,\n\t\tanswered: state.answers.has(i),\n\t\tactive: i === state.currentTab,\n\t}));\n\treturn {\n\t\ttabs,\n\t\tsubmit: {\n\t\t\tactive: state.currentTab === ctx.questions.length,\n\t\t\tallAnswered: state.answers.size === ctx.questions.length && ctx.questions.length > 0,\n\t\t},\n\t};\n};\n\nexport const selectChatRowProps: GlobalSelector<ChatRowViewProps> = (state, ctx) => {\n\tconst activeItems = selectActiveTabItems(ctx.itemsByTab, state.currentTab, ctx.totalQuestions);\n\treturn {\n\t\tfocused: ctx.activeView === \"chat\",\n\t\tnumbering: chatNumberingFor(activeItems),\n\t\tinputBuffer: ctx.chatInputBuffer,\n\t\tinputCaret: ctx.chatInputCaret,\n\t};\n};\n\nexport const selectDialogProps: GlobalSelector<DialogProps> = (state, ctx) => ({\n\tstate,\n\tactivePreviewPane: ctx.activePreviewPane,\n});\n"]}
1
+ {"version":3,"file":"projections.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/state/selectors/projections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAC3G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpE,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,oBAAoB,CAevE,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,mBAAmB,CAWrE,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAAc,CAAC,iBAAiB,CAQrE,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,gBAAgB,CAIlE,CAAC;AAEH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,WAAW,CAazD,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,gBAAgB,CAQ/D,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,WAAW,CAGxD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"state-reducer.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/state-reducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAI3D,OAAO,KAAK,EAAoB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvE,uGAAuG;AACvG,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;CACzD;AAED;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GACf;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,yBAAyB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AA4RD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,mBAAmB,EAAE,GAAG,EAAE,YAAY,GAAG,WAAW,CAG7G","sourcesContent":["import type { QuestionAnswer, QuestionData, QuestionnaireResult } from \"../tool/types.ts\";\nimport type { WrappingSelectItem } from \"../view/components/wrapping-select.ts\";\nimport type { QuestionnaireAction } from \"./key-router.ts\";\nimport { readInlineCaret, resolveInlineDraftValue, withInlineDraft } from \"./inline-input.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport { computeFocusedOptionHasPreview } from \"./selectors/derivations.ts\";\nimport type { InlineInputOwner, QuestionnaireState } from \"./state.ts\";\n\n/** Session-lifetime constants. No live-component reads — peripheral values live on canonical state. */\nexport interface ApplyContext {\n\tquestions: readonly QuestionData[];\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n}\n\n/**\n * Declarative side-effects emitted by `reduce`. The runtime executes them after\n * committing the new state, then asks the props-adapter to re-project. Closed set —\n * adding an effect requires updating both the union AND the runtime's `runEffect` switch\n * (compiler-enforced exhaustive). No string-keyed escape hatch.\n */\nexport type Effect =\n\t| { kind: \"set_input_buffer\"; value: string; caret: number }\n\t| { kind: \"set_notes_value\"; value: string }\n\t| { kind: \"set_notes_focused\"; focused: boolean }\n\t| { kind: \"forward_notes_keystroke\"; data: string }\n\t| { kind: \"done\"; result: QuestionnaireResult };\n\nexport interface ApplyResult {\n\tstate: QuestionnaireState;\n\teffects: readonly Effect[];\n}\n\nfunction orderedAnswers(state: QuestionnaireState, questions: readonly QuestionData[]): QuestionAnswer[] {\n\tconst out: QuestionAnswer[] = [];\n\tfor (let i = 0; i < questions.length; i++) {\n\t\tconst a = state.answers.get(i);\n\t\tif (a) out.push(a);\n\t}\n\treturn out;\n}\n\nfunction withFocusedOptionHasPreview(\n\tstate: QuestionnaireState,\n\tquestions: readonly QuestionData[],\n): QuestionnaireState {\n\tconst focusedOptionHasPreview = computeFocusedOptionHasPreview(questions, state.currentTab, state.optionIndex);\n\tif (state.focusedOptionHasPreview === focusedOptionHasPreview) return state;\n\treturn { ...state, focusedOptionHasPreview };\n}\n\nfunction syncMultiSelectFromAnswers(\n\tanswers: ReadonlyMap<number, QuestionAnswer>,\n\tquestions: readonly QuestionData[],\n\ttab: number,\n): ReadonlySet<number> {\n\tconst q = questions[tab];\n\tif (!q?.multiSelect) return new Set();\n\tconst saved = answers.get(tab);\n\tconst labels = saved?.selected ?? [];\n\tconst indices = new Set<number>();\n\tfor (let i = 0; i < q.options.length; i++) {\n\t\tif (labels.includes(q.options[i]!.label)) indices.add(i);\n\t}\n\treturn indices;\n}\n\nfunction persistMultiSelectAnswer(state: QuestionnaireState, ctx: ApplyContext): ReadonlyMap<number, QuestionAnswer> {\n\tconst q = ctx.questions[state.currentTab];\n\tif (!q?.multiSelect) return state.answers;\n\tconst selected: string[] = [];\n\tfor (let i = 0; i < q.options.length; i++) {\n\t\tif (state.multiSelectChecked.has(i)) selected.push(q.options[i]!.label);\n\t}\n\tconst out = new Map(state.answers);\n\tif (selected.length === 0) {\n\t\tout.delete(state.currentTab);\n\t\treturn out;\n\t}\n\tconst pendingNotes = state.notesByTab.get(state.currentTab);\n\tout.set(state.currentTab, {\n\t\tquestionIndex: state.currentTab,\n\t\tquestion: q.question,\n\t\tkind: \"multi\",\n\t\tanswer: null,\n\t\tselected,\n\t\t...(pendingNotes && pendingNotes.length > 0 ? { notes: pendingNotes } : {}),\n\t});\n\treturn out;\n}\n\nfunction clampCaret(value: string, caret: number | undefined): number {\n\tif (caret === undefined || !Number.isFinite(caret)) return value.length;\n\treturn Math.max(0, Math.min(caret, value.length));\n}\n\nfunction inlineOwnerForItem(item: WrappingSelectItem | undefined): InlineInputOwner | null {\n\tif (!item || !ROW_INTENT_META[item.kind].activatesInputMode) return null;\n\tswitch (item.kind) {\n\t\tcase \"other\":\n\t\tcase \"chat\":\n\t\t\treturn item.kind;\n\t\tcase \"option\":\n\t\tcase \"next\":\n\t\t\treturn null;\n\t}\n}\n\nfunction hydrateInlineInputResult(state: QuestionnaireState, owner: InlineInputOwner): ApplyResult {\n\tconst value = resolveInlineDraftValue(state, owner);\n\tconst caret = clampCaret(value, readInlineCaret(state, owner));\n\treturn {\n\t\tstate: withInlineDraft({ ...state, inputMode: true, inlineInputOwner: owner }, owner, value, caret),\n\t\teffects: [{ kind: \"set_input_buffer\", value, caret }],\n\t};\n}\n\nfunction switchTabResult(state: QuestionnaireState, nextTab: number, ctx: ApplyContext): ApplyResult {\n\tconst notesValue = state.notesByTab.get(nextTab) ?? state.answers.get(nextTab)?.notes ?? \"\";\n\tconst transitioned: QuestionnaireState = {\n\t\t...state,\n\t\tcurrentTab: nextTab,\n\t\toptionIndex: 0,\n\t\tinputMode: false,\n\t\tinlineInputOwner: null,\n\t\tnotesVisible: false,\n\t\tchatFocused: false,\n\t\tsubmitChoiceIndex: 0,\n\t\tmultiSelectChecked: syncMultiSelectFromAnswers(state.answers, ctx.questions, nextTab),\n\t\tnotesDraft: notesValue,\n\t};\n\tconst finalState = withFocusedOptionHasPreview(transitioned, ctx.questions);\n\treturn {\n\t\tstate: finalState,\n\t\teffects: [\n\t\t\t{ kind: \"set_notes_focused\", focused: false },\n\t\t\t{ kind: \"set_notes_value\", value: notesValue },\n\t\t],\n\t};\n}\n\nfunction doneFor(state: QuestionnaireState, ctx: ApplyContext, cancelled: boolean): ApplyResult {\n\tconst result: QuestionnaireResult = { answers: orderedAnswers(state, ctx.questions), cancelled };\n\treturn { state, effects: [{ kind: \"done\", result }] };\n}\n\n/**\n * Per-kind handler signature: action payload narrows to the matching union member\n * via `Extract`, so handlers consume fully-typed actions without `as` casts.\n */\ntype Handler<K extends QuestionnaireAction[\"kind\"]> = (\n\tstate: QuestionnaireState,\n\taction: Extract<QuestionnaireAction, { kind: K }>,\n\tctx: ApplyContext,\n) => ApplyResult;\n\nconst navHandler: Handler<\"nav\"> = (state, action, ctx) => {\n\tconst items = ctx.itemsByTab[state.currentTab] ?? [];\n\tconst item = items[action.nextIndex];\n\tconst owner = inlineOwnerForItem(item);\n\tconst next = withFocusedOptionHasPreview(\n\t\t{\n\t\t\t...state,\n\t\t\tchatFocused: false,\n\t\t\toptionIndex: action.nextIndex,\n\t\t\tinputMode: owner !== null,\n\t\t\tinlineInputOwner: owner,\n\t\t},\n\t\tctx.questions,\n\t);\n\treturn owner ? hydrateInlineInputResult(next, owner) : { state: next, effects: [] };\n};\n\nconst tabSwitchHandler: Handler<\"tab_switch\"> = (state, action, ctx) => switchTabResult(state, action.nextTab, ctx);\n\nconst confirmHandler: Handler<\"confirm\"> = (state, action, ctx) => {\n\tlet answer = action.answer;\n\tif (answer.kind === \"option\" && answer.answer) {\n\t\tconst q = ctx.questions[answer.questionIndex];\n\t\tconst matched = q?.options.find((o) => o.label === answer.answer);\n\t\tif (matched?.preview && matched.preview.length > 0) {\n\t\t\tanswer = { ...answer, preview: matched.preview };\n\t\t}\n\t}\n\tconst pendingNotes = state.notesByTab.get(answer.questionIndex);\n\tif (pendingNotes && pendingNotes.length > 0) {\n\t\tanswer = { ...answer, notes: pendingNotes };\n\t}\n\tconst answers = new Map(state.answers);\n\tanswers.set(answer.questionIndex, answer);\n\tconst next: QuestionnaireState = { ...state, answers };\n\tif (answer.kind === \"chat\") return doneFor(next, ctx, false);\n\tif (action.autoAdvanceTab !== undefined) return switchTabResult(next, action.autoAdvanceTab, ctx);\n\treturn doneFor(next, ctx, false);\n};\n\nconst toggleHandler: Handler<\"toggle\"> = (state, action, ctx) => {\n\tconst checked = new Set(state.multiSelectChecked);\n\tif (checked.has(action.index)) checked.delete(action.index);\n\telse checked.add(action.index);\n\tconst intermediate: QuestionnaireState = { ...state, multiSelectChecked: checked };\n\tconst answers = persistMultiSelectAnswer(intermediate, ctx);\n\treturn { state: { ...intermediate, answers }, effects: [] };\n};\n\nconst multiConfirmHandler: Handler<\"multi_confirm\"> = (state, action, ctx) => {\n\tconst q = ctx.questions[state.currentTab];\n\tif (!q) return { state, effects: [] };\n\tconst pendingNotes = state.notesByTab.get(state.currentTab);\n\tconst answers = new Map(state.answers);\n\tanswers.set(state.currentTab, {\n\t\tquestionIndex: state.currentTab,\n\t\tquestion: q.question,\n\t\tkind: \"multi\",\n\t\tanswer: null,\n\t\tselected: action.selected,\n\t\t...(pendingNotes && pendingNotes.length > 0 ? { notes: pendingNotes } : {}),\n\t});\n\tconst synced: QuestionnaireState = {\n\t\t...state,\n\t\tanswers,\n\t\tmultiSelectChecked: syncMultiSelectFromAnswers(answers, ctx.questions, state.currentTab),\n\t};\n\tif (action.autoAdvanceTab !== undefined) return switchTabResult(synced, action.autoAdvanceTab, ctx);\n\treturn doneFor(synced, ctx, false);\n};\n\nconst notesEnterHandler: Handler<\"notes_enter\"> = (state, _action, _ctx) => {\n\tconst value = state.answers.get(state.currentTab)?.notes ?? \"\";\n\treturn {\n\t\tstate: { ...state, notesVisible: true, notesDraft: value },\n\t\teffects: [\n\t\t\t{ kind: \"set_notes_value\", value },\n\t\t\t{ kind: \"set_notes_focused\", focused: true },\n\t\t],\n\t};\n};\n\nconst notesExitHandler: Handler<\"notes_exit\"> = (state, _action, _ctx) => {\n\tconst trimmed = state.notesDraft.trim();\n\tconst notes = new Map(state.notesByTab);\n\tconst answers = new Map(state.answers);\n\tif (trimmed.length === 0) {\n\t\tnotes.delete(state.currentTab);\n\t\tconst prev = answers.get(state.currentTab);\n\t\tif (prev?.notes) {\n\t\t\tconst stripped = { ...prev };\n\t\t\tdelete (stripped as { notes?: string }).notes;\n\t\t\tanswers.set(state.currentTab, stripped);\n\t\t}\n\t} else {\n\t\tnotes.set(state.currentTab, trimmed);\n\t\tconst prev = answers.get(state.currentTab);\n\t\tif (prev) answers.set(state.currentTab, { ...prev, notes: trimmed });\n\t}\n\treturn {\n\t\tstate: { ...state, notesByTab: notes, answers, notesVisible: false },\n\t\teffects: [{ kind: \"set_notes_focused\", focused: false }],\n\t};\n};\n\nconst focusOptionsHandler: Handler<\"focus_options\"> = (state, action, ctx) => {\n\tconst items = ctx.itemsByTab[state.currentTab] ?? [];\n\tconst focused = items[action.optionIndex];\n\tconst owner = inlineOwnerForItem(focused);\n\tconst next = withFocusedOptionHasPreview(\n\t\t{\n\t\t\t...state,\n\t\t\tchatFocused: false,\n\t\t\toptionIndex: action.optionIndex,\n\t\t\tinputMode: owner !== null,\n\t\t\tinlineInputOwner: owner,\n\t\t},\n\t\tctx.questions,\n\t);\n\treturn owner ? hydrateInlineInputResult(next, owner) : { state: next, effects: [] };\n};\n\nconst cancelHandler: Handler<\"cancel\"> = (s, _a, c) => doneFor(s, c, true);\nconst submitHandler: Handler<\"submit\"> = (s, _a, c) => doneFor(s, c, false);\nconst submitNavHandler: Handler<\"submit_nav\"> = (s, a, _c) => ({\n\tstate: { ...s, submitChoiceIndex: a.nextIndex },\n\teffects: [],\n});\nconst focusChatHandler: Handler<\"focus_chat\"> = (s, _a, _c) =>\n\thydrateInlineInputResult({ ...s, chatFocused: true, inputMode: true, inlineInputOwner: \"chat\" }, \"chat\");\nconst notesForwardHandler: Handler<\"notes_forward\"> = (s, a, _c) => ({\n\tstate: s,\n\teffects: [{ kind: \"forward_notes_keystroke\", data: a.data }],\n});\nconst ignoreHandler: Handler<\"ignore\"> = (s, _a, _c) => ({ state: s, effects: [] });\n\n/**\n * Compile-time-exhaustive dispatch table. `{ [K in Kind]: Handler<K> }` requires\n * an entry per union member — adding a new `QuestionnaireAction` variant fails to\n * compile here until a handler is registered, mirroring the `Record<RowKind, …>`\n * pattern used by `ROW_INTENT_META`.\n */\nconst HANDLERS: { [K in QuestionnaireAction[\"kind\"]]: Handler<K> } = {\n\tnav: navHandler,\n\ttab_switch: tabSwitchHandler,\n\tconfirm: confirmHandler,\n\ttoggle: toggleHandler,\n\tmulti_confirm: multiConfirmHandler,\n\tcancel: cancelHandler,\n\tnotes_enter: notesEnterHandler,\n\tnotes_exit: notesExitHandler,\n\tnotes_forward: notesForwardHandler,\n\tsubmit: submitHandler,\n\tsubmit_nav: submitNavHandler,\n\tfocus_chat: focusChatHandler,\n\tfocus_options: focusOptionsHandler,\n\tignore: ignoreHandler,\n};\n\n/**\n * Pure reducer: (state, action, ctx) → (state, Effect[]). Mirrors `rpiv-todo`'s `applyTaskMutation`.\n * Delegates to `HANDLERS` — per-kind handlers above are pure, named, and individually testable.\n * `ignore` is also handled outside the reducer by `handleIgnoreInline` in the runtime fast path.\n */\nexport function reduce(state: QuestionnaireState, action: QuestionnaireAction, ctx: ApplyContext): ApplyResult {\n\tconst handler = HANDLERS[action.kind] as Handler<typeof action.kind>;\n\treturn handler(state, action as never, ctx);\n}\n"]}
1
+ {"version":3,"file":"state-reducer.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/state-reducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAI3D,OAAO,KAAK,EAAoB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvE,uGAAuG;AACvG,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;CACzD;AAED;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GACf;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,yBAAyB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AA4RD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,mBAAmB,EAAE,GAAG,EAAE,YAAY,GAAG,WAAW,CAG7G"}
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,2FAA2F;IAC3F,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7C,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC;;;;OAIG;IACH,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,gIAAgI;IAChI,uBAAuB,EAAE,OAAO,CAAC;IACjC,0FAA0F;IAC1F,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uGAAuG;IACvG,UAAU,EAAE,MAAM,CAAC;IACnB,4GAA4G;IAC5G,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,uEAAuE;IACvE,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,uEAAuE;IACvE,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,qEAAqE;IACrE,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,WAAW,EAAE;QAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC5C,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACrC","sourcesContent":["import type { QuestionAnswer, QuestionData } from \"../tool/types.ts\";\nimport type { WrappingSelectItem } from \"../view/components/wrapping-select.ts\";\nimport type { RowKind } from \"./row-intent.ts\";\n\nexport type InlineInputOwner = Extract<RowKind, \"other\" | \"chat\">;\n\n/**\n * Canonical state for the questionnaire dialog. Single source of truth — both the\n * dispatcher (`routeKey`) and the view layer read this same shape.\n */\nexport interface QuestionnaireState {\n\tcurrentTab: number;\n\toptionIndex: number;\n\tinputMode: boolean;\n\tnotesVisible: boolean;\n\tchatFocused: boolean;\n\t/** Active owner of the shared inline input, or null when no inline row is being edited. */\n\tinlineInputOwner: InlineInputOwner | null;\n\tanswers: ReadonlyMap<number, QuestionAnswer>;\n\tmultiSelectChecked: ReadonlySet<number>;\n\t/**\n\t * Pre-answer notes side-band, keyed by tab index. Decoupled from `answers` so adding\n\t * notes does NOT mark a question answered (the Submit-tab missing-check would falsely\n\t * pass otherwise). Merged into the answer at confirm time.\n\t */\n\tnotesByTab: ReadonlyMap<number, string>;\n\t/** True iff the focused option carries a non-empty `preview` string. Gates `notes_enter` and the \"n to add notes\" hint chip. */\n\tfocusedOptionHasPreview: boolean;\n\t/** Focused row in the Submit-tab picker (0 = Submit, 1 = Cancel). Reset on tab switch. */\n\tsubmitChoiceIndex: number;\n\t/** Canonical mirror of the in-flight notes editor; runtime mirrors after `forward_notes_keystroke`. */\n\tnotesDraft: string;\n\t/** In-flight custom \"Type something.\" drafts keyed by question tab. Preserved when focus leaves the row. */\n\tcustomDraftByTab: ReadonlyMap<number, string>;\n\t/** Caret offsets for in-flight custom drafts keyed by question tab. */\n\tcustomCaretByTab: ReadonlyMap<number, number>;\n\t/** In-flight \"Chat about this\" inline drafts keyed by question tab. */\n\tchatDraftByTab: ReadonlyMap<number, string>;\n\t/** Caret offsets for in-flight chat drafts keyed by question tab. */\n\tchatCaretByTab: ReadonlyMap<number, number>;\n}\n\n/**\n * Per-tick context the dispatcher needs alongside canonical state. Held separately\n * because `keybindings` / `inputBuffer` must never reach view setProps consumers.\n */\nexport interface QuestionnaireRuntime {\n\tkeybindings: { matches(data: string, name: string): boolean };\n\tinputBuffer: string;\n\tquestions: readonly QuestionData[];\n\tisMulti: boolean;\n\tcurrentItem: WrappingSelectItem | undefined;\n\titems: readonly WrappingSelectItem[];\n}\n"]}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,2FAA2F;IAC3F,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7C,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC;;;;OAIG;IACH,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,gIAAgI;IAChI,uBAAuB,EAAE,OAAO,CAAC;IACjC,0FAA0F;IAC1F,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uGAAuG;IACvG,UAAU,EAAE,MAAM,CAAC;IACnB,4GAA4G;IAC5G,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,uEAAuE;IACvE,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,uEAAuE;IACvE,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,qEAAqE;IACrE,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,WAAW,EAAE;QAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC5C,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACrC"}
@@ -1 +1 @@
1
- {"version":3,"file":"format-answer.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/format-answer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,oGAC4D,CAAC;AAEnG;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,kCAAkC,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAKtE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAc1F","sourcesContent":["import { ROW_INTENT_META } from \"../state/row-intent.ts\";\nimport type { QuestionAnswer } from \"./types.ts\";\n\n/**\n * Continuation message used in the LLM-facing envelope. Two-sentence form —\n * the model needs the \"Stop…wait…\" directive to know what to do next after the\n * user picks chat instead of answering.\n */\nexport const CHAT_CONTINUATION_MESSAGE =\n\t\"User wants to chat about this. Stop the current task flow and wait for the user's next message.\";\n\n/**\n * One-sentence summary form shown in the on-screen Submit-tab review pane. The dialog\n * already shows the question; the imperative continuation directive belongs in the\n * envelope, not in the user-facing summary box.\n */\nexport const CHAT_SUMMARY_MESSAGE = \"User wants to chat about this\";\n\n/**\n * Placeholder for empty / null answer text. Used uniformly across both variants — the\n * earlier `(no answer)` fallback in the dialog summary was accidental drift; tests pin\n * `(no input)` only.\n */\nexport const NO_INPUT_PLACEHOLDER = \"(no input)\";\n\nexport type FormatAnswerVariant = \"summary\" | \"envelope\";\n\n/**\n * Return the user-provided chat message, or undefined for the legacy signal-only\n * chat answer. Whitespace-only text and the reserved sentinel label both keep\n * the legacy stop/wait semantics.\n */\nexport function chatAnswerIntent(a: QuestionAnswer): string | undefined {\n\tif (a.kind !== \"chat\" || typeof a.answer !== \"string\") return undefined;\n\tconst trimmed = a.answer.trim();\n\tif (trimmed.length === 0 || trimmed === ROW_INTENT_META.chat.label) return undefined;\n\treturn a.answer;\n}\n\n/**\n * Format a `QuestionAnswer` to its scalar string form. Variant controls only the\n * `kind: \"chat\"` branch — the envelope's stop/wait directive is needed by the LLM,\n * the dialog summary's one-sentence reminder is not. All other branches return identical\n * strings; the `kind: \"custom\"` empty-string handling and the option fallback both unify\n * on `NO_INPUT_PLACEHOLDER`. Switch is exhaustive — non-`void` return enforces every\n * variant is handled.\n */\nexport function formatAnswerScalar(a: QuestionAnswer, variant: FormatAnswerVariant): string {\n\tswitch (a.kind) {\n\t\tcase \"chat\": {\n\t\t\tconst typedChat = chatAnswerIntent(a);\n\t\t\tif (typedChat !== undefined) return typedChat;\n\t\t\treturn variant === \"envelope\" ? CHAT_CONTINUATION_MESSAGE : CHAT_SUMMARY_MESSAGE;\n\t\t}\n\t\tcase \"multi\":\n\t\t\treturn a.selected && a.selected.length > 0 ? a.selected.join(\", \") : NO_INPUT_PLACEHOLDER;\n\t\tcase \"custom\":\n\t\t\treturn a.answer && a.answer.length > 0 ? a.answer : NO_INPUT_PLACEHOLDER;\n\t\tcase \"option\":\n\t\t\treturn a.answer ?? NO_INPUT_PLACEHOLDER;\n\t}\n}\n"]}
1
+ {"version":3,"file":"format-answer.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/format-answer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,oGAC4D,CAAC;AAEnG;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,kCAAkC,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAKtE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAc1F"}
@@ -1 +1 @@
1
- {"version":3,"file":"response-envelope.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/response-envelope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEtF,eAAO,MAAM,eAAe,sCAAsC,CAAC;AACnE,eAAO,MAAM,eAAe,sCAAsC,CAAC;AACnE,eAAO,MAAM,eAAe,0DAA0D,CAAC;AAMvF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAElE;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc;;cA8C7F,MAAM;;;;;EAfzB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAK5D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE;;cAEzF,MAAM;;;;;EAIzB","sourcesContent":["import { chatAnswerIntent, formatAnswerScalar } from \"./format-answer.ts\";\nimport type { QuestionAnswer, QuestionnaireResult, QuestionParams } from \"./types.ts\";\n\nexport const DECLINE_MESSAGE = \"User declined to answer questions\";\nexport const ENVELOPE_PREFIX = \"User has answered your questions:\";\nexport const ENVELOPE_SUFFIX = \"You can now continue with the user's answers in mind.\";\nconst CHAT_TERMINATION_DIRECTIVE =\n\t\"User wants to chat about this before choosing. Stop the current task flow and wait for the user's next message.\";\nconst TYPED_CHAT_DIRECTIVE =\n\t\"User wants to chat about this before choosing and provided inline text. Stop the structured-choice flow and respond to the user's message.\";\n\n/**\n * True when any answer in the result carries `kind: \"chat\"`.\n * Used by `buildQuestionnaireResponse` to switch to the terminate path.\n */\nexport function hasChatAnswer(result: QuestionnaireResult): boolean {\n\treturn result.answers.some((a) => a.kind === \"chat\");\n}\n\n/**\n * Map a `QuestionnaireResult` (or null/cancelled) to the LLM-facing tool envelope.\n * Pure of `(result, params)`; cancelled and non-chat \"no segments\" both fall to\n * `DECLINE_MESSAGE` so the model sees a single canonical \"didn't answer\" signal\n * regardless of why.\n *\n * Chat rule: signal-only chat keeps the legacy `terminate: true` stop/wait\n * wording. Typed chat omits `terminate` and the generic continuation suffix so\n * the model can respond to the inline user message in this same turn.\n */\nexport function buildQuestionnaireResponse(result: QuestionnaireResult | null | undefined, params: QuestionParams) {\n\tif (!result || result.cancelled) {\n\t\treturn buildToolResult(DECLINE_MESSAGE, {\n\t\t\tanswers: result?.answers ?? [],\n\t\t\tcancelled: true,\n\t\t});\n\t}\n\tconst containsChatAnswer = hasChatAnswer(result);\n\tconst segments: string[] = [];\n\tfor (let i = 0; i < params.questions.length; i++) {\n\t\tconst a = result.answers.find((x) => x.questionIndex === i);\n\t\tif (a) segments.push(buildAnswerSegment(a));\n\t}\n\tif (containsChatAnswer) {\n\t\tconst answerSegments = segments.length > 0 ? ` ${segments.join(\" \")}` : \"\";\n\t\t// Mixed-dialog precedence: signal-only chat WINS. In a multi-question dialog where one\n\t\t// question carried typed chat and another is signal-only, the whole envelope takes the\n\t\t// `terminate: true` stop/wait path — a bare \"chat about this\" is an explicit request to\n\t\t// pause the structured flow, so it dominates. The typed message is not lost: it still\n\t\t// rides along in `answerSegments` for context. This precedence is a deliberate product\n\t\t// decision; mixing typed + signal-only chat across questions is a rare edge.\n\t\tconst hasSignalOnlyChat = result.answers.some((a) => a.kind === \"chat\" && chatAnswerIntent(a) === undefined);\n\t\tif (!hasSignalOnlyChat) {\n\t\t\treturn buildToolResult(`${TYPED_CHAT_DIRECTIVE}${answerSegments}`, result);\n\t\t}\n\t\treturn buildToolResult(`${CHAT_TERMINATION_DIRECTIVE}${answerSegments}`, result, { terminate: true });\n\t}\n\tif (segments.length === 0) {\n\t\treturn buildToolResult(DECLINE_MESSAGE, { answers: result.answers, cancelled: true });\n\t}\n\treturn buildToolResult(`${ENVELOPE_PREFIX} ${segments.join(\" \")} ${ENVELOPE_SUFFIX}`, result);\n}\n\n/**\n * Format a single answer segment for the envelope. Pure of `a`. The `\"Q\"=\"A\"` shape and\n * the optional `selected preview:` / `user notes:` suffixes are pinned by envelope tests.\n */\nexport function buildAnswerSegment(a: QuestionAnswer): string {\n\tconst parts: string[] = [`\"${a.question}\"=\"${formatAnswerScalar(a, \"envelope\")}\"`];\n\tif (a.preview && a.preview.length > 0) parts.push(`selected preview: ${a.preview}`);\n\tif (a.notes && a.notes.length > 0) parts.push(`user notes: ${a.notes}`);\n\treturn `${parts.join(\". \")}.`;\n}\n\nexport function buildToolResult(text: string, details: QuestionnaireResult, options?: { terminate?: boolean }) {\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text }],\n\t\tdetails,\n\t\t...(options?.terminate === true ? { terminate: true } : {}),\n\t};\n}\n"]}
1
+ {"version":3,"file":"response-envelope.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/response-envelope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEtF,eAAO,MAAM,eAAe,sCAAsC,CAAC;AACnE,eAAO,MAAM,eAAe,sCAAsC,CAAC;AACnE,eAAO,MAAM,eAAe,0DAA0D,CAAC;AAMvF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAElE;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc;;cA8C7F,MAAM;;;;;EAfzB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAK5D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE;;cAEzF,MAAM;;;;;EAIzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;CAAiB,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,eAAe,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,YAAY,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,YAC3B,OAAO,yBAIE,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,eAAO,MAAM,YAAY;;;;EAevB,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;EAsBzB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;GAI1B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;EAE/B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AACrD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AACzD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEjE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC7C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,kBAAkB,GAC3B,OAAO,GACP,cAAc,GACd,eAAe,GACf,oBAAoB,GACpB,oBAAoB,GACpB,wBAAwB,GACxB,gBAAgB,CAAC;AAEpB,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC3B;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAIlF","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport { LABELS_BY_KIND, ROW_INTENT_META } from \"../state/row-intent.ts\";\n\nexport const MAX_QUESTIONS = 4;\nexport const MIN_OPTIONS = 2;\nexport const MAX_OPTIONS = 4;\nexport const MAX_HEADER_LENGTH = 16;\nexport const MAX_LABEL_LENGTH = 60;\n\n/**\n * User-facing labels for the three runtime sentinel rows, keyed by their\n * `WrappingSelectItem.kind` discriminator. Sourced from\n * `ROW_INTENT_META` via `LABELS_BY_KIND` (`row-intent.ts`) — single source of\n * truth. Adding a new sentinel requires extending the `WrappingSelectItem`\n * union AND adding an entry to `ROW_INTENT_META`; this map then auto-extends.\n */\nexport const SENTINEL_LABELS = LABELS_BY_KIND;\n\nexport type SentinelKind = keyof typeof SENTINEL_LABELS;\nexport type SentinelLabel = (typeof SENTINEL_LABELS)[SentinelKind];\n\n/**\n * Labels reserved for Pi-internal sentinels — authoring an option with any\n * of these labels triggers the `reserved_label` runtime guard. Three of the\n * four come from `ROW_INTENT_META` (the runtime kinds); `\"Other\"` is\n * reserved for CC parity only (the model is conditioned to reach for\n * \"Other\" in CC; we reject it so the runtime sentinel is the single source\n * of truth) and has no runtime kind.\n *\n * Reserved unconditionally — multiSelect questions also reject these labels\n * even though the runtime sentinel is suppressed there.\n *\n * Order is pinned by `types.test.ts:292` — keep the explicit\n * `[\"Other\", other, chat, next]` literal so consumers using\n * `RESERVED_LABELS[i]` indexing or `Set` membership see no behavior change.\n */\nexport const RESERVED_LABELS = [\n\t\"Other\",\n\tROW_INTENT_META.other.label,\n\tROW_INTENT_META.chat.label,\n\tROW_INTENT_META.next.label,\n] as const;\nexport type ReservedLabel = (typeof RESERVED_LABELS)[number];\n\nexport const OptionSchema = Type.Object({\n\tlabel: Type.String({\n\t\tmaxLength: MAX_LABEL_LENGTH,\n\t\tdescription: `MAX ${MAX_LABEL_LENGTH} CHARACTERS — hard limit, requests over the limit are rejected. The display text for this option that the user will see and select. Should be concise (1-5 words) and clearly describe the choice.`,\n\t}),\n\tdescription: Type.String({\n\t\tdescription:\n\t\t\t\"Explanation of what this option means or what will happen if chosen. Useful for providing context about trade-offs or implications.\",\n\t}),\n\tpreview: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Optional preview content rendered when this option is focused. Use for mockups, code snippets, or visual comparisons that help users compare options. See the tool description for the expected content format.\",\n\t\t}),\n\t),\n});\n\nexport const QuestionSchema = Type.Object({\n\tquestion: Type.String({\n\t\tdescription:\n\t\t\t'The complete question to ask the user. Should be clear, specific, and end with a question mark. Example: \"Which library should we use for date formatting?\" If multiSelect is true, phrase it accordingly, e.g. \"Which features do you want to enable?\"',\n\t}),\n\theader: Type.String({\n\t\tmaxLength: MAX_HEADER_LENGTH,\n\t\tdescription: `MAX ${MAX_HEADER_LENGTH} CHARACTERS — hard limit, requests over the limit are rejected. Very short chip/tag shown next to the question. Examples: \"Auth method\", \"Library\", \"Approach\".`,\n\t}),\n\toptions: Type.Array(OptionSchema, {\n\t\tminItems: MIN_OPTIONS,\n\t\tmaxItems: MAX_OPTIONS,\n\t\tdescription:\n\t\t\t\"The available choices for this question. Must have 2-4 options. Each option should be a distinct, mutually exclusive choice (unless multiSelect is enabled). The 'Type something.' row is appended automatically — do NOT author it.\",\n\t}),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdefault: false,\n\t\t\tdescription:\n\t\t\t\t\"Set to true to allow the user to select multiple options instead of just one. Use when choices are not mutually exclusive.\",\n\t\t}),\n\t),\n});\n\nexport const QuestionsSchema = Type.Array(QuestionSchema, {\n\tminItems: 1,\n\tmaxItems: MAX_QUESTIONS,\n\tdescription: \"Questions to ask the user (1-4 questions)\",\n});\n\nexport const QuestionParamsSchema = Type.Object({\n\tquestions: QuestionsSchema,\n});\n\nexport type OptionData = Static<typeof OptionSchema>;\nexport type QuestionData = Static<typeof QuestionSchema>;\nexport type QuestionParams = Static<typeof QuestionParamsSchema>;\n\n/**\n * Answer-intent discriminated union. `kind` is the single discriminator —\n * pre-1.0.3 boolean flags have been removed (see `banned-flags.test.ts`).\n * Mirrors the row-side `WrappingSelectItem.kind` vocabulary where possible;\n * `multi` is the multi-select variant (no row-side analog).\n *\n * Variant semantics:\n * - `option`: user picked one of the author-defined options. `answer` is the option's label.\n * - `custom`: user typed free-text via the \"Type something.\" row. `answer` is the typed text or null.\n * - `chat`: user picked the chat sentinel. `answer` is either typed inline chat text or the legacy\n * literal \"Chat about this\" when submitted empty / whitespace-only.\n * - `multi`: user committed multi-select choices. `selected` carries chosen labels; `answer` is null.\n */\nexport interface QuestionAnswer {\n\tquestionIndex: number;\n\tquestion: string;\n\tkind: \"option\" | \"custom\" | \"chat\" | \"multi\";\n\tanswer: string | null;\n\tselected?: string[];\n\tnotes?: string;\n\t/**\n\t * Markdown text from the matched option's `preview` field, populated only\n\t * when the user lands on a single-select option carrying a `preview`.\n\t * Used by `buildQuestionnaireResponse` to echo `selected preview: <preview>`\n\t * into the LLM-facing envelope. Undefined for multi-select, custom-text\n\t * (`kind: \"custom\"`), and chat (`kind: \"chat\"`) answers.\n\t */\n\tpreview?: string;\n}\n\nexport type QuestionnaireError =\n\t| \"no_ui\"\n\t| \"no_questions\"\n\t| \"empty_options\"\n\t| \"too_many_questions\"\n\t| \"duplicate_question\"\n\t| \"duplicate_option_label\"\n\t| \"reserved_label\";\n\nexport interface QuestionnaireResult {\n\tanswers: QuestionAnswer[];\n\tcancelled: boolean;\n\terror?: QuestionnaireError;\n}\n\nexport function isQuestionnaireResult(value: unknown): value is QuestionnaireResult {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst v = value as Record<string, unknown>;\n\treturn Array.isArray(v.answers) && typeof v.cancelled === \"boolean\";\n}\n"]}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;CAAiB,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,eAAe,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,YAAY,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,YAC3B,OAAO,yBAIE,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,eAAO,MAAM,YAAY;;;;EAevB,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;EAsBzB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;GAI1B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;EAE/B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AACrD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AACzD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEjE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC7C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,kBAAkB,GAC3B,OAAO,GACP,cAAc,GACd,eAAe,GACf,oBAAoB,GACpB,oBAAoB,GACpB,wBAAwB,GACxB,gBAAgB,CAAC;AAEpB,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC3B;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAIlF"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate-questionnaire.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/validate-questionnaire.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAmB,MAAM,YAAY,CAAC;AAEvH,eAAO,MAAM,kBAAkB,6CAA6C,CAAC;AAC7E,eAAO,MAAM,wBAAwB,0DAAyE,CAAC;AAC/G,eAAO,MAAM,wBAAwB,6DAA6D,CAAC;AACnG,eAAO,MAAM,qBAAqB,qDAAkE,CAAC;AACrG,eAAO,MAAM,oBAAoB,QAAoE,CAAC;AACtG,eAAO,MAAM,4BAA4B,0DAA0D,CAAC;AAIpG,MAAM,MAAM,gBAAgB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExG;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,gBAAgB,CAqC7E","sourcesContent":["import { MAX_QUESTIONS, MIN_OPTIONS, type QuestionnaireError, type QuestionParams, RESERVED_LABELS } from \"./types.ts\";\n\nexport const ERROR_NO_QUESTIONS = \"Error: At least one question is required\";\nexport const ERROR_TOO_MANY_QUESTIONS = `Error: At most ${MAX_QUESTIONS} questions are allowed per invocation`;\nexport const ERROR_DUPLICATE_QUESTION = \"Error: Question text must be unique within an invocation\";\nexport const ERROR_TOO_FEW_OPTIONS = `Error: Each question requires at least ${MIN_OPTIONS} options`;\nexport const ERROR_RESERVED_LABEL = `Error: Option label is reserved (${RESERVED_LABELS.join(\", \")})`;\nexport const ERROR_DUPLICATE_OPTION_LABEL = \"Error: Option labels must be unique within a question\";\n\nconst RESERVED_LABEL_SET: ReadonlySet<string> = new Set(RESERVED_LABELS);\n\nexport type ValidationResult = { ok: true } | { ok: false; error: QuestionnaireError; message: string };\n\n/**\n * Pure runtime validator for `QuestionParams`. Covers every guard except\n * `no_ui` (which depends on `ctx.hasUI` and stays inline at the call site).\n * `reserved_label` MUST short-circuit before `duplicate_option_label`.\n */\nexport function validateQuestionnaire(typed: QuestionParams): ValidationResult {\n\tif (typed.questions.length === 0) {\n\t\treturn { ok: false, error: \"no_questions\", message: ERROR_NO_QUESTIONS };\n\t}\n\tif (typed.questions.length > MAX_QUESTIONS) {\n\t\treturn { ok: false, error: \"too_many_questions\", message: ERROR_TOO_MANY_QUESTIONS };\n\t}\n\n\tconst seenQuestions = new Set<string>();\n\tfor (const q of typed.questions) {\n\t\tif (seenQuestions.has(q.question)) {\n\t\t\treturn { ok: false, error: \"duplicate_question\", message: ERROR_DUPLICATE_QUESTION };\n\t\t}\n\t\tseenQuestions.add(q.question);\n\t}\n\n\tfor (const q of typed.questions) {\n\t\tif (q.options.length < MIN_OPTIONS) {\n\t\t\treturn { ok: false, error: \"empty_options\", message: ERROR_TOO_FEW_OPTIONS };\n\t\t}\n\t\tconst seenLabels = new Set<string>();\n\t\tfor (const o of q.options) {\n\t\t\tif (RESERVED_LABEL_SET.has(o.label)) {\n\t\t\t\treturn { ok: false, error: \"reserved_label\", message: ERROR_RESERVED_LABEL };\n\t\t\t}\n\t\t\tif (seenLabels.has(o.label)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"duplicate_option_label\",\n\t\t\t\t\tmessage: ERROR_DUPLICATE_OPTION_LABEL,\n\t\t\t\t};\n\t\t\t}\n\t\t\tseenLabels.add(o.label);\n\t\t}\n\t}\n\n\treturn { ok: true };\n}\n"]}
1
+ {"version":3,"file":"validate-questionnaire.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/validate-questionnaire.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAmB,MAAM,YAAY,CAAC;AAEvH,eAAO,MAAM,kBAAkB,6CAA6C,CAAC;AAC7E,eAAO,MAAM,wBAAwB,0DAAyE,CAAC;AAC/G,eAAO,MAAM,wBAAwB,6DAA6D,CAAC;AACnG,eAAO,MAAM,qBAAqB,qDAAkE,CAAC;AACrG,eAAO,MAAM,oBAAoB,QAAoE,CAAC;AACtG,eAAO,MAAM,4BAA4B,0DAA0D,CAAC;AAIpG,MAAM,MAAM,gBAAgB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExG;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,gBAAgB,CAqC7E"}