pi-oxidized 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1517) hide show
  1. package/.agent-tasks/pi-rust-rewrite/Cargo.lock +5022 -0
  2. package/.agent-tasks/pi-rust-rewrite/Cargo.toml +22 -0
  3. package/.agent-tasks/pi-rust-rewrite/GOALS.md +1 -0
  4. package/.agent-tasks/pi-rust-rewrite/fixtures/phase7/goldens/branch_summary.json +8 -0
  5. package/.agent-tasks/pi-rust-rewrite/fixtures/phase7/goldens/changelog.md +16 -0
  6. package/.agent-tasks/pi-rust-rewrite/fixtures/phase7/goldens/debug_dump.txt +11 -0
  7. package/.agent-tasks/pi-rust-rewrite/fixtures/phase7/goldens/events.jsonl +6 -0
  8. package/.agent-tasks/pi-rust-rewrite/fixtures/phase7/goldens/legacy_auth_migration.json +16 -0
  9. package/.agent-tasks/pi-rust-rewrite/fixtures/phase7/goldens/selector_snapshot.txt +9 -0
  10. package/.agent-tasks/pi-rust-rewrite/fixtures/phase7/goldens/session_stats.json +16 -0
  11. package/.agent-tasks/pi-rust-rewrite/fixtures/phase7/goldens/share_result.json +7 -0
  12. package/.agent-tasks/pi-rust-rewrite/fixtures/phase7/sessions/export-roundtrip.jsonl +6 -0
  13. package/.agent-tasks/pi-rust-rewrite/fixtures/provider-drain/isolation-expectations.json +21 -0
  14. package/.agent-tasks/pi-rust-rewrite/fixtures/rpc/commands.json +238 -0
  15. package/.agent-tasks/pi-rust-rewrite/fixtures/rpc/host-state.json +128 -0
  16. package/.agent-tasks/pi-rust-rewrite/fixtures/rpc/ts-goldens.json +1 -0
  17. package/.agent-tasks/pi-rust-rewrite/fixtures/rpc/ts-replay.ts +317 -0
  18. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v1/linear-with-compaction.expected.json +576 -0
  19. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v1/linear-with-compaction.jsonl +12 -0
  20. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v2/branched.expected.json +387 -0
  21. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v2/branched.jsonl +8 -0
  22. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/basic.expected.json +256 -0
  23. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/basic.jsonl +5 -0
  24. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/branched-labels.expected.json +474 -0
  25. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/branched-labels.jsonl +13 -0
  26. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/branched-session.expected.json +352 -0
  27. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/branched-session.jsonl +9 -0
  28. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/compacted-twice.expected.json +590 -0
  29. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/compacted-twice.jsonl +13 -0
  30. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/custom-messages.expected.json +400 -0
  31. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/custom-messages.jsonl +8 -0
  32. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/forked-header.expected.json +257 -0
  33. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/forked-header.jsonl +5 -0
  34. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/unknown-entries.expected.json +297 -0
  35. package/.agent-tasks/pi-rust-rewrite/fixtures/sessions/v3/unknown-entries.jsonl +6 -0
  36. package/.agent-tasks/pi-rust-rewrite/fixtures/tool-schemas/bash.json +16 -0
  37. package/.agent-tasks/pi-rust-rewrite/fixtures/tool-schemas/edit.json +34 -0
  38. package/.agent-tasks/pi-rust-rewrite/fixtures/tool-schemas/find.json +20 -0
  39. package/.agent-tasks/pi-rust-rewrite/fixtures/tool-schemas/grep.json +36 -0
  40. package/.agent-tasks/pi-rust-rewrite/fixtures/tool-schemas/ls.json +13 -0
  41. package/.agent-tasks/pi-rust-rewrite/fixtures/tool-schemas/read.json +20 -0
  42. package/.agent-tasks/pi-rust-rewrite/fixtures/tool-schemas/write.json +17 -0
  43. package/.agent-tasks/pi-rust-rewrite/fixtures/tools/edit-cases.json +93 -0
  44. package/.agent-tasks/pi-rust-rewrite/helpers/reopen_session.ts +168 -0
  45. package/.agent-tasks/pi-rust-rewrite/src/lib.rs +13 -0
  46. package/.agent-tasks/pi-rust-rewrite/tests/phase7_fixtures.rs +742 -0
  47. package/.agent-tasks/pi-rust-rewrite/tests/pi_ai_contract.rs +101 -0
  48. package/.agent-tasks/pi-rust-rewrite/tests/provider_drain_isolation.rs +760 -0
  49. package/.agent-tasks/pi-rust-rewrite/tests/rpc_parity.rs +679 -0
  50. package/.agent-tasks/pi-rust-rewrite/tests/session_compat.rs +839 -0
  51. package/.agent-tasks/pi-rust-rewrite/tests/tool_parity.rs +761 -0
  52. package/.github/workflows/release-verification.yml +204 -0
  53. package/.gitmodules +5 -0
  54. package/.outline/audit/coverage.json +3262 -0
  55. package/.outline/audit/queue.json +4841 -0
  56. package/.outline/review-fix-grill/iterations/1.json +767 -0
  57. package/.outline/review-fix-grill/iterations/2.json +69 -0
  58. package/.outline/review-fix-grill/iterations/3.json +81 -0
  59. package/.outline/review-fix-grill/iterations/4.json +35 -0
  60. package/.outline/review-fix-grill/iterations/5.json +21 -0
  61. package/.outline/review-fix-grill/queue.json +6454 -0
  62. package/.references/pi/.gitattributes +23 -0
  63. package/.references/pi/.github/APPROVED_CONTRIBUTORS +281 -0
  64. package/.references/pi/.github/ISSUE_TEMPLATE/bug.yml +45 -0
  65. package/.references/pi/.github/ISSUE_TEMPLATE/config.yml +5 -0
  66. package/.references/pi/.github/ISSUE_TEMPLATE/contribution.yml +36 -0
  67. package/.references/pi/.github/ISSUE_TEMPLATE/package-report.yml +49 -0
  68. package/.references/pi/.github/workflows/approve-contributor.yml +179 -0
  69. package/.references/pi/.github/workflows/build-binaries.yml +294 -0
  70. package/.references/pi/.github/workflows/ci.yml +42 -0
  71. package/.references/pi/.github/workflows/issue-analysis.yml +634 -0
  72. package/.references/pi/.github/workflows/issue-gate.yml +129 -0
  73. package/.references/pi/.github/workflows/issue-triage-labels.yml +142 -0
  74. package/.references/pi/.github/workflows/npm-audit.yml +31 -0
  75. package/.references/pi/.github/workflows/pr-gate.yml +128 -0
  76. package/.references/pi/.github/workflows/publish-model-catalog.yml +112 -0
  77. package/.references/pi/.github/workflows/remove-inprogress-on-close.yml +31 -0
  78. package/.references/pi/.husky/pre-commit +45 -0
  79. package/.references/pi/.pi/extensions/import-repro.ts +351 -0
  80. package/.references/pi/.pi/extensions/prompt-url-widget.ts +270 -0
  81. package/.references/pi/.pi/extensions/redraws.ts +24 -0
  82. package/.references/pi/.pi/extensions/tps.ts +47 -0
  83. package/.references/pi/.pi/git/.gitignore +2 -0
  84. package/.references/pi/.pi/npm/.gitignore +2 -0
  85. package/.references/pi/.pi/prompts/cl.md +54 -0
  86. package/.references/pi/.pi/prompts/is.md +28 -0
  87. package/.references/pi/.pi/prompts/pr.md +37 -0
  88. package/.references/pi/.pi/prompts/sa.md +163 -0
  89. package/.references/pi/.pi/prompts/wr.md +40 -0
  90. package/.references/pi/.pi/skills/add-llm-provider.md +57 -0
  91. package/.references/pi/AGENTS.md +162 -0
  92. package/.references/pi/CONTRIBUTING.md +102 -0
  93. package/.references/pi/LICENSE +21 -0
  94. package/.references/pi/README.md +99 -0
  95. package/.references/pi/SECURITY.md +87 -0
  96. package/.references/pi/biome.json +39 -0
  97. package/.references/pi/bun.lock +961 -0
  98. package/.references/pi/package-lock.json +6172 -0
  99. package/.references/pi/package.json +66 -0
  100. package/.references/pi/packages/agent/CHANGELOG.md +594 -0
  101. package/.references/pi/packages/agent/README.md +488 -0
  102. package/.references/pi/packages/agent/docs/agent-harness.md +488 -0
  103. package/.references/pi/packages/agent/docs/durable-harness.md +212 -0
  104. package/.references/pi/packages/agent/docs/hooks.md +445 -0
  105. package/.references/pi/packages/agent/docs/models.md +964 -0
  106. package/.references/pi/packages/agent/docs/observability.md +376 -0
  107. package/.references/pi/packages/agent/package.json +60 -0
  108. package/.references/pi/packages/agent/src/agent-loop.ts +792 -0
  109. package/.references/pi/packages/agent/src/agent.ts +575 -0
  110. package/.references/pi/packages/agent/src/harness/agent-harness.ts +1029 -0
  111. package/.references/pi/packages/agent/src/harness/compaction/branch-summarization.ts +261 -0
  112. package/.references/pi/packages/agent/src/harness/compaction/compaction.ts +753 -0
  113. package/.references/pi/packages/agent/src/harness/compaction/utils.ts +144 -0
  114. package/.references/pi/packages/agent/src/harness/env/nodejs.ts +569 -0
  115. package/.references/pi/packages/agent/src/harness/messages.ts +164 -0
  116. package/.references/pi/packages/agent/src/harness/prompt-templates.ts +267 -0
  117. package/.references/pi/packages/agent/src/harness/session/jsonl-repo.ts +179 -0
  118. package/.references/pi/packages/agent/src/harness/session/jsonl-storage.ts +314 -0
  119. package/.references/pi/packages/agent/src/harness/session/memory-repo.ts +50 -0
  120. package/.references/pi/packages/agent/src/harness/session/memory-storage.ts +133 -0
  121. package/.references/pi/packages/agent/src/harness/session/repo-utils.ts +51 -0
  122. package/.references/pi/packages/agent/src/harness/session/session.ts +338 -0
  123. package/.references/pi/packages/agent/src/harness/session/uuid.ts +54 -0
  124. package/.references/pi/packages/agent/src/harness/skills.ts +375 -0
  125. package/.references/pi/packages/agent/src/harness/system-prompt.ts +34 -0
  126. package/.references/pi/packages/agent/src/harness/types.ts +838 -0
  127. package/.references/pi/packages/agent/src/harness/utils/shell-output.ts +135 -0
  128. package/.references/pi/packages/agent/src/harness/utils/truncate.ts +344 -0
  129. package/.references/pi/packages/agent/src/index.ts +46 -0
  130. package/.references/pi/packages/agent/src/node.ts +2 -0
  131. package/.references/pi/packages/agent/src/proxy.ts +367 -0
  132. package/.references/pi/packages/agent/src/types.ts +430 -0
  133. package/.references/pi/packages/agent/test/agent-loop.test.ts +1424 -0
  134. package/.references/pi/packages/agent/test/agent.test.ts +699 -0
  135. package/.references/pi/packages/agent/test/e2e.test.ts +404 -0
  136. package/.references/pi/packages/agent/test/harness/agent-harness-stream.test.ts +213 -0
  137. package/.references/pi/packages/agent/test/harness/agent-harness.test.ts +608 -0
  138. package/.references/pi/packages/agent/test/harness/compaction.test.ts +655 -0
  139. package/.references/pi/packages/agent/test/harness/nodejs-env.test.ts +321 -0
  140. package/.references/pi/packages/agent/test/harness/prompt-templates.test.ts +90 -0
  141. package/.references/pi/packages/agent/test/harness/repo.test.ts +92 -0
  142. package/.references/pi/packages/agent/test/harness/resource-formatting.test.ts +24 -0
  143. package/.references/pi/packages/agent/test/harness/session-test-utils.ts +55 -0
  144. package/.references/pi/packages/agent/test/harness/session-uuid.test.ts +50 -0
  145. package/.references/pi/packages/agent/test/harness/session.test.ts +203 -0
  146. package/.references/pi/packages/agent/test/harness/skills.test.ts +116 -0
  147. package/.references/pi/packages/agent/test/harness/storage.test.ts +341 -0
  148. package/.references/pi/packages/agent/test/harness/system-prompt.test.ts +66 -0
  149. package/.references/pi/packages/agent/test/harness/truncate.test.ts +169 -0
  150. package/.references/pi/packages/agent/test/scratch/simple.ts +72 -0
  151. package/.references/pi/packages/agent/test/utils/calculate.ts +32 -0
  152. package/.references/pi/packages/agent/test/utils/get-current-time.ts +46 -0
  153. package/.references/pi/packages/agent/tsconfig.build.json +13 -0
  154. package/.references/pi/packages/agent/vitest.config.ts +21 -0
  155. package/.references/pi/packages/agent/vitest.harness.config.ts +28 -0
  156. package/.references/pi/packages/ai/CHANGELOG.md +1739 -0
  157. package/.references/pi/packages/ai/README.md +1621 -0
  158. package/.references/pi/packages/ai/bedrock-provider.d.ts +1 -0
  159. package/.references/pi/packages/ai/bedrock-provider.js +1 -0
  160. package/.references/pi/packages/ai/package.json +97 -0
  161. package/.references/pi/packages/ai/scripts/generate-image-models.ts +131 -0
  162. package/.references/pi/packages/ai/scripts/generate-models.ts +2401 -0
  163. package/.references/pi/packages/ai/scripts/generate-test-image.ts +33 -0
  164. package/.references/pi/packages/ai/src/api/anthropic-messages.lazy.ts +4 -0
  165. package/.references/pi/packages/ai/src/api/anthropic-messages.ts +1313 -0
  166. package/.references/pi/packages/ai/src/api/azure-openai-responses.lazy.ts +4 -0
  167. package/.references/pi/packages/ai/src/api/azure-openai-responses.ts +298 -0
  168. package/.references/pi/packages/ai/src/api/bedrock-converse-stream.lazy.ts +30 -0
  169. package/.references/pi/packages/ai/src/api/bedrock-converse-stream.ts +1091 -0
  170. package/.references/pi/packages/ai/src/api/cloudflare.ts +15 -0
  171. package/.references/pi/packages/ai/src/api/github-copilot-headers.ts +37 -0
  172. package/.references/pi/packages/ai/src/api/google-generative-ai.lazy.ts +4 -0
  173. package/.references/pi/packages/ai/src/api/google-generative-ai.ts +509 -0
  174. package/.references/pi/packages/ai/src/api/google-shared.ts +350 -0
  175. package/.references/pi/packages/ai/src/api/google-vertex.lazy.ts +4 -0
  176. package/.references/pi/packages/ai/src/api/google-vertex.ts +584 -0
  177. package/.references/pi/packages/ai/src/api/lazy.ts +75 -0
  178. package/.references/pi/packages/ai/src/api/mistral-conversations.lazy.ts +4 -0
  179. package/.references/pi/packages/ai/src/api/mistral-conversations.ts +664 -0
  180. package/.references/pi/packages/ai/src/api/openai-codex-responses.lazy.ts +4 -0
  181. package/.references/pi/packages/ai/src/api/openai-codex-responses.ts +1573 -0
  182. package/.references/pi/packages/ai/src/api/openai-completions.lazy.ts +4 -0
  183. package/.references/pi/packages/ai/src/api/openai-completions.ts +1355 -0
  184. package/.references/pi/packages/ai/src/api/openai-prompt-cache.ts +8 -0
  185. package/.references/pi/packages/ai/src/api/openai-responses-shared.ts +613 -0
  186. package/.references/pi/packages/ai/src/api/openai-responses.lazy.ts +4 -0
  187. package/.references/pi/packages/ai/src/api/openai-responses.ts +318 -0
  188. package/.references/pi/packages/ai/src/api/openrouter-images.lazy.ts +10 -0
  189. package/.references/pi/packages/ai/src/api/openrouter-images.ts +185 -0
  190. package/.references/pi/packages/ai/src/api/pi-messages.lazy.ts +4 -0
  191. package/.references/pi/packages/ai/src/api/pi-messages.ts +436 -0
  192. package/.references/pi/packages/ai/src/api/simple-options.ts +77 -0
  193. package/.references/pi/packages/ai/src/api/transform-messages.ts +223 -0
  194. package/.references/pi/packages/ai/src/auth/context.ts +45 -0
  195. package/.references/pi/packages/ai/src/auth/credential-store.ts +51 -0
  196. package/.references/pi/packages/ai/src/auth/helpers.ts +47 -0
  197. package/.references/pi/packages/ai/src/auth/oauth/anthropic.ts +350 -0
  198. package/.references/pi/packages/ai/src/auth/oauth/device-code.ts +98 -0
  199. package/.references/pi/packages/ai/src/auth/oauth/github-copilot.ts +379 -0
  200. package/.references/pi/packages/ai/src/auth/oauth/load.ts +56 -0
  201. package/.references/pi/packages/ai/src/auth/oauth/oauth-page.ts +109 -0
  202. package/.references/pi/packages/ai/src/auth/oauth/openai-codex.ts +538 -0
  203. package/.references/pi/packages/ai/src/auth/oauth/pkce.ts +34 -0
  204. package/.references/pi/packages/ai/src/auth/oauth/radius.ts +410 -0
  205. package/.references/pi/packages/ai/src/auth/oauth/xai.ts +238 -0
  206. package/.references/pi/packages/ai/src/auth/resolve.ts +139 -0
  207. package/.references/pi/packages/ai/src/auth/types.ts +220 -0
  208. package/.references/pi/packages/ai/src/bedrock-provider.ts +6 -0
  209. package/.references/pi/packages/ai/src/bun-oauth.ts +17 -0
  210. package/.references/pi/packages/ai/src/cli.ts +118 -0
  211. package/.references/pi/packages/ai/src/compat/extension-oauth-types.ts +45 -0
  212. package/.references/pi/packages/ai/src/compat.ts +298 -0
  213. package/.references/pi/packages/ai/src/env-api-keys.ts +178 -0
  214. package/.references/pi/packages/ai/src/image-models.generated.ts +534 -0
  215. package/.references/pi/packages/ai/src/image-models.ts +42 -0
  216. package/.references/pi/packages/ai/src/images-api-registry.ts +53 -0
  217. package/.references/pi/packages/ai/src/images-models.ts +274 -0
  218. package/.references/pi/packages/ai/src/images.ts +21 -0
  219. package/.references/pi/packages/ai/src/index.ts +45 -0
  220. package/.references/pi/packages/ai/src/legacy-api-aliases.ts +108 -0
  221. package/.references/pi/packages/ai/src/models-store.ts +38 -0
  222. package/.references/pi/packages/ai/src/models.generated.ts +76 -0
  223. package/.references/pi/packages/ai/src/models.ts +705 -0
  224. package/.references/pi/packages/ai/src/oauth.ts +10 -0
  225. package/.references/pi/packages/ai/src/providers/all.ts +140 -0
  226. package/.references/pi/packages/ai/src/providers/amazon-bedrock.models.ts +1897 -0
  227. package/.references/pi/packages/ai/src/providers/amazon-bedrock.ts +80 -0
  228. package/.references/pi/packages/ai/src/providers/ant-ling.models.ts +62 -0
  229. package/.references/pi/packages/ai/src/providers/ant-ling.ts +15 -0
  230. package/.references/pi/packages/ai/src/providers/anthropic.models.ts +257 -0
  231. package/.references/pi/packages/ai/src/providers/anthropic.ts +20 -0
  232. package/.references/pi/packages/ai/src/providers/azure-openai-responses.models.ts +816 -0
  233. package/.references/pi/packages/ai/src/providers/azure-openai-responses.ts +14 -0
  234. package/.references/pi/packages/ai/src/providers/cerebras.models.ts +61 -0
  235. package/.references/pi/packages/ai/src/providers/cerebras.ts +15 -0
  236. package/.references/pi/packages/ai/src/providers/cloudflare-ai-gateway.models.ts +760 -0
  237. package/.references/pi/packages/ai/src/providers/cloudflare-ai-gateway.ts +23 -0
  238. package/.references/pi/packages/ai/src/providers/cloudflare-auth.ts +96 -0
  239. package/.references/pi/packages/ai/src/providers/cloudflare-stream.ts +28 -0
  240. package/.references/pi/packages/ai/src/providers/cloudflare-workers-ai.models.ts +241 -0
  241. package/.references/pi/packages/ai/src/providers/cloudflare-workers-ai.ts +15 -0
  242. package/.references/pi/packages/ai/src/providers/deepseek.models.ts +45 -0
  243. package/.references/pi/packages/ai/src/providers/deepseek.ts +15 -0
  244. package/.references/pi/packages/ai/src/providers/faux.ts +538 -0
  245. package/.references/pi/packages/ai/src/providers/fireworks.models.ts +297 -0
  246. package/.references/pi/packages/ai/src/providers/fireworks.ts +19 -0
  247. package/.references/pi/packages/ai/src/providers/github-copilot.models.ts +543 -0
  248. package/.references/pi/packages/ai/src/providers/github-copilot.ts +34 -0
  249. package/.references/pi/packages/ai/src/providers/google-vertex.models.ts +184 -0
  250. package/.references/pi/packages/ai/src/providers/google-vertex.ts +93 -0
  251. package/.references/pi/packages/ai/src/providers/google.models.ts +290 -0
  252. package/.references/pi/packages/ai/src/providers/google.ts +15 -0
  253. package/.references/pi/packages/ai/src/providers/groq.models.ts +127 -0
  254. package/.references/pi/packages/ai/src/providers/groq.ts +15 -0
  255. package/.references/pi/packages/ai/src/providers/huggingface.models.ts +889 -0
  256. package/.references/pi/packages/ai/src/providers/huggingface.ts +15 -0
  257. package/.references/pi/packages/ai/src/providers/images/register-builtins.ts +50 -0
  258. package/.references/pi/packages/ai/src/providers/kimi-coding.models.ts +103 -0
  259. package/.references/pi/packages/ai/src/providers/kimi-coding.ts +15 -0
  260. package/.references/pi/packages/ai/src/providers/minimax-cn.models.ts +58 -0
  261. package/.references/pi/packages/ai/src/providers/minimax-cn.ts +15 -0
  262. package/.references/pi/packages/ai/src/providers/minimax.models.ts +58 -0
  263. package/.references/pi/packages/ai/src/providers/minimax.ts +15 -0
  264. package/.references/pi/packages/ai/src/providers/mistral.models.ts +517 -0
  265. package/.references/pi/packages/ai/src/providers/mistral.ts +15 -0
  266. package/.references/pi/packages/ai/src/providers/moonshotai-cn.models.ts +190 -0
  267. package/.references/pi/packages/ai/src/providers/moonshotai-cn.ts +15 -0
  268. package/.references/pi/packages/ai/src/providers/moonshotai.models.ts +190 -0
  269. package/.references/pi/packages/ai/src/providers/moonshotai.ts +15 -0
  270. package/.references/pi/packages/ai/src/providers/nvidia.models.ts +387 -0
  271. package/.references/pi/packages/ai/src/providers/nvidia.ts +15 -0
  272. package/.references/pi/packages/ai/src/providers/openai-codex.models.ts +144 -0
  273. package/.references/pi/packages/ai/src/providers/openai-codex.ts +18 -0
  274. package/.references/pi/packages/ai/src/providers/openai.models.ts +830 -0
  275. package/.references/pi/packages/ai/src/providers/openai.ts +15 -0
  276. package/.references/pi/packages/ai/src/providers/opencode-go.models.ts +278 -0
  277. package/.references/pi/packages/ai/src/providers/opencode-go.ts +18 -0
  278. package/.references/pi/packages/ai/src/providers/opencode.models.ts +1001 -0
  279. package/.references/pi/packages/ai/src/providers/opencode.ts +24 -0
  280. package/.references/pi/packages/ai/src/providers/openrouter-images.ts +14 -0
  281. package/.references/pi/packages/ai/src/providers/openrouter.models.ts +4915 -0
  282. package/.references/pi/packages/ai/src/providers/openrouter.ts +15 -0
  283. package/.references/pi/packages/ai/src/providers/radius-config.ts +95 -0
  284. package/.references/pi/packages/ai/src/providers/radius.ts +67 -0
  285. package/.references/pi/packages/ai/src/providers/together.models.ts +382 -0
  286. package/.references/pi/packages/ai/src/providers/together.ts +15 -0
  287. package/.references/pi/packages/ai/src/providers/vercel-ai-gateway.models.ts +3268 -0
  288. package/.references/pi/packages/ai/src/providers/vercel-ai-gateway.ts +15 -0
  289. package/.references/pi/packages/ai/src/providers/xai.models.ts +62 -0
  290. package/.references/pi/packages/ai/src/providers/xai.ts +27 -0
  291. package/.references/pi/packages/ai/src/providers/xiaomi-token-plan-ams.models.ts +61 -0
  292. package/.references/pi/packages/ai/src/providers/xiaomi-token-plan-ams.ts +15 -0
  293. package/.references/pi/packages/ai/src/providers/xiaomi-token-plan-cn.models.ts +61 -0
  294. package/.references/pi/packages/ai/src/providers/xiaomi-token-plan-cn.ts +15 -0
  295. package/.references/pi/packages/ai/src/providers/xiaomi-token-plan-sgp.models.ts +61 -0
  296. package/.references/pi/packages/ai/src/providers/xiaomi-token-plan-sgp.ts +15 -0
  297. package/.references/pi/packages/ai/src/providers/xiaomi.models.ts +115 -0
  298. package/.references/pi/packages/ai/src/providers/xiaomi.ts +15 -0
  299. package/.references/pi/packages/ai/src/providers/zai-coding-cn.models.ts +116 -0
  300. package/.references/pi/packages/ai/src/providers/zai-coding-cn.ts +15 -0
  301. package/.references/pi/packages/ai/src/providers/zai.models.ts +116 -0
  302. package/.references/pi/packages/ai/src/providers/zai.ts +15 -0
  303. package/.references/pi/packages/ai/src/session-resources.ts +24 -0
  304. package/.references/pi/packages/ai/src/types.ts +738 -0
  305. package/.references/pi/packages/ai/src/utils/abort-signals.ts +41 -0
  306. package/.references/pi/packages/ai/src/utils/deferred-tools.ts +39 -0
  307. package/.references/pi/packages/ai/src/utils/diagnostics.ts +45 -0
  308. package/.references/pi/packages/ai/src/utils/error-body.ts +127 -0
  309. package/.references/pi/packages/ai/src/utils/estimate.ts +143 -0
  310. package/.references/pi/packages/ai/src/utils/event-stream.ts +88 -0
  311. package/.references/pi/packages/ai/src/utils/hash.ts +13 -0
  312. package/.references/pi/packages/ai/src/utils/headers.ts +18 -0
  313. package/.references/pi/packages/ai/src/utils/json-parse.ts +124 -0
  314. package/.references/pi/packages/ai/src/utils/node-http-proxy.ts +112 -0
  315. package/.references/pi/packages/ai/src/utils/overflow.ts +165 -0
  316. package/.references/pi/packages/ai/src/utils/provider-env.ts +52 -0
  317. package/.references/pi/packages/ai/src/utils/retry.ts +101 -0
  318. package/.references/pi/packages/ai/src/utils/sanitize-unicode.ts +25 -0
  319. package/.references/pi/packages/ai/src/utils/typebox-helpers.ts +24 -0
  320. package/.references/pi/packages/ai/src/utils/validation.ts +310 -0
  321. package/.references/pi/packages/ai/test/abort.test.ts +303 -0
  322. package/.references/pi/packages/ai/test/anthropic-adaptive-thinking-models.test.ts +39 -0
  323. package/.references/pi/packages/ai/test/anthropic-cache-write-1h-cost.test.ts +86 -0
  324. package/.references/pi/packages/ai/test/anthropic-eager-tool-input-compat.test.ts +122 -0
  325. package/.references/pi/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts +155 -0
  326. package/.references/pi/packages/ai/test/anthropic-empty-thinking-signature-compat.test.ts +108 -0
  327. package/.references/pi/packages/ai/test/anthropic-force-adaptive-thinking.test.ts +125 -0
  328. package/.references/pi/packages/ai/test/anthropic-long-cache-retention-e2e.test.ts +127 -0
  329. package/.references/pi/packages/ai/test/anthropic-oauth.test.ts +137 -0
  330. package/.references/pi/packages/ai/test/anthropic-opus-4-8-smoke.test.ts +71 -0
  331. package/.references/pi/packages/ai/test/anthropic-sse-parsing.test.ts +275 -0
  332. package/.references/pi/packages/ai/test/anthropic-temperature-compat.test.ts +103 -0
  333. package/.references/pi/packages/ai/test/anthropic-thinking-disable.test.ts +172 -0
  334. package/.references/pi/packages/ai/test/anthropic-tool-name-normalization.test.ts +204 -0
  335. package/.references/pi/packages/ai/test/azure-openai-base-url.test.ts +175 -0
  336. package/.references/pi/packages/ai/test/azure-openai-responses-reasoning-replay.test.ts +136 -0
  337. package/.references/pi/packages/ai/test/azure-utils.ts +28 -0
  338. package/.references/pi/packages/ai/test/bedrock-convert-messages.test.ts +245 -0
  339. package/.references/pi/packages/ai/test/bedrock-custom-headers.test.ts +202 -0
  340. package/.references/pi/packages/ai/test/bedrock-endpoint-resolution.test.ts +208 -0
  341. package/.references/pi/packages/ai/test/bedrock-models.test.ts +65 -0
  342. package/.references/pi/packages/ai/test/bedrock-thinking-payload.test.ts +246 -0
  343. package/.references/pi/packages/ai/test/bedrock-utils.ts +18 -0
  344. package/.references/pi/packages/ai/test/cache-retention.test.ts +498 -0
  345. package/.references/pi/packages/ai/test/cloudflare-stream.test.ts +65 -0
  346. package/.references/pi/packages/ai/test/cloudflare-utils.ts +9 -0
  347. package/.references/pi/packages/ai/test/codex-websocket-cached-probe.ts +299 -0
  348. package/.references/pi/packages/ai/test/compat-env.test.ts +74 -0
  349. package/.references/pi/packages/ai/test/context-estimate.test.ts +81 -0
  350. package/.references/pi/packages/ai/test/context-overflow.test.ts +734 -0
  351. package/.references/pi/packages/ai/test/cross-provider-handoff.test.ts +502 -0
  352. package/.references/pi/packages/ai/test/data/red-circle.png +0 -0
  353. package/.references/pi/packages/ai/test/deferred-tools.test.ts +481 -0
  354. package/.references/pi/packages/ai/test/empty.test.ts +741 -0
  355. package/.references/pi/packages/ai/test/env-api-keys.test.ts +60 -0
  356. package/.references/pi/packages/ai/test/error-body.test.ts +154 -0
  357. package/.references/pi/packages/ai/test/faux-provider.test.ts +597 -0
  358. package/.references/pi/packages/ai/test/fireworks-models.test.ts +269 -0
  359. package/.references/pi/packages/ai/test/github-copilot-anthropic.test.ts +120 -0
  360. package/.references/pi/packages/ai/test/github-copilot-oauth.test.ts +444 -0
  361. package/.references/pi/packages/ai/test/google-shared-convert-tools.test.ts +187 -0
  362. package/.references/pi/packages/ai/test/google-shared-gemini3-unsigned-tool-call.test.ts +116 -0
  363. package/.references/pi/packages/ai/test/google-shared-image-tool-result-routing.test.ts +102 -0
  364. package/.references/pi/packages/ai/test/google-thinking-disable.test.ts +160 -0
  365. package/.references/pi/packages/ai/test/google-thinking-signature.test.ts +38 -0
  366. package/.references/pi/packages/ai/test/google-vertex-api-key-resolution.test.ts +223 -0
  367. package/.references/pi/packages/ai/test/image-tool-result.test.ts +501 -0
  368. package/.references/pi/packages/ai/test/images-models.test.ts +209 -0
  369. package/.references/pi/packages/ai/test/images.test.ts +90 -0
  370. package/.references/pi/packages/ai/test/interleaved-thinking.test.ts +144 -0
  371. package/.references/pi/packages/ai/test/lax-message-content.test.ts +67 -0
  372. package/.references/pi/packages/ai/test/lazy-module-load.test.ts +119 -0
  373. package/.references/pi/packages/ai/test/max-thinking.test.ts +89 -0
  374. package/.references/pi/packages/ai/test/mistral-reasoning-mode.test.ts +97 -0
  375. package/.references/pi/packages/ai/test/mistral-tool-schema.test.ts +60 -0
  376. package/.references/pi/packages/ai/test/models-runtime.test.ts +689 -0
  377. package/.references/pi/packages/ai/test/node-http-proxy.test.ts +76 -0
  378. package/.references/pi/packages/ai/test/oauth-auth.test.ts +141 -0
  379. package/.references/pi/packages/ai/test/oauth-device-code.test.ts +139 -0
  380. package/.references/pi/packages/ai/test/oauth.ts +83 -0
  381. package/.references/pi/packages/ai/test/openai-codex-cache-affinity-e2e.test.ts +35 -0
  382. package/.references/pi/packages/ai/test/openai-codex-oauth.test.ts +479 -0
  383. package/.references/pi/packages/ai/test/openai-codex-stream.test.ts +2028 -0
  384. package/.references/pi/packages/ai/test/openai-completions-cache-control-format.test.ts +189 -0
  385. package/.references/pi/packages/ai/test/openai-completions-empty-tools.test.ts +304 -0
  386. package/.references/pi/packages/ai/test/openai-completions-prompt-cache.test.ts +264 -0
  387. package/.references/pi/packages/ai/test/openai-completions-reasoning-details.test.ts +118 -0
  388. package/.references/pi/packages/ai/test/openai-completions-response-model.test.ts +140 -0
  389. package/.references/pi/packages/ai/test/openai-completions-retry.test.ts +86 -0
  390. package/.references/pi/packages/ai/test/openai-completions-thinking-as-text.test.ts +218 -0
  391. package/.references/pi/packages/ai/test/openai-completions-tool-choice.test.ts +1716 -0
  392. package/.references/pi/packages/ai/test/openai-completions-tool-result-images.test.ts +152 -0
  393. package/.references/pi/packages/ai/test/openai-responses-cache-affinity-e2e.test.ts +31 -0
  394. package/.references/pi/packages/ai/test/openai-responses-compat.test.ts +472 -0
  395. package/.references/pi/packages/ai/test/openai-responses-empty-tool-result.test.ts +58 -0
  396. package/.references/pi/packages/ai/test/openai-responses-foreign-toolcall-id.test.ts +66 -0
  397. package/.references/pi/packages/ai/test/openai-responses-message-id.test.ts +48 -0
  398. package/.references/pi/packages/ai/test/openai-responses-partial-json-cleanup.test.ts +106 -0
  399. package/.references/pi/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts +291 -0
  400. package/.references/pi/packages/ai/test/openai-responses-terminal-event.test.ts +233 -0
  401. package/.references/pi/packages/ai/test/openai-responses-tool-result-images.test.ts +194 -0
  402. package/.references/pi/packages/ai/test/openrouter-cache-write-repro.test.ts +77 -0
  403. package/.references/pi/packages/ai/test/openrouter-images.test.ts +140 -0
  404. package/.references/pi/packages/ai/test/overflow.test.ts +139 -0
  405. package/.references/pi/packages/ai/test/pi-messages.test.ts +243 -0
  406. package/.references/pi/packages/ai/test/provider-error-body-passthrough.test.ts +78 -0
  407. package/.references/pi/packages/ai/test/provider-error-body-regression.test.ts +189 -0
  408. package/.references/pi/packages/ai/test/providers.test.ts +404 -0
  409. package/.references/pi/packages/ai/test/responseid.test.ts +120 -0
  410. package/.references/pi/packages/ai/test/retry.test.ts +59 -0
  411. package/.references/pi/packages/ai/test/scratch.ts +57 -0
  412. package/.references/pi/packages/ai/test/stream.test.ts +1632 -0
  413. package/.references/pi/packages/ai/test/supports-xhigh.test.ts +145 -0
  414. package/.references/pi/packages/ai/test/together-models.test.ts +78 -0
  415. package/.references/pi/packages/ai/test/tokens.test.ts +333 -0
  416. package/.references/pi/packages/ai/test/tool-call-id-normalization.test.ts +290 -0
  417. package/.references/pi/packages/ai/test/tool-call-without-result.test.ts +327 -0
  418. package/.references/pi/packages/ai/test/total-tokens.test.ts +789 -0
  419. package/.references/pi/packages/ai/test/transform-messages-copilot-openai-to-anthropic.test.ts +191 -0
  420. package/.references/pi/packages/ai/test/unicode-surrogate.test.ts +774 -0
  421. package/.references/pi/packages/ai/test/validation.test.ts +116 -0
  422. package/.references/pi/packages/ai/test/xai-oauth.test.ts +330 -0
  423. package/.references/pi/packages/ai/test/xai-responses.test.ts +117 -0
  424. package/.references/pi/packages/ai/test/xhigh.test.ts +70 -0
  425. package/.references/pi/packages/ai/test/xiaomi-models.test.ts +17 -0
  426. package/.references/pi/packages/ai/test/xiaomi-token-plan-ams-anthropic-empty-signature-smoke.test.ts +114 -0
  427. package/.references/pi/packages/ai/test/zen.test.ts +25 -0
  428. package/.references/pi/packages/ai/tsconfig.build.json +9 -0
  429. package/.references/pi/packages/ai/vitest.config.ts +11 -0
  430. package/.references/pi/packages/coding-agent/CHANGELOG.md +5038 -0
  431. package/.references/pi/packages/coding-agent/README.md +691 -0
  432. package/.references/pi/packages/coding-agent/docs/compaction.md +396 -0
  433. package/.references/pi/packages/coding-agent/docs/containerization.md +111 -0
  434. package/.references/pi/packages/coding-agent/docs/custom-provider.md +735 -0
  435. package/.references/pi/packages/coding-agent/docs/development.md +71 -0
  436. package/.references/pi/packages/coding-agent/docs/docs.json +156 -0
  437. package/.references/pi/packages/coding-agent/docs/extensions.md +2911 -0
  438. package/.references/pi/packages/coding-agent/docs/images/doom-extension.png +0 -0
  439. package/.references/pi/packages/coding-agent/docs/images/exy.png +0 -0
  440. package/.references/pi/packages/coding-agent/docs/images/interactive-mode.png +0 -0
  441. package/.references/pi/packages/coding-agent/docs/images/tree-view.png +0 -0
  442. package/.references/pi/packages/coding-agent/docs/index.md +82 -0
  443. package/.references/pi/packages/coding-agent/docs/json.md +82 -0
  444. package/.references/pi/packages/coding-agent/docs/keybindings.md +198 -0
  445. package/.references/pi/packages/coding-agent/docs/models.md +540 -0
  446. package/.references/pi/packages/coding-agent/docs/packages.md +228 -0
  447. package/.references/pi/packages/coding-agent/docs/prompt-templates.md +95 -0
  448. package/.references/pi/packages/coding-agent/docs/providers.md +290 -0
  449. package/.references/pi/packages/coding-agent/docs/quickstart.md +165 -0
  450. package/.references/pi/packages/coding-agent/docs/rpc.md +1480 -0
  451. package/.references/pi/packages/coding-agent/docs/sdk.md +1183 -0
  452. package/.references/pi/packages/coding-agent/docs/security.md +59 -0
  453. package/.references/pi/packages/coding-agent/docs/session-format.md +422 -0
  454. package/.references/pi/packages/coding-agent/docs/sessions.md +145 -0
  455. package/.references/pi/packages/coding-agent/docs/settings.md +319 -0
  456. package/.references/pi/packages/coding-agent/docs/shell-aliases.md +13 -0
  457. package/.references/pi/packages/coding-agent/docs/skills.md +231 -0
  458. package/.references/pi/packages/coding-agent/docs/terminal-setup.md +142 -0
  459. package/.references/pi/packages/coding-agent/docs/termux.md +127 -0
  460. package/.references/pi/packages/coding-agent/docs/themes.md +297 -0
  461. package/.references/pi/packages/coding-agent/docs/tmux.md +63 -0
  462. package/.references/pi/packages/coding-agent/docs/tui.md +927 -0
  463. package/.references/pi/packages/coding-agent/docs/usage.md +310 -0
  464. package/.references/pi/packages/coding-agent/docs/windows.md +17 -0
  465. package/.references/pi/packages/coding-agent/examples/README.md +25 -0
  466. package/.references/pi/packages/coding-agent/examples/extensions/README.md +213 -0
  467. package/.references/pi/packages/coding-agent/examples/extensions/auto-commit-on-exit.ts +49 -0
  468. package/.references/pi/packages/coding-agent/examples/extensions/bash-spawn-hook.ts +30 -0
  469. package/.references/pi/packages/coding-agent/examples/extensions/bookmark.ts +50 -0
  470. package/.references/pi/packages/coding-agent/examples/extensions/border-status-editor.ts +150 -0
  471. package/.references/pi/packages/coding-agent/examples/extensions/built-in-tool-renderer.ts +249 -0
  472. package/.references/pi/packages/coding-agent/examples/extensions/claude-rules.ts +86 -0
  473. package/.references/pi/packages/coding-agent/examples/extensions/commands.ts +72 -0
  474. package/.references/pi/packages/coding-agent/examples/extensions/confirm-destructive.ts +59 -0
  475. package/.references/pi/packages/coding-agent/examples/extensions/custom-compaction.ts +128 -0
  476. package/.references/pi/packages/coding-agent/examples/extensions/custom-footer.ts +64 -0
  477. package/.references/pi/packages/coding-agent/examples/extensions/custom-header.ts +73 -0
  478. package/.references/pi/packages/coding-agent/examples/extensions/custom-provider-anthropic/index.ts +604 -0
  479. package/.references/pi/packages/coding-agent/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
  480. package/.references/pi/packages/coding-agent/examples/extensions/custom-provider-anthropic/package.json +19 -0
  481. package/.references/pi/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/index.ts +404 -0
  482. package/.references/pi/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
  483. package/.references/pi/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
  484. package/.references/pi/packages/coding-agent/examples/extensions/dirty-repo-guard.ts +56 -0
  485. package/.references/pi/packages/coding-agent/examples/extensions/doom-overlay/README.md +46 -0
  486. package/.references/pi/packages/coding-agent/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
  487. package/.references/pi/packages/coding-agent/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  488. package/.references/pi/packages/coding-agent/examples/extensions/doom-overlay/doom/build.sh +152 -0
  489. package/.references/pi/packages/coding-agent/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
  490. package/.references/pi/packages/coding-agent/examples/extensions/doom-overlay/doom-component.ts +132 -0
  491. package/.references/pi/packages/coding-agent/examples/extensions/doom-overlay/doom-engine.ts +173 -0
  492. package/.references/pi/packages/coding-agent/examples/extensions/doom-overlay/doom-keys.ts +104 -0
  493. package/.references/pi/packages/coding-agent/examples/extensions/doom-overlay/index.ts +74 -0
  494. package/.references/pi/packages/coding-agent/examples/extensions/doom-overlay/wad-finder.ts +51 -0
  495. package/.references/pi/packages/coding-agent/examples/extensions/dynamic-resources/SKILL.md +8 -0
  496. package/.references/pi/packages/coding-agent/examples/extensions/dynamic-resources/dynamic.json +79 -0
  497. package/.references/pi/packages/coding-agent/examples/extensions/dynamic-resources/dynamic.md +5 -0
  498. package/.references/pi/packages/coding-agent/examples/extensions/dynamic-resources/index.ts +15 -0
  499. package/.references/pi/packages/coding-agent/examples/extensions/dynamic-tools.ts +74 -0
  500. package/.references/pi/packages/coding-agent/examples/extensions/entry-renderer.ts +41 -0
  501. package/.references/pi/packages/coding-agent/examples/extensions/event-bus.ts +43 -0
  502. package/.references/pi/packages/coding-agent/examples/extensions/file-trigger.ts +41 -0
  503. package/.references/pi/packages/coding-agent/examples/extensions/git-checkpoint.ts +53 -0
  504. package/.references/pi/packages/coding-agent/examples/extensions/git-merge-and-resolve.ts +115 -0
  505. package/.references/pi/packages/coding-agent/examples/extensions/github-issue-autocomplete.ts +185 -0
  506. package/.references/pi/packages/coding-agent/examples/extensions/gondolin/index.ts +531 -0
  507. package/.references/pi/packages/coding-agent/examples/extensions/gondolin/package-lock.json +185 -0
  508. package/.references/pi/packages/coding-agent/examples/extensions/gondolin/package.json +19 -0
  509. package/.references/pi/packages/coding-agent/examples/extensions/handoff.ts +191 -0
  510. package/.references/pi/packages/coding-agent/examples/extensions/hello.ts +26 -0
  511. package/.references/pi/packages/coding-agent/examples/extensions/hidden-thinking-label.ts +53 -0
  512. package/.references/pi/packages/coding-agent/examples/extensions/inline-bash.ts +94 -0
  513. package/.references/pi/packages/coding-agent/examples/extensions/input-transform-streaming.ts +39 -0
  514. package/.references/pi/packages/coding-agent/examples/extensions/input-transform.ts +43 -0
  515. package/.references/pi/packages/coding-agent/examples/extensions/interactive-shell.ts +196 -0
  516. package/.references/pi/packages/coding-agent/examples/extensions/kimi-deferred-tools.ts +61 -0
  517. package/.references/pi/packages/coding-agent/examples/extensions/mac-system-theme.ts +47 -0
  518. package/.references/pi/packages/coding-agent/examples/extensions/message-renderer.ts +59 -0
  519. package/.references/pi/packages/coding-agent/examples/extensions/minimal-mode.ts +426 -0
  520. package/.references/pi/packages/coding-agent/examples/extensions/modal-editor.ts +85 -0
  521. package/.references/pi/packages/coding-agent/examples/extensions/model-status.ts +31 -0
  522. package/.references/pi/packages/coding-agent/examples/extensions/notify.ts +55 -0
  523. package/.references/pi/packages/coding-agent/examples/extensions/overlay-qa-tests.ts +1450 -0
  524. package/.references/pi/packages/coding-agent/examples/extensions/overlay-test.ts +153 -0
  525. package/.references/pi/packages/coding-agent/examples/extensions/permission-gate.ts +34 -0
  526. package/.references/pi/packages/coding-agent/examples/extensions/pirate.ts +47 -0
  527. package/.references/pi/packages/coding-agent/examples/extensions/plan-mode/README.md +66 -0
  528. package/.references/pi/packages/coding-agent/examples/extensions/plan-mode/index.ts +390 -0
  529. package/.references/pi/packages/coding-agent/examples/extensions/plan-mode/utils.ts +168 -0
  530. package/.references/pi/packages/coding-agent/examples/extensions/preset.ts +436 -0
  531. package/.references/pi/packages/coding-agent/examples/extensions/project-trust.ts +64 -0
  532. package/.references/pi/packages/coding-agent/examples/extensions/prompt-customizer.ts +97 -0
  533. package/.references/pi/packages/coding-agent/examples/extensions/protected-paths.ts +30 -0
  534. package/.references/pi/packages/coding-agent/examples/extensions/provider-payload.ts +18 -0
  535. package/.references/pi/packages/coding-agent/examples/extensions/qna.ts +122 -0
  536. package/.references/pi/packages/coding-agent/examples/extensions/question.ts +286 -0
  537. package/.references/pi/packages/coding-agent/examples/extensions/questionnaire.ts +448 -0
  538. package/.references/pi/packages/coding-agent/examples/extensions/rainbow-editor.ts +88 -0
  539. package/.references/pi/packages/coding-agent/examples/extensions/reload-runtime.ts +37 -0
  540. package/.references/pi/packages/coding-agent/examples/extensions/rpc-demo.ts +118 -0
  541. package/.references/pi/packages/coding-agent/examples/extensions/sandbox/index.ts +321 -0
  542. package/.references/pi/packages/coding-agent/examples/extensions/sandbox/package-lock.json +92 -0
  543. package/.references/pi/packages/coding-agent/examples/extensions/sandbox/package.json +19 -0
  544. package/.references/pi/packages/coding-agent/examples/extensions/send-user-message.ts +97 -0
  545. package/.references/pi/packages/coding-agent/examples/extensions/session-name.ts +27 -0
  546. package/.references/pi/packages/coding-agent/examples/extensions/shutdown-command.ts +63 -0
  547. package/.references/pi/packages/coding-agent/examples/extensions/snake.ts +343 -0
  548. package/.references/pi/packages/coding-agent/examples/extensions/space-invaders.ts +560 -0
  549. package/.references/pi/packages/coding-agent/examples/extensions/ssh.ts +220 -0
  550. package/.references/pi/packages/coding-agent/examples/extensions/status-line.ts +32 -0
  551. package/.references/pi/packages/coding-agent/examples/extensions/structured-output.ts +65 -0
  552. package/.references/pi/packages/coding-agent/examples/extensions/subagent/README.md +175 -0
  553. package/.references/pi/packages/coding-agent/examples/extensions/subagent/agents/planner.md +37 -0
  554. package/.references/pi/packages/coding-agent/examples/extensions/subagent/agents/reviewer.md +35 -0
  555. package/.references/pi/packages/coding-agent/examples/extensions/subagent/agents/scout.md +50 -0
  556. package/.references/pi/packages/coding-agent/examples/extensions/subagent/agents/worker.md +24 -0
  557. package/.references/pi/packages/coding-agent/examples/extensions/subagent/agents.ts +126 -0
  558. package/.references/pi/packages/coding-agent/examples/extensions/subagent/index.ts +1015 -0
  559. package/.references/pi/packages/coding-agent/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  560. package/.references/pi/packages/coding-agent/examples/extensions/subagent/prompts/implement.md +10 -0
  561. package/.references/pi/packages/coding-agent/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  562. package/.references/pi/packages/coding-agent/examples/extensions/summarize.ts +207 -0
  563. package/.references/pi/packages/coding-agent/examples/extensions/system-prompt-header.ts +17 -0
  564. package/.references/pi/packages/coding-agent/examples/extensions/tic-tac-toe.ts +1008 -0
  565. package/.references/pi/packages/coding-agent/examples/extensions/timed-confirm.ts +70 -0
  566. package/.references/pi/packages/coding-agent/examples/extensions/titlebar-spinner.ts +58 -0
  567. package/.references/pi/packages/coding-agent/examples/extensions/todo.ts +297 -0
  568. package/.references/pi/packages/coding-agent/examples/extensions/tool-override.ts +144 -0
  569. package/.references/pi/packages/coding-agent/examples/extensions/tools.ts +146 -0
  570. package/.references/pi/packages/coding-agent/examples/extensions/trigger-compact.ts +50 -0
  571. package/.references/pi/packages/coding-agent/examples/extensions/truncated-tool.ts +195 -0
  572. package/.references/pi/packages/coding-agent/examples/extensions/widget-placement.ts +9 -0
  573. package/.references/pi/packages/coding-agent/examples/extensions/with-deps/index.ts +32 -0
  574. package/.references/pi/packages/coding-agent/examples/extensions/with-deps/package-lock.json +31 -0
  575. package/.references/pi/packages/coding-agent/examples/extensions/with-deps/package.json +22 -0
  576. package/.references/pi/packages/coding-agent/examples/extensions/working-indicator.ts +123 -0
  577. package/.references/pi/packages/coding-agent/examples/extensions/working-message-test.ts +25 -0
  578. package/.references/pi/packages/coding-agent/examples/rpc-extension-ui.ts +632 -0
  579. package/.references/pi/packages/coding-agent/examples/sdk/01-minimal.ts +26 -0
  580. package/.references/pi/packages/coding-agent/examples/sdk/02-custom-model.ts +50 -0
  581. package/.references/pi/packages/coding-agent/examples/sdk/03-custom-prompt.ts +75 -0
  582. package/.references/pi/packages/coding-agent/examples/sdk/04-skills.ts +55 -0
  583. package/.references/pi/packages/coding-agent/examples/sdk/05-tools.ts +48 -0
  584. package/.references/pi/packages/coding-agent/examples/sdk/06-extensions.ts +99 -0
  585. package/.references/pi/packages/coding-agent/examples/sdk/07-context-files.ts +47 -0
  586. package/.references/pi/packages/coding-agent/examples/sdk/08-prompt-templates.ts +51 -0
  587. package/.references/pi/packages/coding-agent/examples/sdk/09-api-keys-and-oauth.ts +34 -0
  588. package/.references/pi/packages/coding-agent/examples/sdk/10-settings.ts +53 -0
  589. package/.references/pi/packages/coding-agent/examples/sdk/11-sessions.ts +52 -0
  590. package/.references/pi/packages/coding-agent/examples/sdk/12-full-control.ts +72 -0
  591. package/.references/pi/packages/coding-agent/examples/sdk/13-session-runtime.ts +67 -0
  592. package/.references/pi/packages/coding-agent/examples/sdk/README.md +140 -0
  593. package/.references/pi/packages/coding-agent/install-lock/package-lock.json +1849 -0
  594. package/.references/pi/packages/coding-agent/install-lock/package.json +18 -0
  595. package/.references/pi/packages/coding-agent/npm-shrinkwrap.json +1839 -0
  596. package/.references/pi/packages/coding-agent/package.json +100 -0
  597. package/.references/pi/packages/coding-agent/scripts/migrate-sessions.sh +93 -0
  598. package/.references/pi/packages/coding-agent/src/bun/cli.ts +15 -0
  599. package/.references/pi/packages/coding-agent/src/bun/register-bedrock.ts +4 -0
  600. package/.references/pi/packages/coding-agent/src/bun/restore-sandbox-env.ts +36 -0
  601. package/.references/pi/packages/coding-agent/src/cli/args.ts +390 -0
  602. package/.references/pi/packages/coding-agent/src/cli/config-selector.ts +56 -0
  603. package/.references/pi/packages/coding-agent/src/cli/file-processor.ts +87 -0
  604. package/.references/pi/packages/coding-agent/src/cli/initial-message.ts +43 -0
  605. package/.references/pi/packages/coding-agent/src/cli/list-models.ts +111 -0
  606. package/.references/pi/packages/coding-agent/src/cli/project-trust.ts +62 -0
  607. package/.references/pi/packages/coding-agent/src/cli/session-picker.ts +55 -0
  608. package/.references/pi/packages/coding-agent/src/cli/startup-ui.ts +239 -0
  609. package/.references/pi/packages/coding-agent/src/cli.ts +20 -0
  610. package/.references/pi/packages/coding-agent/src/config.ts +566 -0
  611. package/.references/pi/packages/coding-agent/src/core/agent-session-runtime.ts +438 -0
  612. package/.references/pi/packages/coding-agent/src/core/agent-session-services.ts +207 -0
  613. package/.references/pi/packages/coding-agent/src/core/agent-session.ts +3279 -0
  614. package/.references/pi/packages/coding-agent/src/core/auth-guidance.ts +25 -0
  615. package/.references/pi/packages/coding-agent/src/core/auth-storage.ts +271 -0
  616. package/.references/pi/packages/coding-agent/src/core/bash-executor.ts +156 -0
  617. package/.references/pi/packages/coding-agent/src/core/cache-stats.ts +164 -0
  618. package/.references/pi/packages/coding-agent/src/core/compaction/branch-summarization.ts +371 -0
  619. package/.references/pi/packages/coding-agent/src/core/compaction/compaction.ts +872 -0
  620. package/.references/pi/packages/coding-agent/src/core/compaction/index.ts +7 -0
  621. package/.references/pi/packages/coding-agent/src/core/compaction/utils.ts +170 -0
  622. package/.references/pi/packages/coding-agent/src/core/defaults.ts +3 -0
  623. package/.references/pi/packages/coding-agent/src/core/diagnostics.ts +15 -0
  624. package/.references/pi/packages/coding-agent/src/core/event-bus.ts +33 -0
  625. package/.references/pi/packages/coding-agent/src/core/exec.ts +107 -0
  626. package/.references/pi/packages/coding-agent/src/core/experimental.ts +3 -0
  627. package/.references/pi/packages/coding-agent/src/core/export-html/ansi-to-html.ts +258 -0
  628. package/.references/pi/packages/coding-agent/src/core/export-html/index.ts +316 -0
  629. package/.references/pi/packages/coding-agent/src/core/export-html/template.css +1066 -0
  630. package/.references/pi/packages/coding-agent/src/core/export-html/template.html +55 -0
  631. package/.references/pi/packages/coding-agent/src/core/export-html/template.js +1864 -0
  632. package/.references/pi/packages/coding-agent/src/core/export-html/tool-renderer.ts +172 -0
  633. package/.references/pi/packages/coding-agent/src/core/export-html/vendor/highlight.min.js +1213 -0
  634. package/.references/pi/packages/coding-agent/src/core/export-html/vendor/marked.min.js +78 -0
  635. package/.references/pi/packages/coding-agent/src/core/extensions/index.ts +184 -0
  636. package/.references/pi/packages/coding-agent/src/core/extensions/loader.ts +708 -0
  637. package/.references/pi/packages/coding-agent/src/core/extensions/runner.ts +1189 -0
  638. package/.references/pi/packages/coding-agent/src/core/extensions/types.ts +1674 -0
  639. package/.references/pi/packages/coding-agent/src/core/extensions/wrapper.ts +45 -0
  640. package/.references/pi/packages/coding-agent/src/core/footer-data-provider.ts +388 -0
  641. package/.references/pi/packages/coding-agent/src/core/http-dispatcher.ts +106 -0
  642. package/.references/pi/packages/coding-agent/src/core/index.ts +80 -0
  643. package/.references/pi/packages/coding-agent/src/core/keybindings.ts +370 -0
  644. package/.references/pi/packages/coding-agent/src/core/messages.ts +195 -0
  645. package/.references/pi/packages/coding-agent/src/core/model-config.ts +287 -0
  646. package/.references/pi/packages/coding-agent/src/core/model-registry.ts +126 -0
  647. package/.references/pi/packages/coding-agent/src/core/model-resolver.ts +705 -0
  648. package/.references/pi/packages/coding-agent/src/core/model-runtime.ts +566 -0
  649. package/.references/pi/packages/coding-agent/src/core/models-store.ts +57 -0
  650. package/.references/pi/packages/coding-agent/src/core/output-guard.ts +108 -0
  651. package/.references/pi/packages/coding-agent/src/core/package-manager.ts +2650 -0
  652. package/.references/pi/packages/coding-agent/src/core/project-trust.ts +96 -0
  653. package/.references/pi/packages/coding-agent/src/core/prompt-templates.ts +284 -0
  654. package/.references/pi/packages/coding-agent/src/core/provider-attribution.ts +97 -0
  655. package/.references/pi/packages/coding-agent/src/core/provider-composer.ts +548 -0
  656. package/.references/pi/packages/coding-agent/src/core/radius.ts +1 -0
  657. package/.references/pi/packages/coding-agent/src/core/remote-catalog-provider.ts +83 -0
  658. package/.references/pi/packages/coding-agent/src/core/resolve-config-value.ts +287 -0
  659. package/.references/pi/packages/coding-agent/src/core/resource-loader.ts +1039 -0
  660. package/.references/pi/packages/coding-agent/src/core/runtime-credentials.ts +48 -0
  661. package/.references/pi/packages/coding-agent/src/core/sdk.ts +393 -0
  662. package/.references/pi/packages/coding-agent/src/core/session-cwd.ts +59 -0
  663. package/.references/pi/packages/coding-agent/src/core/session-manager.ts +1623 -0
  664. package/.references/pi/packages/coding-agent/src/core/settings-manager.ts +1234 -0
  665. package/.references/pi/packages/coding-agent/src/core/skills.ts +487 -0
  666. package/.references/pi/packages/coding-agent/src/core/slash-commands.ts +42 -0
  667. package/.references/pi/packages/coding-agent/src/core/source-info.ts +40 -0
  668. package/.references/pi/packages/coding-agent/src/core/system-prompt.ts +162 -0
  669. package/.references/pi/packages/coding-agent/src/core/telemetry.ts +13 -0
  670. package/.references/pi/packages/coding-agent/src/core/timings.ts +50 -0
  671. package/.references/pi/packages/coding-agent/src/core/tools/bash.ts +470 -0
  672. package/.references/pi/packages/coding-agent/src/core/tools/edit-diff.ts +560 -0
  673. package/.references/pi/packages/coding-agent/src/core/tools/edit.ts +437 -0
  674. package/.references/pi/packages/coding-agent/src/core/tools/file-mutation-queue.ts +61 -0
  675. package/.references/pi/packages/coding-agent/src/core/tools/find.ts +374 -0
  676. package/.references/pi/packages/coding-agent/src/core/tools/grep.ts +385 -0
  677. package/.references/pi/packages/coding-agent/src/core/tools/index.ts +196 -0
  678. package/.references/pi/packages/coding-agent/src/core/tools/ls.ts +225 -0
  679. package/.references/pi/packages/coding-agent/src/core/tools/output-accumulator.ts +222 -0
  680. package/.references/pi/packages/coding-agent/src/core/tools/path-utils.ts +118 -0
  681. package/.references/pi/packages/coding-agent/src/core/tools/read.ts +351 -0
  682. package/.references/pi/packages/coding-agent/src/core/tools/render-utils.ts +85 -0
  683. package/.references/pi/packages/coding-agent/src/core/tools/tool-definition-wrapper.ts +45 -0
  684. package/.references/pi/packages/coding-agent/src/core/tools/truncate.ts +276 -0
  685. package/.references/pi/packages/coding-agent/src/core/tools/write.ts +267 -0
  686. package/.references/pi/packages/coding-agent/src/core/trust-manager.ts +244 -0
  687. package/.references/pi/packages/coding-agent/src/index.ts +394 -0
  688. package/.references/pi/packages/coding-agent/src/main.ts +857 -0
  689. package/.references/pi/packages/coding-agent/src/migrations.ts +315 -0
  690. package/.references/pi/packages/coding-agent/src/modes/index.ts +15 -0
  691. package/.references/pi/packages/coding-agent/src/modes/interactive/assets/clankolas.png +0 -0
  692. package/.references/pi/packages/coding-agent/src/modes/interactive/components/armin.ts +382 -0
  693. package/.references/pi/packages/coding-agent/src/modes/interactive/components/assistant-message.ts +180 -0
  694. package/.references/pi/packages/coding-agent/src/modes/interactive/components/bash-execution.ts +220 -0
  695. package/.references/pi/packages/coding-agent/src/modes/interactive/components/bordered-loader.ts +68 -0
  696. package/.references/pi/packages/coding-agent/src/modes/interactive/components/branch-summary-message.ts +58 -0
  697. package/.references/pi/packages/coding-agent/src/modes/interactive/components/compaction-summary-message.ts +59 -0
  698. package/.references/pi/packages/coding-agent/src/modes/interactive/components/config-selector.ts +942 -0
  699. package/.references/pi/packages/coding-agent/src/modes/interactive/components/countdown-timer.ts +39 -0
  700. package/.references/pi/packages/coding-agent/src/modes/interactive/components/custom-editor.ts +80 -0
  701. package/.references/pi/packages/coding-agent/src/modes/interactive/components/custom-entry.ts +62 -0
  702. package/.references/pi/packages/coding-agent/src/modes/interactive/components/custom-message.ts +99 -0
  703. package/.references/pi/packages/coding-agent/src/modes/interactive/components/daxnuts.ts +164 -0
  704. package/.references/pi/packages/coding-agent/src/modes/interactive/components/diff.ts +147 -0
  705. package/.references/pi/packages/coding-agent/src/modes/interactive/components/dynamic-border.ts +25 -0
  706. package/.references/pi/packages/coding-agent/src/modes/interactive/components/earendil-announcement.ts +53 -0
  707. package/.references/pi/packages/coding-agent/src/modes/interactive/components/extension-editor.ts +167 -0
  708. package/.references/pi/packages/coding-agent/src/modes/interactive/components/extension-input.ts +87 -0
  709. package/.references/pi/packages/coding-agent/src/modes/interactive/components/extension-selector.ts +112 -0
  710. package/.references/pi/packages/coding-agent/src/modes/interactive/components/first-time-setup.ts +145 -0
  711. package/.references/pi/packages/coding-agent/src/modes/interactive/components/footer.ts +245 -0
  712. package/.references/pi/packages/coding-agent/src/modes/interactive/components/index.ts +38 -0
  713. package/.references/pi/packages/coding-agent/src/modes/interactive/components/keybinding-hints.ts +48 -0
  714. package/.references/pi/packages/coding-agent/src/modes/interactive/components/login-dialog.ts +233 -0
  715. package/.references/pi/packages/coding-agent/src/modes/interactive/components/model-selector.ts +361 -0
  716. package/.references/pi/packages/coding-agent/src/modes/interactive/components/oauth-selector.ts +214 -0
  717. package/.references/pi/packages/coding-agent/src/modes/interactive/components/scoped-models-selector.ts +360 -0
  718. package/.references/pi/packages/coding-agent/src/modes/interactive/components/session-selector-search.ts +194 -0
  719. package/.references/pi/packages/coding-agent/src/modes/interactive/components/session-selector.ts +1031 -0
  720. package/.references/pi/packages/coding-agent/src/modes/interactive/components/settings-selector.ts +838 -0
  721. package/.references/pi/packages/coding-agent/src/modes/interactive/components/show-images-selector.ts +50 -0
  722. package/.references/pi/packages/coding-agent/src/modes/interactive/components/skill-invocation-message.ts +55 -0
  723. package/.references/pi/packages/coding-agent/src/modes/interactive/components/status-indicator.ts +114 -0
  724. package/.references/pi/packages/coding-agent/src/modes/interactive/components/theme-selector.ts +67 -0
  725. package/.references/pi/packages/coding-agent/src/modes/interactive/components/thinking-selector.ts +75 -0
  726. package/.references/pi/packages/coding-agent/src/modes/interactive/components/tool-execution.ts +377 -0
  727. package/.references/pi/packages/coding-agent/src/modes/interactive/components/tree-selector.ts +1427 -0
  728. package/.references/pi/packages/coding-agent/src/modes/interactive/components/trust-selector.ts +134 -0
  729. package/.references/pi/packages/coding-agent/src/modes/interactive/components/user-message-selector.ts +155 -0
  730. package/.references/pi/packages/coding-agent/src/modes/interactive/components/user-message.ts +57 -0
  731. package/.references/pi/packages/coding-agent/src/modes/interactive/components/visual-truncate.ts +50 -0
  732. package/.references/pi/packages/coding-agent/src/modes/interactive/interactive-mode.ts +6005 -0
  733. package/.references/pi/packages/coding-agent/src/modes/interactive/model-search.ts +21 -0
  734. package/.references/pi/packages/coding-agent/src/modes/interactive/theme/dark.json +87 -0
  735. package/.references/pi/packages/coding-agent/src/modes/interactive/theme/light.json +86 -0
  736. package/.references/pi/packages/coding-agent/src/modes/interactive/theme/theme-controller.ts +126 -0
  737. package/.references/pi/packages/coding-agent/src/modes/interactive/theme/theme-schema.json +340 -0
  738. package/.references/pi/packages/coding-agent/src/modes/interactive/theme/theme.ts +1294 -0
  739. package/.references/pi/packages/coding-agent/src/modes/print-mode.ts +159 -0
  740. package/.references/pi/packages/coding-agent/src/modes/rpc/jsonl.ts +58 -0
  741. package/.references/pi/packages/coding-agent/src/modes/rpc/rpc-client.ts +592 -0
  742. package/.references/pi/packages/coding-agent/src/modes/rpc/rpc-mode.ts +795 -0
  743. package/.references/pi/packages/coding-agent/src/modes/rpc/rpc-types.ts +281 -0
  744. package/.references/pi/packages/coding-agent/src/package-manager-cli.ts +887 -0
  745. package/.references/pi/packages/coding-agent/src/rpc-entry.ts +12 -0
  746. package/.references/pi/packages/coding-agent/src/utils/ansi.ts +60 -0
  747. package/.references/pi/packages/coding-agent/src/utils/changelog.ts +196 -0
  748. package/.references/pi/packages/coding-agent/src/utils/child-process.ts +137 -0
  749. package/.references/pi/packages/coding-agent/src/utils/clipboard-image.ts +300 -0
  750. package/.references/pi/packages/coding-agent/src/utils/clipboard-native.ts +33 -0
  751. package/.references/pi/packages/coding-agent/src/utils/clipboard.ts +141 -0
  752. package/.references/pi/packages/coding-agent/src/utils/deprecation.ts +14 -0
  753. package/.references/pi/packages/coding-agent/src/utils/exif-orientation.ts +183 -0
  754. package/.references/pi/packages/coding-agent/src/utils/frontmatter.ts +39 -0
  755. package/.references/pi/packages/coding-agent/src/utils/fs-watch.ts +30 -0
  756. package/.references/pi/packages/coding-agent/src/utils/git.ts +226 -0
  757. package/.references/pi/packages/coding-agent/src/utils/highlight-js-lib-index.d.ts +19 -0
  758. package/.references/pi/packages/coding-agent/src/utils/html.ts +51 -0
  759. package/.references/pi/packages/coding-agent/src/utils/image-convert.ts +49 -0
  760. package/.references/pi/packages/coding-agent/src/utils/image-process.ts +119 -0
  761. package/.references/pi/packages/coding-agent/src/utils/image-resize-core.ts +164 -0
  762. package/.references/pi/packages/coding-agent/src/utils/image-resize-worker.ts +42 -0
  763. package/.references/pi/packages/coding-agent/src/utils/image-resize.ts +123 -0
  764. package/.references/pi/packages/coding-agent/src/utils/json.ts +6 -0
  765. package/.references/pi/packages/coding-agent/src/utils/mime.ts +116 -0
  766. package/.references/pi/packages/coding-agent/src/utils/open-browser.ts +24 -0
  767. package/.references/pi/packages/coding-agent/src/utils/paths.ts +118 -0
  768. package/.references/pi/packages/coding-agent/src/utils/photon.ts +139 -0
  769. package/.references/pi/packages/coding-agent/src/utils/pi-user-agent.ts +4 -0
  770. package/.references/pi/packages/coding-agent/src/utils/shell.ts +225 -0
  771. package/.references/pi/packages/coding-agent/src/utils/sleep.ts +18 -0
  772. package/.references/pi/packages/coding-agent/src/utils/syntax-highlight.ts +146 -0
  773. package/.references/pi/packages/coding-agent/src/utils/tools-manager.ts +369 -0
  774. package/.references/pi/packages/coding-agent/src/utils/version-check.ts +80 -0
  775. package/.references/pi/packages/coding-agent/src/utils/windows-self-update.ts +84 -0
  776. package/.references/pi/packages/coding-agent/test/agent-session-auto-compaction-queue.test.ts +450 -0
  777. package/.references/pi/packages/coding-agent/test/agent-session-branching.test.ts +156 -0
  778. package/.references/pi/packages/coding-agent/test/agent-session-compaction.test.ts +208 -0
  779. package/.references/pi/packages/coding-agent/test/agent-session-concurrent.test.ts +629 -0
  780. package/.references/pi/packages/coding-agent/test/agent-session-dynamic-provider.test.ts +122 -0
  781. package/.references/pi/packages/coding-agent/test/agent-session-dynamic-tools.test.ts +185 -0
  782. package/.references/pi/packages/coding-agent/test/agent-session-retry.test.ts +322 -0
  783. package/.references/pi/packages/coding-agent/test/agent-session-runtime-events.test.ts +257 -0
  784. package/.references/pi/packages/coding-agent/test/agent-session-stats.test.ts +169 -0
  785. package/.references/pi/packages/coding-agent/test/agent-session-tree-navigation.test.ts +323 -0
  786. package/.references/pi/packages/coding-agent/test/ansi-utils.test.ts +110 -0
  787. package/.references/pi/packages/coding-agent/test/args.test.ts +441 -0
  788. package/.references/pi/packages/coding-agent/test/assistant-message.test.ts +130 -0
  789. package/.references/pi/packages/coding-agent/test/auth-storage.test.ts +217 -0
  790. package/.references/pi/packages/coding-agent/test/bash-close-hang-windows.test.ts +126 -0
  791. package/.references/pi/packages/coding-agent/test/bash-execution-width.test.ts +80 -0
  792. package/.references/pi/packages/coding-agent/test/block-images.test.ts +148 -0
  793. package/.references/pi/packages/coding-agent/test/cache-stats.test.ts +143 -0
  794. package/.references/pi/packages/coding-agent/test/changelog.test.ts +49 -0
  795. package/.references/pi/packages/coding-agent/test/clipboard-image-bmp-conversion.test.ts +88 -0
  796. package/.references/pi/packages/coding-agent/test/clipboard-image.test.ts +184 -0
  797. package/.references/pi/packages/coding-agent/test/clipboard-native.test.ts +32 -0
  798. package/.references/pi/packages/coding-agent/test/clipboard.test.ts +166 -0
  799. package/.references/pi/packages/coding-agent/test/compaction-extensions-example.test.ts +66 -0
  800. package/.references/pi/packages/coding-agent/test/compaction-extensions.test.ts +415 -0
  801. package/.references/pi/packages/coding-agent/test/compaction-serialization.test.ts +79 -0
  802. package/.references/pi/packages/coding-agent/test/compaction-summary-reasoning.test.ts +134 -0
  803. package/.references/pi/packages/coding-agent/test/compaction.test.ts +596 -0
  804. package/.references/pi/packages/coding-agent/test/config-value-migration.test.ts +178 -0
  805. package/.references/pi/packages/coding-agent/test/config.test.ts +437 -0
  806. package/.references/pi/packages/coding-agent/test/edit-tool-legacy-input.test.ts +116 -0
  807. package/.references/pi/packages/coding-agent/test/edit-tool-no-full-redraw.test.ts +235 -0
  808. package/.references/pi/packages/coding-agent/test/experimental.test.ts +44 -0
  809. package/.references/pi/packages/coding-agent/test/export-html-skill-block.test.ts +40 -0
  810. package/.references/pi/packages/coding-agent/test/export-html-whitespace.test.ts +42 -0
  811. package/.references/pi/packages/coding-agent/test/export-html-xss.test.ts +67 -0
  812. package/.references/pi/packages/coding-agent/test/extensions-discovery.test.ts +528 -0
  813. package/.references/pi/packages/coding-agent/test/extensions-input-event.test.ts +125 -0
  814. package/.references/pi/packages/coding-agent/test/extensions-runner.test.ts +986 -0
  815. package/.references/pi/packages/coding-agent/test/file-mutation-queue.test.ts +274 -0
  816. package/.references/pi/packages/coding-agent/test/first-time-setup-fork.test.ts +39 -0
  817. package/.references/pi/packages/coding-agent/test/first-time-setup.test.ts +95 -0
  818. package/.references/pi/packages/coding-agent/test/fixtures/assistant-message-with-thinking-code.json +33 -0
  819. package/.references/pi/packages/coding-agent/test/fixtures/before-compaction.jsonl +1003 -0
  820. package/.references/pi/packages/coding-agent/test/fixtures/empty-agent/.gitkeep +0 -0
  821. package/.references/pi/packages/coding-agent/test/fixtures/empty-cwd/.gitkeep +0 -0
  822. package/.references/pi/packages/coding-agent/test/fixtures/large-session.jsonl +1019 -0
  823. package/.references/pi/packages/coding-agent/test/fixtures/skills/consecutive-hyphens/SKILL.md +8 -0
  824. package/.references/pi/packages/coding-agent/test/fixtures/skills/disable-model-invocation/SKILL.md +9 -0
  825. package/.references/pi/packages/coding-agent/test/fixtures/skills/invalid-name-chars/SKILL.md +8 -0
  826. package/.references/pi/packages/coding-agent/test/fixtures/skills/invalid-yaml/SKILL.md +8 -0
  827. package/.references/pi/packages/coding-agent/test/fixtures/skills/long-name/SKILL.md +8 -0
  828. package/.references/pi/packages/coding-agent/test/fixtures/skills/missing-description/SKILL.md +7 -0
  829. package/.references/pi/packages/coding-agent/test/fixtures/skills/multiline-description/SKILL.md +11 -0
  830. package/.references/pi/packages/coding-agent/test/fixtures/skills/name-mismatch/SKILL.md +8 -0
  831. package/.references/pi/packages/coding-agent/test/fixtures/skills/nested/child-skill/SKILL.md +8 -0
  832. package/.references/pi/packages/coding-agent/test/fixtures/skills/no-frontmatter/SKILL.md +3 -0
  833. package/.references/pi/packages/coding-agent/test/fixtures/skills/root-skill-preferred/SKILL.md +3 -0
  834. package/.references/pi/packages/coding-agent/test/fixtures/skills/root-skill-preferred/nested-child/SKILL.md +3 -0
  835. package/.references/pi/packages/coding-agent/test/fixtures/skills/unknown-field/SKILL.md +10 -0
  836. package/.references/pi/packages/coding-agent/test/fixtures/skills/valid-skill/SKILL.md +8 -0
  837. package/.references/pi/packages/coding-agent/test/fixtures/skills-collision/first/calendar/SKILL.md +8 -0
  838. package/.references/pi/packages/coding-agent/test/fixtures/skills-collision/second/calendar/SKILL.md +8 -0
  839. package/.references/pi/packages/coding-agent/test/footer-data-provider.test.ts +265 -0
  840. package/.references/pi/packages/coding-agent/test/footer-width.test.ts +144 -0
  841. package/.references/pi/packages/coding-agent/test/format-resume-command.test.ts +135 -0
  842. package/.references/pi/packages/coding-agent/test/frontmatter.test.ts +60 -0
  843. package/.references/pi/packages/coding-agent/test/git-merge-and-resolve-extension.test.ts +206 -0
  844. package/.references/pi/packages/coding-agent/test/git-ssh-url.test.ts +91 -0
  845. package/.references/pi/packages/coding-agent/test/git-update.test.ts +484 -0
  846. package/.references/pi/packages/coding-agent/test/http-dispatcher.test.ts +53 -0
  847. package/.references/pi/packages/coding-agent/test/image-process.test.ts +53 -0
  848. package/.references/pi/packages/coding-agent/test/image-processing.test.ts +170 -0
  849. package/.references/pi/packages/coding-agent/test/image-resize-callers.test.ts +53 -0
  850. package/.references/pi/packages/coding-agent/test/initial-message.test.ts +48 -0
  851. package/.references/pi/packages/coding-agent/test/input-transform-streaming-example.test.ts +84 -0
  852. package/.references/pi/packages/coding-agent/test/interactive-mode-anthropic-warning.test.ts +89 -0
  853. package/.references/pi/packages/coding-agent/test/interactive-mode-clone-command.test.ts +72 -0
  854. package/.references/pi/packages/coding-agent/test/interactive-mode-compaction.test.ts +85 -0
  855. package/.references/pi/packages/coding-agent/test/interactive-mode-import-command.test.ts +143 -0
  856. package/.references/pi/packages/coding-agent/test/interactive-mode-startup-input.test.ts +72 -0
  857. package/.references/pi/packages/coding-agent/test/interactive-mode-status.test.ts +1123 -0
  858. package/.references/pi/packages/coding-agent/test/interactive-mode-suspend.test.ts +149 -0
  859. package/.references/pi/packages/coding-agent/test/keybindings-migration.test.ts +88 -0
  860. package/.references/pi/packages/coding-agent/test/max-thinking.test.ts +45 -0
  861. package/.references/pi/packages/coding-agent/test/model-registry.test.ts +1934 -0
  862. package/.references/pi/packages/coding-agent/test/model-resolver.test.ts +690 -0
  863. package/.references/pi/packages/coding-agent/test/model-runtime-auth-options.test.ts +256 -0
  864. package/.references/pi/packages/coding-agent/test/model-runtime-cloudflare-compat.test.ts +95 -0
  865. package/.references/pi/packages/coding-agent/test/model-runtime-modify-models-compat.test.ts +85 -0
  866. package/.references/pi/packages/coding-agent/test/model-runtime-test-utils.ts +25 -0
  867. package/.references/pi/packages/coding-agent/test/models-store.test.ts +51 -0
  868. package/.references/pi/packages/coding-agent/test/oauth-selector.test.ts +147 -0
  869. package/.references/pi/packages/coding-agent/test/package-command-paths.test.ts +746 -0
  870. package/.references/pi/packages/coding-agent/test/package-manager-ssh.test.ts +97 -0
  871. package/.references/pi/packages/coding-agent/test/package-manager.test.ts +2512 -0
  872. package/.references/pi/packages/coding-agent/test/path-utils.test.ts +174 -0
  873. package/.references/pi/packages/coding-agent/test/paths.test.ts +150 -0
  874. package/.references/pi/packages/coding-agent/test/pi-user-agent.test.ts +12 -0
  875. package/.references/pi/packages/coding-agent/test/plan-mode-extension.test.ts +167 -0
  876. package/.references/pi/packages/coding-agent/test/plan-mode-utils.test.ts +261 -0
  877. package/.references/pi/packages/coding-agent/test/print-mode.test.ts +142 -0
  878. package/.references/pi/packages/coding-agent/test/prompt-templates.test.ts +615 -0
  879. package/.references/pi/packages/coding-agent/test/radius.test.ts +151 -0
  880. package/.references/pi/packages/coding-agent/test/remote-catalog-provider.test.ts +118 -0
  881. package/.references/pi/packages/coding-agent/test/resolve-config-value.test.ts +121 -0
  882. package/.references/pi/packages/coding-agent/test/resource-loader.test.ts +739 -0
  883. package/.references/pi/packages/coding-agent/test/restore-sandbox-env.test.ts +77 -0
  884. package/.references/pi/packages/coding-agent/test/rpc-client-clone.test.ts +29 -0
  885. package/.references/pi/packages/coding-agent/test/rpc-client-process-exit.test.ts +38 -0
  886. package/.references/pi/packages/coding-agent/test/rpc-example.ts +86 -0
  887. package/.references/pi/packages/coding-agent/test/rpc-jsonl.test.ts +65 -0
  888. package/.references/pi/packages/coding-agent/test/rpc-prompt-response-semantics.test.ts +288 -0
  889. package/.references/pi/packages/coding-agent/test/rpc.test.ts +377 -0
  890. package/.references/pi/packages/coding-agent/test/runtime-credentials.test.ts +42 -0
  891. package/.references/pi/packages/coding-agent/test/sdk-codex-cache-probe-tool-loop.ts +497 -0
  892. package/.references/pi/packages/coding-agent/test/sdk-openrouter-attribution.test.ts +266 -0
  893. package/.references/pi/packages/coding-agent/test/sdk-session-manager.test.ts +95 -0
  894. package/.references/pi/packages/coding-agent/test/sdk-skills.test.ts +109 -0
  895. package/.references/pi/packages/coding-agent/test/sdk-stream-options.test.ts +188 -0
  896. package/.references/pi/packages/coding-agent/test/session-cwd.test.ts +91 -0
  897. package/.references/pi/packages/coding-agent/test/session-file-invalid.test.ts +65 -0
  898. package/.references/pi/packages/coding-agent/test/session-id-readonly.test.ts +190 -0
  899. package/.references/pi/packages/coding-agent/test/session-info-modified-timestamp.test.ts +83 -0
  900. package/.references/pi/packages/coding-agent/test/session-manager/build-context.test.ts +305 -0
  901. package/.references/pi/packages/coding-agent/test/session-manager/custom-session-id.test.ts +169 -0
  902. package/.references/pi/packages/coding-agent/test/session-manager/file-operations.test.ts +315 -0
  903. package/.references/pi/packages/coding-agent/test/session-manager/labels.test.ts +211 -0
  904. package/.references/pi/packages/coding-agent/test/session-manager/migration.test.ts +78 -0
  905. package/.references/pi/packages/coding-agent/test/session-manager/save-entry.test.ts +55 -0
  906. package/.references/pi/packages/coding-agent/test/session-manager/tree-traversal.test.ts +533 -0
  907. package/.references/pi/packages/coding-agent/test/session-selector-path-delete.test.ts +354 -0
  908. package/.references/pi/packages/coding-agent/test/session-selector-rename.test.ts +111 -0
  909. package/.references/pi/packages/coding-agent/test/session-selector-search.test.ts +195 -0
  910. package/.references/pi/packages/coding-agent/test/settings-manager-bug.test.ts +147 -0
  911. package/.references/pi/packages/coding-agent/test/settings-manager.test.ts +511 -0
  912. package/.references/pi/packages/coding-agent/test/skills.test.ts +432 -0
  913. package/.references/pi/packages/coding-agent/test/startup-session-name.test.ts +135 -0
  914. package/.references/pi/packages/coding-agent/test/status-indicator.test.ts +32 -0
  915. package/.references/pi/packages/coding-agent/test/stdout-cleanliness.test.ts +128 -0
  916. package/.references/pi/packages/coding-agent/test/streaming-render-debug.ts +97 -0
  917. package/.references/pi/packages/coding-agent/test/suite/README.md +16 -0
  918. package/.references/pi/packages/coding-agent/test/suite/agent-session-bash-persistence.test.ts +242 -0
  919. package/.references/pi/packages/coding-agent/test/suite/agent-session-compaction.test.ts +446 -0
  920. package/.references/pi/packages/coding-agent/test/suite/agent-session-model-extension.test.ts +373 -0
  921. package/.references/pi/packages/coding-agent/test/suite/agent-session-prompt.test.ts +398 -0
  922. package/.references/pi/packages/coding-agent/test/suite/agent-session-queue.test.ts +445 -0
  923. package/.references/pi/packages/coding-agent/test/suite/agent-session-retry-events.test.ts +364 -0
  924. package/.references/pi/packages/coding-agent/test/suite/agent-session-runtime.test.ts +609 -0
  925. package/.references/pi/packages/coding-agent/test/suite/harness.ts +219 -0
  926. package/.references/pi/packages/coding-agent/test/suite/lax-message-content.test.ts +162 -0
  927. package/.references/pi/packages/coding-agent/test/suite/regressions/1717-2113-agent-session-event-settlement.test.ts +95 -0
  928. package/.references/pi/packages/coding-agent/test/suite/regressions/2023-queued-slash-command-followup.test.ts +80 -0
  929. package/.references/pi/packages/coding-agent/test/suite/regressions/2753-reload-stale-resource-settings.test.ts +105 -0
  930. package/.references/pi/packages/coding-agent/test/suite/regressions/2781-skill-collision-precedence.test.ts +120 -0
  931. package/.references/pi/packages/coding-agent/test/suite/regressions/2791-fswatch-error-crash.test.ts +106 -0
  932. package/.references/pi/packages/coding-agent/test/suite/regressions/2835-tools-allowlist-filters-extension-tools.test.ts +94 -0
  933. package/.references/pi/packages/coding-agent/test/suite/regressions/2860-replaced-session-context.test.ts +279 -0
  934. package/.references/pi/packages/coding-agent/test/suite/regressions/3217-scoped-model-order.test.ts +104 -0
  935. package/.references/pi/packages/coding-agent/test/suite/regressions/3302-find-path-glob.test.ts +72 -0
  936. package/.references/pi/packages/coding-agent/test/suite/regressions/3303-find-nested-gitignore.test.ts +83 -0
  937. package/.references/pi/packages/coding-agent/test/suite/regressions/3317-network-connection-lost-retry.test.ts +33 -0
  938. package/.references/pi/packages/coding-agent/test/suite/regressions/3592-no-builtin-tools-keeps-extension-tools.test.ts +119 -0
  939. package/.references/pi/packages/coding-agent/test/suite/regressions/3616-settings-inmemory-reload.test.ts +86 -0
  940. package/.references/pi/packages/coding-agent/test/suite/regressions/3686-session-name-event.test.ts +61 -0
  941. package/.references/pi/packages/coding-agent/test/suite/regressions/3688-tree-cancel-compacting.test.ts +36 -0
  942. package/.references/pi/packages/coding-agent/test/suite/regressions/3982-message-end-cost-override.test.ts +56 -0
  943. package/.references/pi/packages/coding-agent/test/suite/regressions/4167-thinking-toggle-pending-tool-render.test.ts +182 -0
  944. package/.references/pi/packages/coding-agent/test/suite/regressions/5080-signal-shutdown-extension-cleanup.test.ts +185 -0
  945. package/.references/pi/packages/coding-agent/test/suite/regressions/5109-exclude-tools.test.ts +81 -0
  946. package/.references/pi/packages/coding-agent/test/suite/regressions/5208-late-bash-output.test.ts +29 -0
  947. package/.references/pi/packages/coding-agent/test/suite/regressions/5217-compaction-reason.test.ts +95 -0
  948. package/.references/pi/packages/coding-agent/test/suite/regressions/5303-bash-output-truncation.test.ts +79 -0
  949. package/.references/pi/packages/coding-agent/test/suite/regressions/5433-extension-oauth-prompt-input.test.ts +119 -0
  950. package/.references/pi/packages/coding-agent/test/suite/regressions/5596-missing-theme-export.test.ts +89 -0
  951. package/.references/pi/packages/coding-agent/test/suite/regressions/5661-uppercase-header-values.test.ts +91 -0
  952. package/.references/pi/packages/coding-agent/test/suite/regressions/5724-sigterm-signal-exit.test.ts +94 -0
  953. package/.references/pi/packages/coding-agent/test/suite/regressions/5868-rpc-unknown-command-id.test.ts +113 -0
  954. package/.references/pi/packages/coding-agent/test/suite/regressions/5943-session-start-notify.test.ts +515 -0
  955. package/.references/pi/packages/coding-agent/test/suite/regressions/5996-session-name-newlines.test.ts +40 -0
  956. package/.references/pi/packages/coding-agent/test/suite/regressions/6019-explicit-provider-retry-message.test.ts +31 -0
  957. package/.references/pi/packages/coding-agent/test/suite/regressions/6162-extension-active-tools-next-turn.test.ts +192 -0
  958. package/.references/pi/packages/coding-agent/test/suite/regressions/6260-inline-extension-naming.test.ts +99 -0
  959. package/.references/pi/packages/coding-agent/test/suite/regressions/6324-branch-summary-ambient-auth.test.ts +61 -0
  960. package/.references/pi/packages/coding-agent/test/suite/regressions/6363-agent-settled-event.test.ts +158 -0
  961. package/.references/pi/packages/coding-agent/test/suite/regressions/extension-factory-cache.test.ts +131 -0
  962. package/.references/pi/packages/coding-agent/test/suite/regressions/pre-prompt-compaction-no-continue.test.ts +75 -0
  963. package/.references/pi/packages/coding-agent/test/syntax-highlight.test.ts +76 -0
  964. package/.references/pi/packages/coding-agent/test/system-prompt.test.ts +114 -0
  965. package/.references/pi/packages/coding-agent/test/test-harness.test.ts +321 -0
  966. package/.references/pi/packages/coding-agent/test/test-harness.ts +463 -0
  967. package/.references/pi/packages/coding-agent/test/test-theme-colors.ts +246 -0
  968. package/.references/pi/packages/coding-agent/test/theme-detection.test.ts +133 -0
  969. package/.references/pi/packages/coding-agent/test/theme-export.test.ts +104 -0
  970. package/.references/pi/packages/coding-agent/test/theme-picker.test.ts +51 -0
  971. package/.references/pi/packages/coding-agent/test/tool-execution-component.test.ts +529 -0
  972. package/.references/pi/packages/coding-agent/test/tools.test.ts +1213 -0
  973. package/.references/pi/packages/coding-agent/test/tree-selector.test.ts +702 -0
  974. package/.references/pi/packages/coding-agent/test/trigger-compact-extension.test.ts +59 -0
  975. package/.references/pi/packages/coding-agent/test/truncate-to-width.test.ts +81 -0
  976. package/.references/pi/packages/coding-agent/test/trust-manager.test.ts +67 -0
  977. package/.references/pi/packages/coding-agent/test/trust-selector.test.ts +87 -0
  978. package/.references/pi/packages/coding-agent/test/user-message.test.ts +25 -0
  979. package/.references/pi/packages/coding-agent/test/utilities.ts +318 -0
  980. package/.references/pi/packages/coding-agent/test/version-check.test.ts +91 -0
  981. package/.references/pi/packages/coding-agent/tsconfig.build.json +17 -0
  982. package/.references/pi/packages/coding-agent/tsconfig.examples.json +16 -0
  983. package/.references/pi/packages/coding-agent/vitest.config.ts +38 -0
  984. package/.references/pi/packages/orchestrator/CHANGELOG.md +19 -0
  985. package/.references/pi/packages/orchestrator/README.md +11 -0
  986. package/.references/pi/packages/orchestrator/package.json +45 -0
  987. package/.references/pi/packages/orchestrator/src/cli.ts +161 -0
  988. package/.references/pi/packages/orchestrator/src/config.ts +69 -0
  989. package/.references/pi/packages/orchestrator/src/handler.ts +161 -0
  990. package/.references/pi/packages/orchestrator/src/index.ts +10 -0
  991. package/.references/pi/packages/orchestrator/src/ipc/client.ts +63 -0
  992. package/.references/pi/packages/orchestrator/src/ipc/protocol.ts +142 -0
  993. package/.references/pi/packages/orchestrator/src/ipc/server.ts +209 -0
  994. package/.references/pi/packages/orchestrator/src/radius.ts +440 -0
  995. package/.references/pi/packages/orchestrator/src/rpc-process.ts +201 -0
  996. package/.references/pi/packages/orchestrator/src/serve.ts +77 -0
  997. package/.references/pi/packages/orchestrator/src/storage.ts +70 -0
  998. package/.references/pi/packages/orchestrator/src/supervisor.ts +354 -0
  999. package/.references/pi/packages/orchestrator/src/types.ts +25 -0
  1000. package/.references/pi/packages/orchestrator/tsconfig.build.json +9 -0
  1001. package/.references/pi/packages/tui/CHANGELOG.md +1005 -0
  1002. package/.references/pi/packages/tui/README.md +791 -0
  1003. package/.references/pi/packages/tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  1004. package/.references/pi/packages/tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  1005. package/.references/pi/packages/tui/native/darwin/src/darwin-modifiers.c +70 -0
  1006. package/.references/pi/packages/tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  1007. package/.references/pi/packages/tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  1008. package/.references/pi/packages/tui/native/win32/src/win32-console-mode.c +53 -0
  1009. package/.references/pi/packages/tui/package.json +47 -0
  1010. package/.references/pi/packages/tui/src/autocomplete.ts +786 -0
  1011. package/.references/pi/packages/tui/src/components/box.ts +137 -0
  1012. package/.references/pi/packages/tui/src/components/cancellable-loader.ts +40 -0
  1013. package/.references/pi/packages/tui/src/components/editor.ts +2333 -0
  1014. package/.references/pi/packages/tui/src/components/image.ts +126 -0
  1015. package/.references/pi/packages/tui/src/components/input.ts +447 -0
  1016. package/.references/pi/packages/tui/src/components/loader.ts +92 -0
  1017. package/.references/pi/packages/tui/src/components/markdown.ts +858 -0
  1018. package/.references/pi/packages/tui/src/components/select-list.ts +229 -0
  1019. package/.references/pi/packages/tui/src/components/settings-list.ts +250 -0
  1020. package/.references/pi/packages/tui/src/components/spacer.ts +28 -0
  1021. package/.references/pi/packages/tui/src/components/text.ts +106 -0
  1022. package/.references/pi/packages/tui/src/components/truncated-text.ts +65 -0
  1023. package/.references/pi/packages/tui/src/editor-component.ts +74 -0
  1024. package/.references/pi/packages/tui/src/fuzzy.ts +137 -0
  1025. package/.references/pi/packages/tui/src/index.ts +114 -0
  1026. package/.references/pi/packages/tui/src/keybindings.ts +244 -0
  1027. package/.references/pi/packages/tui/src/keys.ts +1401 -0
  1028. package/.references/pi/packages/tui/src/kill-ring.ts +46 -0
  1029. package/.references/pi/packages/tui/src/native-modifiers.ts +59 -0
  1030. package/.references/pi/packages/tui/src/stdin-buffer.ts +434 -0
  1031. package/.references/pi/packages/tui/src/terminal-colors.ts +73 -0
  1032. package/.references/pi/packages/tui/src/terminal-image.ts +488 -0
  1033. package/.references/pi/packages/tui/src/terminal.ts +531 -0
  1034. package/.references/pi/packages/tui/src/tui.ts +1714 -0
  1035. package/.references/pi/packages/tui/src/undo-stack.ts +28 -0
  1036. package/.references/pi/packages/tui/src/utils.ts +1209 -0
  1037. package/.references/pi/packages/tui/src/word-navigation.ts +117 -0
  1038. package/.references/pi/packages/tui/test/autocomplete.test.ts +542 -0
  1039. package/.references/pi/packages/tui/test/bug-regression-isimageline-startswith-bug.test.ts +237 -0
  1040. package/.references/pi/packages/tui/test/chat-simple.ts +129 -0
  1041. package/.references/pi/packages/tui/test/editor.test.ts +4051 -0
  1042. package/.references/pi/packages/tui/test/fuzzy.test.ts +112 -0
  1043. package/.references/pi/packages/tui/test/image-test.ts +56 -0
  1044. package/.references/pi/packages/tui/test/input.test.ts +647 -0
  1045. package/.references/pi/packages/tui/test/key-tester.ts +123 -0
  1046. package/.references/pi/packages/tui/test/keybindings.test.ts +46 -0
  1047. package/.references/pi/packages/tui/test/keys.test.ts +622 -0
  1048. package/.references/pi/packages/tui/test/markdown.test.ts +1442 -0
  1049. package/.references/pi/packages/tui/test/overlay-non-capturing.test.ts +1202 -0
  1050. package/.references/pi/packages/tui/test/overlay-options.test.ts +541 -0
  1051. package/.references/pi/packages/tui/test/overlay-short-content.test.ts +61 -0
  1052. package/.references/pi/packages/tui/test/regression-overlay-cjk-boundary.test.ts +68 -0
  1053. package/.references/pi/packages/tui/test/regression-regional-indicator-width.test.ts +52 -0
  1054. package/.references/pi/packages/tui/test/select-list.test.ts +116 -0
  1055. package/.references/pi/packages/tui/test/stdin-buffer.test.ts +458 -0
  1056. package/.references/pi/packages/tui/test/tab-width.test.ts +87 -0
  1057. package/.references/pi/packages/tui/test/terminal-colors.test.ts +249 -0
  1058. package/.references/pi/packages/tui/test/terminal-image.test.ts +492 -0
  1059. package/.references/pi/packages/tui/test/terminal.test.ts +233 -0
  1060. package/.references/pi/packages/tui/test/test-themes.ts +38 -0
  1061. package/.references/pi/packages/tui/test/truncate-to-width.test.ts +76 -0
  1062. package/.references/pi/packages/tui/test/truncated-text.test.ts +129 -0
  1063. package/.references/pi/packages/tui/test/tui-cell-size-input.test.ts +81 -0
  1064. package/.references/pi/packages/tui/test/tui-overlay-style-leak.test.ts +80 -0
  1065. package/.references/pi/packages/tui/test/tui-render.test.ts +767 -0
  1066. package/.references/pi/packages/tui/test/tui-shrink.test.ts +44 -0
  1067. package/.references/pi/packages/tui/test/viewport-overwrite-repro.ts +106 -0
  1068. package/.references/pi/packages/tui/test/virtual-terminal.ts +218 -0
  1069. package/.references/pi/packages/tui/test/word-navigation.test.ts +191 -0
  1070. package/.references/pi/packages/tui/test/wrap-ansi.test.ts +266 -0
  1071. package/.references/pi/packages/tui/tsconfig.build.json +9 -0
  1072. package/.references/pi/packages/tui/vitest.config.ts +7 -0
  1073. package/.references/pi/pi-test.bat +14 -0
  1074. package/.references/pi/pi-test.ps1 +71 -0
  1075. package/.references/pi/pi-test.sh +57 -0
  1076. package/.references/pi/scripts/browser-smoke-entry.ts +61 -0
  1077. package/.references/pi/scripts/build-binaries.sh +245 -0
  1078. package/.references/pi/scripts/check-browser-smoke.mjs +36 -0
  1079. package/.references/pi/scripts/check-lockfile-commit.mjs +120 -0
  1080. package/.references/pi/scripts/check-pinned-deps.mjs +63 -0
  1081. package/.references/pi/scripts/check-ts-relative-imports.mjs +74 -0
  1082. package/.references/pi/scripts/cost.ts +183 -0
  1083. package/.references/pi/scripts/edit-tool-stats.mjs +834 -0
  1084. package/.references/pi/scripts/generate-coding-agent-install-lock.mjs +439 -0
  1085. package/.references/pi/scripts/generate-coding-agent-shrinkwrap.mjs +365 -0
  1086. package/.references/pi/scripts/local-release.mjs +284 -0
  1087. package/.references/pi/scripts/profile-coding-agent-node.mjs +639 -0
  1088. package/.references/pi/scripts/publish-model-catalog.mjs +295 -0
  1089. package/.references/pi/scripts/publish.mjs +125 -0
  1090. package/.references/pi/scripts/read-tool-stats.mjs +505 -0
  1091. package/.references/pi/scripts/release-notes.mjs +364 -0
  1092. package/.references/pi/scripts/release.mjs +207 -0
  1093. package/.references/pi/scripts/repro-5893-wsl-bash.mjs +55 -0
  1094. package/.references/pi/scripts/session-context-stats.mjs +405 -0
  1095. package/.references/pi/scripts/session-transcripts.ts +406 -0
  1096. package/.references/pi/scripts/stats.ts +233 -0
  1097. package/.references/pi/scripts/sync-versions.js +96 -0
  1098. package/.references/pi/scripts/tool-stats.ts +232 -0
  1099. package/.references/pi/scripts/update-source-imports-to-ts.sh +10 -0
  1100. package/.references/pi/test.sh +81 -0
  1101. package/.references/pi/tsconfig.base.json +25 -0
  1102. package/.references/pi/tsconfig.json +27 -0
  1103. package/AGENTS.md +8 -0
  1104. package/Cargo.lock +5169 -0
  1105. package/Cargo.toml +39 -0
  1106. package/LICENSE +21 -0
  1107. package/README.md +43 -0
  1108. package/aliases/pi-rusty/Cargo.toml +19 -0
  1109. package/aliases/pi-rusty/LICENSE +21 -0
  1110. package/aliases/pi-rusty/README.md +10 -0
  1111. package/aliases/pi-rusty/src/main.rs +8 -0
  1112. package/bun.lock +687 -0
  1113. package/crates/pi/Cargo.toml +66 -0
  1114. package/crates/pi/LICENSE +21 -0
  1115. package/crates/pi/assets/export-html/template.css +1066 -0
  1116. package/crates/pi/assets/export-html/template.html +55 -0
  1117. package/crates/pi/assets/export-html/template.js +1864 -0
  1118. package/crates/pi/assets/export-html/vendor/dark.json +29 -0
  1119. package/crates/pi/assets/export-html/vendor/highlight.min.js +1213 -0
  1120. package/crates/pi/assets/export-html/vendor/light.json +6 -0
  1121. package/crates/pi/assets/export-html/vendor/marked.min.js +78 -0
  1122. package/crates/pi/src/cli/args.rs +889 -0
  1123. package/crates/pi/src/cli/bootstrap.rs +2134 -0
  1124. package/crates/pi/src/cli/entry.rs +1332 -0
  1125. package/crates/pi/src/cli/help.rs +326 -0
  1126. package/crates/pi/src/cli/mod.rs +22 -0
  1127. package/crates/pi/src/cli/package_manager_cli.rs +1515 -0
  1128. package/crates/pi/src/core/agent_session/bash.rs +453 -0
  1129. package/crates/pi/src/core/agent_session/compaction.rs +1804 -0
  1130. package/crates/pi/src/core/agent_session/events.rs +621 -0
  1131. package/crates/pi/src/core/agent_session/extension.rs +2146 -0
  1132. package/crates/pi/src/core/agent_session/extension_runner.rs +471 -0
  1133. package/crates/pi/src/core/agent_session/mod.rs +1768 -0
  1134. package/crates/pi/src/core/agent_session/model.rs +775 -0
  1135. package/crates/pi/src/core/agent_session/persistence.rs +265 -0
  1136. package/crates/pi/src/core/agent_session/prompt.rs +2289 -0
  1137. package/crates/pi/src/core/agent_session/retry.rs +448 -0
  1138. package/crates/pi/src/core/agent_session/stats.rs +302 -0
  1139. package/crates/pi/src/core/agent_session/subscribe.rs +450 -0
  1140. package/crates/pi/src/core/agent_session/tools.rs +515 -0
  1141. package/crates/pi/src/core/agent_session/tree.rs +1219 -0
  1142. package/crates/pi/src/core/agent_session_runtime.rs +1612 -0
  1143. package/crates/pi/src/core/agent_session_services.rs +1613 -0
  1144. package/crates/pi/src/core/compaction/branch.rs +767 -0
  1145. package/crates/pi/src/core/compaction/mod.rs +1955 -0
  1146. package/crates/pi/src/core/compaction/utils.rs +357 -0
  1147. package/crates/pi/src/core/config.rs +1102 -0
  1148. package/crates/pi/src/core/config_value.rs +179 -0
  1149. package/crates/pi/src/core/experimental.rs +29 -0
  1150. package/crates/pi/src/core/export_html/ansi_to_html.rs +279 -0
  1151. package/crates/pi/src/core/export_html/mod.rs +787 -0
  1152. package/crates/pi/src/core/export_html/session_data.rs +149 -0
  1153. package/crates/pi/src/core/extension_host/acquire/tests.rs +579 -0
  1154. package/crates/pi/src/core/extension_host/acquire.rs +431 -0
  1155. package/crates/pi/src/core/extension_host/tests.rs +2353 -0
  1156. package/crates/pi/src/core/extension_host.rs +2541 -0
  1157. package/crates/pi/src/core/lockfile.rs +913 -0
  1158. package/crates/pi/src/core/messages.rs +706 -0
  1159. package/crates/pi/src/core/migrations.rs +828 -0
  1160. package/crates/pi/src/core/mod.rs +28 -0
  1161. package/crates/pi/src/core/model_resolver.rs +1473 -0
  1162. package/crates/pi/src/core/model_runtime.rs +2720 -0
  1163. package/crates/pi/src/core/output_guard.rs +977 -0
  1164. package/crates/pi/src/core/package_manager/tests.rs +1304 -0
  1165. package/crates/pi/src/core/package_manager.rs +2015 -0
  1166. package/crates/pi/src/core/platform/clipboard.rs +702 -0
  1167. package/crates/pi/src/core/platform/command.rs +223 -0
  1168. package/crates/pi/src/core/platform/debug_dump.rs +274 -0
  1169. package/crates/pi/src/core/platform/external_editor.rs +294 -0
  1170. package/crates/pi/src/core/platform/first_run.rs +120 -0
  1171. package/crates/pi/src/core/platform/image.rs +237 -0
  1172. package/crates/pi/src/core/platform/mod.rs +27 -0
  1173. package/crates/pi/src/core/platform/open_browser.rs +187 -0
  1174. package/crates/pi/src/core/platform/release_packaging.rs +785 -0
  1175. package/crates/pi/src/core/resources/diagnostics.rs +148 -0
  1176. package/crates/pi/src/core/resources/discovery.rs +2570 -0
  1177. package/crates/pi/src/core/resources/frontmatter.rs +205 -0
  1178. package/crates/pi/src/core/resources/mod.rs +1402 -0
  1179. package/crates/pi/src/core/resources/prompts.rs +510 -0
  1180. package/crates/pi/src/core/resources/skills.rs +981 -0
  1181. package/crates/pi/src/core/resources/slash.rs +199 -0
  1182. package/crates/pi/src/core/resources/source_info.rs +141 -0
  1183. package/crates/pi/src/core/resources/themes.rs +297 -0
  1184. package/crates/pi/src/core/session_transfer.rs +390 -0
  1185. package/crates/pi/src/core/sessions/context.rs +657 -0
  1186. package/crates/pi/src/core/sessions/cwd.rs +176 -0
  1187. package/crates/pi/src/core/sessions/entries.rs +1364 -0
  1188. package/crates/pi/src/core/sessions/list.rs +610 -0
  1189. package/crates/pi/src/core/sessions/mod.rs +2117 -0
  1190. package/crates/pi/src/core/settings.rs +3794 -0
  1191. package/crates/pi/src/core/share.rs +577 -0
  1192. package/crates/pi/src/core/system_prompt.rs +654 -0
  1193. package/crates/pi/src/core/tools/bash.rs +1574 -0
  1194. package/crates/pi/src/core/tools/edit.rs +919 -0
  1195. package/crates/pi/src/core/tools/edit_diff.rs +1150 -0
  1196. package/crates/pi/src/core/tools/find.rs +652 -0
  1197. package/crates/pi/src/core/tools/grep.rs +965 -0
  1198. package/crates/pi/src/core/tools/ls.rs +545 -0
  1199. package/crates/pi/src/core/tools/mod.rs +312 -0
  1200. package/crates/pi/src/core/tools/mutation_queue.rs +392 -0
  1201. package/crates/pi/src/core/tools/output_accumulator.rs +689 -0
  1202. package/crates/pi/src/core/tools/path_utils.rs +517 -0
  1203. package/crates/pi/src/core/tools/read.rs +1413 -0
  1204. package/crates/pi/src/core/tools/truncate.rs +644 -0
  1205. package/crates/pi/src/core/tools/write.rs +524 -0
  1206. package/crates/pi/src/core/trust.rs +1414 -0
  1207. package/crates/pi/src/core/update/changelog.rs +583 -0
  1208. package/crates/pi/src/core/update/deprecation.rs +148 -0
  1209. package/crates/pi/src/core/update/mod.rs +8 -0
  1210. package/crates/pi/src/core/update/self_update.rs +1327 -0
  1211. package/crates/pi/src/core/update/user_agent.rs +59 -0
  1212. package/crates/pi/src/core/update/version_check.rs +511 -0
  1213. package/crates/pi/src/core/update/windows_quarantine.rs +262 -0
  1214. package/crates/pi/src/lib.rs +23 -0
  1215. package/crates/pi/src/main.rs +13 -0
  1216. package/crates/pi/src/modes/interactive/footer.rs +260 -0
  1217. package/crates/pi/src/modes/interactive/header.rs +83 -0
  1218. package/crates/pi/src/modes/interactive/input.rs +923 -0
  1219. package/crates/pi/src/modes/interactive/messages.rs +640 -0
  1220. package/crates/pi/src/modes/interactive/mod.rs +51 -0
  1221. package/crates/pi/src/modes/interactive/progress.rs +181 -0
  1222. package/crates/pi/src/modes/interactive/runtime.rs +7227 -0
  1223. package/crates/pi/src/modes/interactive/selectors.rs +146 -0
  1224. package/crates/pi/src/modes/interactive/snapshots/pi__modes__interactive__tests__bash_state_widths.snap +138 -0
  1225. package/crates/pi/src/modes/interactive/snapshots/pi__modes__interactive__tests__compact_state_widths.snap +137 -0
  1226. package/crates/pi/src/modes/interactive/snapshots/pi__modes__interactive__tests__custom_branch_skill_messages.snap +145 -0
  1227. package/crates/pi/src/modes/interactive/snapshots/pi__modes__interactive__tests__empty_state_widths.snap +136 -0
  1228. package/crates/pi/src/modes/interactive/snapshots/pi__modes__interactive__tests__error_state_widths.snap +139 -0
  1229. package/crates/pi/src/modes/interactive/snapshots/pi__modes__interactive__tests__loading_state_widths.snap +136 -0
  1230. package/crates/pi/src/modes/interactive/snapshots/pi__modes__interactive__tests__queue_state_widths.snap +139 -0
  1231. package/crates/pi/src/modes/interactive/snapshots/pi__modes__interactive__tests__retry_state_widths.snap +136 -0
  1232. package/crates/pi/src/modes/interactive/snapshots/pi__modes__interactive__tests__streaming_state_widths.snap +137 -0
  1233. package/crates/pi/src/modes/interactive/snapshots/pi__modes__interactive__tests__user_assistant_messages.snap +145 -0
  1234. package/crates/pi/src/modes/interactive/startup.rs +194 -0
  1235. package/crates/pi/src/modes/interactive/state.rs +759 -0
  1236. package/crates/pi/src/modes/interactive/status.rs +70 -0
  1237. package/crates/pi/src/modes/interactive/tests.rs +945 -0
  1238. package/crates/pi/src/modes/interactive/theme.rs +1402 -0
  1239. package/crates/pi/src/modes/interactive/tool_renderer.rs +152 -0
  1240. package/crates/pi/src/modes/interactive/view.rs +511 -0
  1241. package/crates/pi/src/modes/mod.rs +6 -0
  1242. package/crates/pi/src/modes/print/input.rs +415 -0
  1243. package/crates/pi/src/modes/print/json.rs +195 -0
  1244. package/crates/pi/src/modes/print/mod.rs +403 -0
  1245. package/crates/pi/src/modes/print/text.rs +412 -0
  1246. package/crates/pi/src/modes/rpc/extension_ui.rs +361 -0
  1247. package/crates/pi/src/modes/rpc/jsonl.rs +429 -0
  1248. package/crates/pi/src/modes/rpc/mod.rs +19 -0
  1249. package/crates/pi/src/modes/rpc/mode_host.rs +466 -0
  1250. package/crates/pi/src/modes/rpc/server.rs +3037 -0
  1251. package/crates/pi/src/modes/rpc/types.rs +2999 -0
  1252. package/crates/pi/src/modes/run.rs +636 -0
  1253. package/crates/pi/tests/fixtures/fake_extension_host.rs +104 -0
  1254. package/crates/pi/tests/fixtures/tool-schemas/bash.json +16 -0
  1255. package/crates/pi/tests/fixtures/tool-schemas/edit.json +34 -0
  1256. package/crates/pi/tests/fixtures/tool-schemas/find.json +20 -0
  1257. package/crates/pi/tests/fixtures/tool-schemas/grep.json +36 -0
  1258. package/crates/pi/tests/fixtures/tool-schemas/ls.json +13 -0
  1259. package/crates/pi/tests/fixtures/tool-schemas/read.json +20 -0
  1260. package/crates/pi/tests/fixtures/tool-schemas/write.json +17 -0
  1261. package/crates/pi-agent/Cargo.toml +30 -0
  1262. package/crates/pi-agent/LICENSE +21 -0
  1263. package/crates/pi-agent/src/agent.rs +1780 -0
  1264. package/crates/pi-agent/src/bus.rs +770 -0
  1265. package/crates/pi-agent/src/config.rs +445 -0
  1266. package/crates/pi-agent/src/drain.rs +941 -0
  1267. package/crates/pi-agent/src/error.rs +61 -0
  1268. package/crates/pi-agent/src/event.rs +270 -0
  1269. package/crates/pi-agent/src/lib.rs +49 -0
  1270. package/crates/pi-agent/src/message.rs +296 -0
  1271. package/crates/pi-agent/src/queue.rs +222 -0
  1272. package/crates/pi-agent/src/run.rs +1870 -0
  1273. package/crates/pi-agent/src/schedule.rs +2153 -0
  1274. package/crates/pi-agent/src/state.rs +377 -0
  1275. package/crates/pi-agent/src/tool.rs +496 -0
  1276. package/crates/pi-ai/Cargo.toml +45 -0
  1277. package/crates/pi-ai/LICENSE +21 -0
  1278. package/crates/pi-ai/data/builtin-models.json +25193 -0
  1279. package/crates/pi-ai/src/auth/config_value.rs +598 -0
  1280. package/crates/pi-ai/src/auth/context.rs +215 -0
  1281. package/crates/pi-ai/src/auth/credential_store.rs +318 -0
  1282. package/crates/pi-ai/src/auth/env_keys.rs +647 -0
  1283. package/crates/pi-ai/src/auth/error.rs +194 -0
  1284. package/crates/pi-ai/src/auth/file_store.rs +1179 -0
  1285. package/crates/pi-ai/src/auth/http.rs +526 -0
  1286. package/crates/pi-ai/src/auth/mod.rs +29 -0
  1287. package/crates/pi-ai/src/auth/oauth/anthropic.rs +1482 -0
  1288. package/crates/pi-ai/src/auth/oauth/callback_server.rs +584 -0
  1289. package/crates/pi-ai/src/auth/oauth/device_code.rs +573 -0
  1290. package/crates/pi-ai/src/auth/oauth/github_copilot.rs +1527 -0
  1291. package/crates/pi-ai/src/auth/oauth/mod.rs +26 -0
  1292. package/crates/pi-ai/src/auth/oauth/openai_codex.rs +2005 -0
  1293. package/crates/pi-ai/src/auth/oauth/page.rs +159 -0
  1294. package/crates/pi-ai/src/auth/oauth/pkce.rs +145 -0
  1295. package/crates/pi-ai/src/auth/oauth/radius.rs +1685 -0
  1296. package/crates/pi-ai/src/auth/oauth/xai.rs +989 -0
  1297. package/crates/pi-ai/src/auth/resolve.rs +701 -0
  1298. package/crates/pi-ai/src/auth/runtime_credentials.rs +226 -0
  1299. package/crates/pi-ai/src/auth/types.rs +534 -0
  1300. package/crates/pi-ai/src/catalog/merge.rs +316 -0
  1301. package/crates/pi-ai/src/catalog/mod.rs +485 -0
  1302. package/crates/pi-ai/src/lib.rs +13 -0
  1303. package/crates/pi-ai/src/models_store.rs +974 -0
  1304. package/crates/pi-ai/src/provider.rs +285 -0
  1305. package/crates/pi-ai/src/providers/anthropic_messages.rs +1473 -0
  1306. package/crates/pi-ai/src/providers/azure_openai_responses.rs +559 -0
  1307. package/crates/pi-ai/src/providers/bedrock_converse_stream.rs +2430 -0
  1308. package/crates/pi-ai/src/providers/google_generative_ai.rs +622 -0
  1309. package/crates/pi-ai/src/providers/google_vertex.rs +1565 -0
  1310. package/crates/pi-ai/src/providers/mistral_conversations.rs +1245 -0
  1311. package/crates/pi-ai/src/providers/mod.rs +35 -0
  1312. package/crates/pi-ai/src/providers/openai_codex_responses.rs +2003 -0
  1313. package/crates/pi-ai/src/providers/openai_completions.rs +1873 -0
  1314. package/crates/pi-ai/src/providers/openai_responses.rs +757 -0
  1315. package/crates/pi-ai/src/providers/pi_messages.rs +1235 -0
  1316. package/crates/pi-ai/src/providers/registry.rs +923 -0
  1317. package/crates/pi-ai/src/providers/shared/google.rs +1193 -0
  1318. package/crates/pi-ai/src/providers/shared/responses.rs +1873 -0
  1319. package/crates/pi-ai/src/providers/shared.rs +671 -0
  1320. package/crates/pi-ai/src/providers/stream_state.rs +683 -0
  1321. package/crates/pi-ai/src/providers/transport.rs +488 -0
  1322. package/crates/pi-ai/src/text.rs +322 -0
  1323. package/crates/pi-ai/src/types.rs +1050 -0
  1324. package/crates/pi-ai/tests/conformance_anthropic_mistral.rs +664 -0
  1325. package/crates/pi-ai/tests/conformance_bedrock_pi_messages.rs +908 -0
  1326. package/crates/pi-ai/tests/conformance_codex.rs +815 -0
  1327. package/crates/pi-ai/tests/conformance_google.rs +743 -0
  1328. package/crates/pi-ai/tests/conformance_openai.rs +607 -0
  1329. package/crates/pi-ai/tests/fixtures/provenance/anthropic-sse-parsing.test.ts +275 -0
  1330. package/crates/pi-ai/tests/fixtures/provenance/mistral-reasoning-mode.test.ts +97 -0
  1331. package/crates/pi-ai/tests/fixtures/provenance/upstream/src/api/anthropic-messages.ts +1313 -0
  1332. package/crates/pi-ai/tests/fixtures/provenance/upstream/src/api/bedrock-converse-stream.ts +1091 -0
  1333. package/crates/pi-ai/tests/fixtures/provenance/upstream/src/api/google-generative-ai.ts +509 -0
  1334. package/crates/pi-ai/tests/fixtures/provenance/upstream/src/api/google-shared.ts +350 -0
  1335. package/crates/pi-ai/tests/fixtures/provenance/upstream/src/api/mistral-conversations.ts +664 -0
  1336. package/crates/pi-ai/tests/fixtures/provenance/upstream/src/api/openai-completions.ts +1355 -0
  1337. package/crates/pi-ai/tests/fixtures/provenance/upstream/src/api/openai-responses-shared.ts +613 -0
  1338. package/crates/pi-ai/tests/fixtures/provenance/upstream/src/api/openai-responses.ts +318 -0
  1339. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/abort.test.ts +303 -0
  1340. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/azure-openai-base-url.test.ts +175 -0
  1341. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/bedrock-endpoint-resolution.test.ts +208 -0
  1342. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/bedrock-thinking-payload.test.ts +246 -0
  1343. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/google-vertex-api-key-resolution.test.ts +223 -0
  1344. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/openai-codex-stream.test.ts +2028 -0
  1345. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/openai-completions-prompt-cache.test.ts +264 -0
  1346. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/openai-completions-retry.test.ts +86 -0
  1347. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/openai-completions-thinking-as-text.test.ts +218 -0
  1348. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/openai-completions-tool-choice.test.ts +1716 -0
  1349. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/openai-responses-partial-json-cleanup.test.ts +106 -0
  1350. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/openai-responses-terminal-event.test.ts +233 -0
  1351. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/pi-messages.test.ts +243 -0
  1352. package/crates/pi-ai/tests/fixtures/provenance/upstream/test/provider-error-body-passthrough.test.ts +78 -0
  1353. package/crates/pi-ai/tests/fixtures/providers/anthropic-messages/cases.jsonl +4 -0
  1354. package/crates/pi-ai/tests/fixtures/providers/azure-openai-responses/cases.jsonl +2 -0
  1355. package/crates/pi-ai/tests/fixtures/providers/bedrock-converse-stream/cases.jsonl +8 -0
  1356. package/crates/pi-ai/tests/fixtures/providers/google-generative-ai/cases.jsonl +6 -0
  1357. package/crates/pi-ai/tests/fixtures/providers/google-vertex/cases.jsonl +6 -0
  1358. package/crates/pi-ai/tests/fixtures/providers/mistral-conversations/cases.jsonl +4 -0
  1359. package/crates/pi-ai/tests/fixtures/providers/openai-codex-responses/cases.jsonl +7 -0
  1360. package/crates/pi-ai/tests/fixtures/providers/openai-completions/cases.jsonl +9 -0
  1361. package/crates/pi-ai/tests/fixtures/providers/openai-responses/cases.jsonl +5 -0
  1362. package/crates/pi-ai/tests/fixtures/providers/pi-messages/cases.jsonl +8 -0
  1363. package/crates/pi-ai/tests/provider_registry_contract.rs +85 -0
  1364. package/crates/pi-ai/tests/support/golden.rs +191 -0
  1365. package/crates/pi-ai/tests/support/http.rs +525 -0
  1366. package/crates/pi-ai/tests/support/mod.rs +4 -0
  1367. package/crates/pi-ext/Cargo.toml +34 -0
  1368. package/crates/pi-ext/LICENSE +21 -0
  1369. package/crates/pi-ext/src/adapters.rs +1915 -0
  1370. package/crates/pi-ext/src/client.rs +1883 -0
  1371. package/crates/pi-ext/src/host.rs +318 -0
  1372. package/crates/pi-ext/src/lib.rs +10 -0
  1373. package/crates/pi-ext/src/protocol.rs +1880 -0
  1374. package/crates/pi-ext/src/sanitize.rs +524 -0
  1375. package/crates/pi-ext/src/test_support.rs +93 -0
  1376. package/crates/pi-ext/tests/fixtures/fake_jsonl_host.rs +35 -0
  1377. package/crates/pi-ext/tests/fixtures/protocol/frames.jsonl +30 -0
  1378. package/crates/pi-ext/tests/scaling.rs +530 -0
  1379. package/crates/pi-tui/Cargo.toml +56 -0
  1380. package/crates/pi-tui/LICENSE +21 -0
  1381. package/crates/pi-tui/src/bin/pi_tui_pty_fixture.rs +722 -0
  1382. package/crates/pi-tui/src/component.rs +101 -0
  1383. package/crates/pi-tui/src/components/editor/mod.rs +2626 -0
  1384. package/crates/pi-tui/src/components/editor/paste.rs +406 -0
  1385. package/crates/pi-tui/src/components/editor/state.rs +240 -0
  1386. package/crates/pi-tui/src/components/image.rs +352 -0
  1387. package/crates/pi-tui/src/components/input.rs +742 -0
  1388. package/crates/pi-tui/src/components/loader.rs +364 -0
  1389. package/crates/pi-tui/src/components/markdown.rs +1154 -0
  1390. package/crates/pi-tui/src/components/mod.rs +30 -0
  1391. package/crates/pi-tui/src/components/padded.rs +212 -0
  1392. package/crates/pi-tui/src/components/select_list.rs +464 -0
  1393. package/crates/pi-tui/src/components/settings_list.rs +597 -0
  1394. package/crates/pi-tui/src/components/spacer.rs +67 -0
  1395. package/crates/pi-tui/src/components/text.rs +225 -0
  1396. package/crates/pi-tui/src/components/truncated_text.rs +137 -0
  1397. package/crates/pi-tui/src/components/util.rs +324 -0
  1398. package/crates/pi-tui/src/editor_support/autocomplete.rs +1039 -0
  1399. package/crates/pi-tui/src/editor_support/history.rs +248 -0
  1400. package/crates/pi-tui/src/editor_support/kill_ring.rs +139 -0
  1401. package/crates/pi-tui/src/editor_support/mod.rs +29 -0
  1402. package/crates/pi-tui/src/editor_support/undo.rs +73 -0
  1403. package/crates/pi-tui/src/editor_support/word_nav.rs +412 -0
  1404. package/crates/pi-tui/src/editor_support/wrap.rs +317 -0
  1405. package/crates/pi-tui/src/focus.rs +564 -0
  1406. package/crates/pi-tui/src/frame.rs +163 -0
  1407. package/crates/pi-tui/src/fuzzy.rs +221 -0
  1408. package/crates/pi-tui/src/image.rs +749 -0
  1409. package/crates/pi-tui/src/keybindings.rs +602 -0
  1410. package/crates/pi-tui/src/keys.rs +1242 -0
  1411. package/crates/pi-tui/src/layout.rs +563 -0
  1412. package/crates/pi-tui/src/lib.rs +16 -0
  1413. package/crates/pi-tui/src/link.rs +408 -0
  1414. package/crates/pi-tui/src/overlay.rs +895 -0
  1415. package/crates/pi-tui/src/terminal/backend.rs +348 -0
  1416. package/crates/pi-tui/src/terminal/caps.rs +237 -0
  1417. package/crates/pi-tui/src/terminal/guard.rs +476 -0
  1418. package/crates/pi-tui/src/terminal/input.rs +266 -0
  1419. package/crates/pi-tui/src/terminal/mod.rs +31 -0
  1420. package/crates/pi-tui/src/terminal/probe.rs +498 -0
  1421. package/crates/pi-tui/src/terminal/sink.rs +128 -0
  1422. package/crates/pi-tui/src/terminal/writer.rs +1541 -0
  1423. package/crates/pi-tui/src/text/ansi.rs +415 -0
  1424. package/crates/pi-tui/src/text/mod.rs +29 -0
  1425. package/crates/pi-tui/src/text/slice.rs +645 -0
  1426. package/crates/pi-tui/src/text/tests.rs +359 -0
  1427. package/crates/pi-tui/src/text/width.rs +311 -0
  1428. package/crates/pi-tui/src/text/wrap.rs +344 -0
  1429. package/crates/pi-tui/tests/pty_no_flicker.rs +929 -0
  1430. package/deny.toml +38 -0
  1431. package/npm-package/bin/pi.js +31 -0
  1432. package/npm-package/package.json +35 -0
  1433. package/npm-package/tests/archive.test.ts +139 -0
  1434. package/npm-package/tests/wrapper.test.ts +70 -0
  1435. package/package.json +34 -0
  1436. package/packages/extension-host/bun.lock +664 -0
  1437. package/packages/extension-host/fixtures/extensions/all-events.ts +55 -0
  1438. package/packages/extension-host/fixtures/extensions/control-compat-shadow.ts +14 -0
  1439. package/packages/extension-host/fixtures/extensions/control-compat.ts +35 -0
  1440. package/packages/extension-host/fixtures/extensions/crash.ts +36 -0
  1441. package/packages/extension-host/fixtures/extensions/hooks.ts +45 -0
  1442. package/packages/extension-host/fixtures/extensions/hostile.ts +39 -0
  1443. package/packages/extension-host/fixtures/extensions/idle.ts +9 -0
  1444. package/packages/extension-host/fixtures/extensions/provider-stream.ts +121 -0
  1445. package/packages/extension-host/fixtures/extensions/terminal-input-fast.ts +20 -0
  1446. package/packages/extension-host/fixtures/extensions/terminal-input-slow.ts +23 -0
  1447. package/packages/extension-host/fixtures/extensions/tool-progress.ts +72 -0
  1448. package/packages/extension-host/fixtures/extensions/tool.ts +71 -0
  1449. package/packages/extension-host/fixtures/extensions/virtual-imports.ts +87 -0
  1450. package/packages/extension-host/fixtures/extensions/widget-active.ts +16 -0
  1451. package/packages/extension-host/fixtures/photon-probe.ts +45 -0
  1452. package/packages/extension-host/fixtures/runtime-import.ts +54 -0
  1453. package/packages/extension-host/package.json +54 -0
  1454. package/packages/extension-host/src/host.ts +1651 -0
  1455. package/packages/extension-host/src/index.ts +21 -0
  1456. package/packages/extension-host/src/main.ts +51 -0
  1457. package/packages/extension-host/src/protocol.ts +10 -0
  1458. package/packages/extension-host/src/refs.d.ts +364 -0
  1459. package/packages/extension-host/src/sanitize.ts +503 -0
  1460. package/packages/extension-host/src/virtual-modules.ts +90 -0
  1461. package/packages/extension-host/tests/acceptance.test.ts +1498 -0
  1462. package/packages/extension-host/tests/host.test.ts +244 -0
  1463. package/packages/extension-host/tests/sanitize.test.ts +359 -0
  1464. package/packages/extension-host/tests/scaling.test.ts +661 -0
  1465. package/packages/extension-host/tests/virtual-modules.test.ts +122 -0
  1466. package/packages/extension-host/tsconfig.build.json +44 -0
  1467. package/packages/extension-host/tsconfig.check.json +29 -0
  1468. package/packages/extension-host/tsconfig.json +8 -0
  1469. package/packages/pi-tui-protocol/package.json +41 -0
  1470. package/packages/pi-tui-protocol/src/client.ts +341 -0
  1471. package/packages/pi-tui-protocol/src/codec.ts +439 -0
  1472. package/packages/pi-tui-protocol/src/index.ts +77 -0
  1473. package/packages/pi-tui-protocol/src/types.ts +358 -0
  1474. package/packages/pi-tui-protocol/tests/client.test.ts +175 -0
  1475. package/packages/pi-tui-protocol/tests/codec.test.ts +196 -0
  1476. package/packages/pi-tui-protocol/tests/fixtures/frames.jsonl +30 -0
  1477. package/packages/pi-tui-protocol/tsconfig.build.json +23 -0
  1478. package/packages/pi-tui-protocol/tsconfig.json +9 -0
  1479. package/rust-toolchain.toml +3 -0
  1480. package/rustfmt.toml +5 -0
  1481. package/scripts/bench-extension-scaling.ts +466 -0
  1482. package/scripts/build-extension-host.ts +51 -0
  1483. package/scripts/generate-builtin-models.ts +344 -0
  1484. package/scripts/generate-session-fixtures.ts +1758 -0
  1485. package/scripts/generate-tool-schemas.ts +444 -0
  1486. package/scripts/package-npm.ts +263 -0
  1487. package/scripts/package-release.ts +422 -0
  1488. package/scripts/release/archive.ts +594 -0
  1489. package/scripts/release/args.ts +187 -0
  1490. package/scripts/release/extract-host-asset.ts +224 -0
  1491. package/scripts/release/host.ts +825 -0
  1492. package/scripts/release/runner.ts +435 -0
  1493. package/scripts/release/runtime.ts +130 -0
  1494. package/scripts/release/stage.ts +437 -0
  1495. package/scripts/release/targets.ts +150 -0
  1496. package/scripts/tests/archive.test.ts +210 -0
  1497. package/scripts/tests/args.test.ts +180 -0
  1498. package/scripts/tests/cli-dry-run.test.ts +61 -0
  1499. package/scripts/tests/compat-authority.test.ts +79 -0
  1500. package/scripts/tests/compat-matrix.test.ts +389 -0
  1501. package/scripts/tests/extract-host-asset.test.ts +176 -0
  1502. package/scripts/tests/host.test.ts +443 -0
  1503. package/scripts/tests/npm-installed-smoke.test.ts +104 -0
  1504. package/scripts/tests/package-release.test.ts +516 -0
  1505. package/scripts/tests/repro-guards.test.ts +241 -0
  1506. package/scripts/tests/runner.test.ts +188 -0
  1507. package/scripts/tests/stage.test.ts +232 -0
  1508. package/scripts/tests/targets.test.ts +93 -0
  1509. package/scripts/tsconfig.json +47 -0
  1510. package/scripts/verification/compat-matrix.json +455 -0
  1511. package/scripts/verification/compat-matrix.ts +632 -0
  1512. package/scripts/verification/e2e-smoke.ts +1093 -0
  1513. package/scripts/verification/extension.ts +339 -0
  1514. package/scripts/verification/foundation.test.ts +339 -0
  1515. package/scripts/verification/npm-installed-smoke.ts +117 -0
  1516. package/scripts/verification/performance.ts +1425 -0
  1517. package/scripts/verification/pty.ts +332 -0
@@ -0,0 +1,2333 @@
1
+ import type { AutocompleteProvider, AutocompleteSuggestions } from "../autocomplete.ts";
2
+ import { getKeybindings } from "../keybindings.ts";
3
+ import { decodePrintableKey, matchesKey } from "../keys.ts";
4
+ import { KillRing } from "../kill-ring.ts";
5
+ import { type Component, CURSOR_MARKER, type Focusable, type TUI } from "../tui.ts";
6
+ import { UndoStack } from "../undo-stack.ts";
7
+ import {
8
+ cjkBreakRegex,
9
+ getGraphemeSegmenter,
10
+ getWordSegmenter,
11
+ isWhitespaceChar,
12
+ truncateToWidth,
13
+ visibleWidth,
14
+ } from "../utils.ts";
15
+ import { findWordBackward, findWordForward } from "../word-navigation.ts";
16
+ import { SelectList, type SelectListLayoutOptions, type SelectListTheme } from "./select-list.ts";
17
+
18
+ const graphemeSegmenter = getGraphemeSegmenter();
19
+ const wordSegmenter = getWordSegmenter();
20
+
21
+ /** Regex matching paste markers like `[paste #1 +123 lines]` or `[paste #2 1234 chars]`. */
22
+ const PASTE_MARKER_REGEX = /\[paste #(\d+)( (\+\d+ lines|\d+ chars))?\]/g;
23
+
24
+ /** Non-global version for single-segment testing. */
25
+ const PASTE_MARKER_SINGLE = /^\[paste #(\d+)( (\+\d+ lines|\d+ chars))?\]$/;
26
+
27
+ /** Check if a segment is a paste marker (i.e. was merged by segmentWithMarkers). */
28
+ function isPasteMarker(segment: string): boolean {
29
+ return segment.length >= 10 && PASTE_MARKER_SINGLE.test(segment);
30
+ }
31
+
32
+ /**
33
+ * A segmenter that wraps Intl.Segmenter and merges graphemes that fall
34
+ * within paste markers into single atomic segments. This makes cursor
35
+ * movement, deletion, word-wrap, etc. treat paste markers as single units.
36
+ *
37
+ * Only markers whose numeric ID exists in `validIds` are merged.
38
+ */
39
+ function segmentWithMarkers(
40
+ text: string,
41
+ baseSegmenter: Intl.Segmenter,
42
+ validIds: Set<number>,
43
+ ): Iterable<Intl.SegmentData> {
44
+ // Fast path: no paste markers in the text or no valid IDs.
45
+ if (validIds.size === 0 || !text.includes("[paste #")) {
46
+ return baseSegmenter.segment(text);
47
+ }
48
+
49
+ // Find all marker spans with valid IDs.
50
+ const markers: Array<{ start: number; end: number }> = [];
51
+ for (const m of text.matchAll(PASTE_MARKER_REGEX)) {
52
+ const id = Number.parseInt(m[1]!, 10);
53
+ if (!validIds.has(id)) continue;
54
+ markers.push({ start: m.index, end: m.index + m[0].length });
55
+ }
56
+ if (markers.length === 0) {
57
+ return baseSegmenter.segment(text);
58
+ }
59
+
60
+ // Build merged segment list.
61
+ const baseSegments = baseSegmenter.segment(text);
62
+ const result: Intl.SegmentData[] = [];
63
+ let markerIdx = 0;
64
+
65
+ for (const seg of baseSegments) {
66
+ // Skip past markers that are entirely before this segment.
67
+ while (markerIdx < markers.length && markers[markerIdx]!.end <= seg.index) {
68
+ markerIdx++;
69
+ }
70
+
71
+ const marker = markerIdx < markers.length ? markers[markerIdx]! : null;
72
+
73
+ if (marker && seg.index >= marker.start && seg.index < marker.end) {
74
+ // This segment falls inside a marker.
75
+ // If this is the first segment of the marker, emit a merged segment.
76
+ if (seg.index === marker.start) {
77
+ const markerText = text.slice(marker.start, marker.end);
78
+ result.push({
79
+ segment: markerText,
80
+ index: marker.start,
81
+ input: text,
82
+ });
83
+ }
84
+ // Otherwise skip (already merged into the first segment).
85
+ } else {
86
+ result.push(seg);
87
+ }
88
+ }
89
+
90
+ return result;
91
+ }
92
+
93
+ /**
94
+ * Represents a chunk of text for word-wrap layout.
95
+ * Tracks both the text content and its position in the original line.
96
+ */
97
+ export interface TextChunk {
98
+ text: string;
99
+ startIndex: number;
100
+ endIndex: number;
101
+ }
102
+
103
+ /**
104
+ * Split a line into word-wrapped chunks.
105
+ * Wraps at word boundaries when possible, falling back to character-level
106
+ * wrapping for words longer than the available width.
107
+ *
108
+ * @param line - The text line to wrap
109
+ * @param maxWidth - Maximum visible width per chunk
110
+ * @param preSegmented - Optional pre-segmented graphemes (e.g. with paste-marker awareness).
111
+ * When omitted the default Intl.Segmenter is used.
112
+ * @returns Array of chunks with text and position information
113
+ */
114
+ export function wordWrapLine(line: string, maxWidth: number, preSegmented?: Intl.SegmentData[]): TextChunk[] {
115
+ if (!line || maxWidth <= 0) {
116
+ return [{ text: "", startIndex: 0, endIndex: 0 }];
117
+ }
118
+
119
+ const lineWidth = visibleWidth(line);
120
+ if (lineWidth <= maxWidth) {
121
+ return [{ text: line, startIndex: 0, endIndex: line.length }];
122
+ }
123
+
124
+ const chunks: TextChunk[] = [];
125
+ const segments = preSegmented ?? [...graphemeSegmenter.segment(line)];
126
+
127
+ let currentWidth = 0;
128
+ let chunkStart = 0;
129
+
130
+ // Wrap opportunity: the position after the last whitespace before a non-whitespace
131
+ // grapheme, i.e. where a line break is allowed.
132
+ let wrapOppIndex = -1;
133
+ let wrapOppWidth = 0;
134
+
135
+ for (let i = 0; i < segments.length; i++) {
136
+ const seg = segments[i]!;
137
+ const grapheme = seg.segment;
138
+ const gWidth = visibleWidth(grapheme);
139
+ const charIndex = seg.index;
140
+ const isWs = !isPasteMarker(grapheme) && isWhitespaceChar(grapheme);
141
+
142
+ // Overflow check before advancing.
143
+ if (currentWidth + gWidth > maxWidth) {
144
+ if (wrapOppIndex >= 0 && currentWidth - wrapOppWidth + gWidth <= maxWidth) {
145
+ // Backtrack to last wrap opportunity (the remaining content
146
+ // plus the current grapheme still fits within maxWidth).
147
+ chunks.push({ text: line.slice(chunkStart, wrapOppIndex), startIndex: chunkStart, endIndex: wrapOppIndex });
148
+ chunkStart = wrapOppIndex;
149
+ currentWidth -= wrapOppWidth;
150
+ } else if (chunkStart < charIndex) {
151
+ // No viable wrap opportunity: force-break at current position.
152
+ // This also handles the case where backtracking to a word
153
+ // boundary wouldn't help because the remaining content plus
154
+ // the current grapheme (e.g. a wide character) still exceeds
155
+ // maxWidth.
156
+ chunks.push({ text: line.slice(chunkStart, charIndex), startIndex: chunkStart, endIndex: charIndex });
157
+ chunkStart = charIndex;
158
+ currentWidth = 0;
159
+ }
160
+ wrapOppIndex = -1;
161
+ }
162
+
163
+ if (gWidth > maxWidth) {
164
+ // Single atomic segment wider than maxWidth (e.g. paste marker
165
+ // in a narrow terminal). Re-wrap it at grapheme granularity.
166
+
167
+ // The segment remains logically atomic for cursor
168
+ // movement / editing — the split is purely visual for word-wrap layout.
169
+ const subChunks = wordWrapLine(grapheme, maxWidth);
170
+ for (let j = 0; j < subChunks.length - 1; j++) {
171
+ const sc = subChunks[j]!;
172
+ chunks.push({ text: sc.text, startIndex: charIndex + sc.startIndex, endIndex: charIndex + sc.endIndex });
173
+ }
174
+ const last = subChunks[subChunks.length - 1]!;
175
+ chunkStart = charIndex + last.startIndex;
176
+ currentWidth = visibleWidth(last.text);
177
+ wrapOppIndex = -1;
178
+ continue;
179
+ }
180
+
181
+ // Advance.
182
+ currentWidth += gWidth;
183
+
184
+ // Record wrap opportunity: whitespace followed by non-whitespace
185
+ // (multiple spaces join; the break point is after the last space),
186
+ // or at a boundary where either side is CJK (CJK allows breaking
187
+ // between any adjacent characters).
188
+ const next = segments[i + 1];
189
+ if (isWs && next && (isPasteMarker(next.segment) || !isWhitespaceChar(next.segment))) {
190
+ wrapOppIndex = next.index;
191
+ wrapOppWidth = currentWidth;
192
+ } else if (!isWs && next && !isWhitespaceChar(next.segment)) {
193
+ const isCjk = !isPasteMarker(grapheme) && cjkBreakRegex.test(grapheme);
194
+ const nextIsCjk = !isPasteMarker(next.segment) && cjkBreakRegex.test(next.segment);
195
+ if (isCjk || nextIsCjk) {
196
+ wrapOppIndex = next.index;
197
+ wrapOppWidth = currentWidth;
198
+ }
199
+ }
200
+ }
201
+
202
+ // Push final chunk.
203
+ chunks.push({ text: line.slice(chunkStart), startIndex: chunkStart, endIndex: line.length });
204
+
205
+ return chunks;
206
+ }
207
+
208
+ // Kitty CSI-u sequences for printable keys, including optional shifted/base codepoints.
209
+ interface EditorState {
210
+ lines: string[];
211
+ cursorLine: number;
212
+ cursorCol: number;
213
+ }
214
+
215
+ interface LayoutLine {
216
+ text: string;
217
+ hasCursor: boolean;
218
+ cursorPos?: number;
219
+ }
220
+
221
+ export interface EditorTheme {
222
+ borderColor: (str: string) => string;
223
+ selectList: SelectListTheme;
224
+ }
225
+
226
+ export interface EditorOptions {
227
+ paddingX?: number;
228
+ autocompleteMaxVisible?: number;
229
+ }
230
+
231
+ const SLASH_COMMAND_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {
232
+ minPrimaryColumnWidth: 12,
233
+ maxPrimaryColumnWidth: 32,
234
+ };
235
+
236
+ const ATTACHMENT_AUTOCOMPLETE_DEBOUNCE_MS = 20;
237
+ const DEFAULT_AUTOCOMPLETE_TRIGGER_CHARACTERS = ["@", "#"];
238
+
239
+ function escapeCharacterClass(value: string): string {
240
+ return value.replace(/[\\^$.*+?()[\]{}|-]/g, "\\$&");
241
+ }
242
+
243
+ function buildTriggerPattern(triggerCharacters: string[]): RegExp {
244
+ return new RegExp(`(?:^|[\\s])[${triggerCharacters.map(escapeCharacterClass).join("")}][^\\s]*$`);
245
+ }
246
+
247
+ function buildDebouncePattern(triggerCharacters: string[]): RegExp {
248
+ const escapedWithoutAt = triggerCharacters.filter((character) => character !== "@").map(escapeCharacterClass);
249
+ return new RegExp(`(?:^|[ \\t])(?:@(?:"[^"]*|[^\\s]*)|[${escapedWithoutAt.join("")}][^\\s]*)$`);
250
+ }
251
+
252
+ export class Editor implements Component, Focusable {
253
+ private state: EditorState = {
254
+ lines: [""],
255
+ cursorLine: 0,
256
+ cursorCol: 0,
257
+ };
258
+
259
+ /** Focusable interface - set by TUI when focus changes */
260
+ focused: boolean = false;
261
+
262
+ protected tui: TUI;
263
+ private theme: EditorTheme;
264
+ private paddingX: number = 0;
265
+
266
+ // Store last render width for cursor navigation
267
+ private lastWidth: number = 80;
268
+
269
+ // Vertical scrolling support
270
+ private scrollOffset: number = 0;
271
+
272
+ // Border color (can be changed dynamically)
273
+ public borderColor: (str: string) => string;
274
+
275
+ // Autocomplete support
276
+ private autocompleteProvider?: AutocompleteProvider;
277
+ private autocompleteTriggerCharacters = [...DEFAULT_AUTOCOMPLETE_TRIGGER_CHARACTERS];
278
+ private autocompleteTriggerPattern = buildTriggerPattern(this.autocompleteTriggerCharacters);
279
+ private autocompleteDebouncePattern = buildDebouncePattern(this.autocompleteTriggerCharacters);
280
+ private autocompleteList?: SelectList;
281
+ private autocompleteState: "regular" | "force" | null = null;
282
+ private autocompletePrefix: string = "";
283
+ private autocompleteMaxVisible: number = 5;
284
+ private autocompleteAbort?: AbortController;
285
+ private autocompleteDebounceTimer?: ReturnType<typeof setTimeout>;
286
+ private autocompleteRequestTask: Promise<void> = Promise.resolve();
287
+ private autocompleteStartToken: number = 0;
288
+ private autocompleteRequestId: number = 0;
289
+
290
+ // Paste tracking for large pastes
291
+ private pastes: Map<number, string> = new Map();
292
+ private pasteCounter: number = 0;
293
+
294
+ // Bracketed paste mode buffering
295
+ private pasteBuffer: string = "";
296
+ private isInPaste: boolean = false;
297
+
298
+ // Prompt history for up/down navigation
299
+ private history: string[] = [];
300
+ private historyIndex: number = -1; // -1 = not browsing, 0 = most recent, 1 = older, etc.
301
+ private historyDraft: EditorState | null = null;
302
+
303
+ // Kill ring for Emacs-style kill/yank operations
304
+ private killRing = new KillRing();
305
+ private lastAction: "kill" | "yank" | "type-word" | null = null;
306
+
307
+ // Character jump mode
308
+ private jumpMode: "forward" | "backward" | null = null;
309
+
310
+ // Preferred visual column for vertical cursor movement (sticky column)
311
+ private preferredVisualCol: number | null = null;
312
+
313
+ // When the cursor is snapped to the start of an atomic segment, e.g. a
314
+ // paste marker, cursorCol no longer reflects where the cursor would have
315
+ // landed. This field stores the pre-snap cursorCol so that the next
316
+ // vertical move can resolve it to a visual column on whatever VL it belongs
317
+ // to.
318
+ private snappedFromCursorCol: number | null = null;
319
+
320
+ // Undo support
321
+ private undoStack = new UndoStack<EditorState>();
322
+
323
+ public onSubmit?: (text: string) => void;
324
+ public onChange?: (text: string) => void;
325
+ public disableSubmit: boolean = false;
326
+
327
+ constructor(tui: TUI, theme: EditorTheme, options: EditorOptions = {}) {
328
+ this.tui = tui;
329
+ this.theme = theme;
330
+ this.borderColor = theme.borderColor;
331
+ const paddingX = options.paddingX ?? 0;
332
+ this.paddingX = Number.isFinite(paddingX) ? Math.max(0, Math.floor(paddingX)) : 0;
333
+ const maxVisible = options.autocompleteMaxVisible ?? 5;
334
+ this.autocompleteMaxVisible = Number.isFinite(maxVisible) ? Math.max(3, Math.min(20, Math.floor(maxVisible))) : 5;
335
+ }
336
+
337
+ /** Set of currently valid paste IDs, for marker-aware segmentation. */
338
+ private validPasteIds(): Set<number> {
339
+ return new Set(this.pastes.keys());
340
+ }
341
+
342
+ /** Segment text with paste-marker awareness, only merging markers with valid IDs. */
343
+ private segment(text: string, mode: "word" | "grapheme"): Iterable<Intl.SegmentData> {
344
+ return segmentWithMarkers(text, mode === "word" ? wordSegmenter : graphemeSegmenter, this.validPasteIds());
345
+ }
346
+
347
+ getPaddingX(): number {
348
+ return this.paddingX;
349
+ }
350
+
351
+ setPaddingX(padding: number): void {
352
+ const newPadding = Number.isFinite(padding) ? Math.max(0, Math.floor(padding)) : 0;
353
+ if (this.paddingX !== newPadding) {
354
+ this.paddingX = newPadding;
355
+ this.tui.requestRender();
356
+ }
357
+ }
358
+
359
+ getAutocompleteMaxVisible(): number {
360
+ return this.autocompleteMaxVisible;
361
+ }
362
+
363
+ setAutocompleteMaxVisible(maxVisible: number): void {
364
+ const newMaxVisible = Number.isFinite(maxVisible) ? Math.max(3, Math.min(20, Math.floor(maxVisible))) : 5;
365
+ if (this.autocompleteMaxVisible !== newMaxVisible) {
366
+ this.autocompleteMaxVisible = newMaxVisible;
367
+ this.tui.requestRender();
368
+ }
369
+ }
370
+
371
+ setAutocompleteProvider(provider: AutocompleteProvider): void {
372
+ this.cancelAutocomplete();
373
+ this.autocompleteProvider = provider;
374
+ this.setAutocompleteTriggerCharacters(provider.triggerCharacters ?? []);
375
+ }
376
+
377
+ /**
378
+ * Add a prompt to history for up/down arrow navigation.
379
+ * Called after successful submission.
380
+ */
381
+ addToHistory(text: string): void {
382
+ const trimmed = text.trim();
383
+ if (!trimmed) return;
384
+ // Don't add consecutive duplicates
385
+ if (this.history.length > 0 && this.history[0] === trimmed) return;
386
+ this.history.unshift(trimmed);
387
+ // Limit history size
388
+ if (this.history.length > 100) {
389
+ this.history.pop();
390
+ }
391
+ }
392
+
393
+ private isEditorEmpty(): boolean {
394
+ return this.state.lines.length === 1 && this.state.lines[0] === "";
395
+ }
396
+
397
+ private isOnFirstVisualLine(): boolean {
398
+ const visualLines = this.buildVisualLineMap(this.lastWidth);
399
+ const currentVisualLine = this.findCurrentVisualLine(visualLines);
400
+ return currentVisualLine === 0;
401
+ }
402
+
403
+ private isOnLastVisualLine(): boolean {
404
+ const visualLines = this.buildVisualLineMap(this.lastWidth);
405
+ const currentVisualLine = this.findCurrentVisualLine(visualLines);
406
+ return currentVisualLine === visualLines.length - 1;
407
+ }
408
+
409
+ private navigateHistory(direction: 1 | -1): void {
410
+ this.lastAction = null;
411
+ if (this.history.length === 0) return;
412
+
413
+ const newIndex = this.historyIndex - direction; // Up(-1) increases index, Down(1) decreases
414
+ if (newIndex < -1 || newIndex >= this.history.length) return;
415
+
416
+ // Capture state when first entering history browsing mode
417
+ if (this.historyIndex === -1 && newIndex >= 0) {
418
+ this.pushUndoSnapshot();
419
+ this.historyDraft = structuredClone(this.state);
420
+ }
421
+
422
+ this.historyIndex = newIndex;
423
+
424
+ if (this.historyIndex === -1) {
425
+ const draft = this.historyDraft;
426
+ this.historyDraft = null;
427
+ if (draft) {
428
+ this.state = draft;
429
+ this.preferredVisualCol = null;
430
+ this.snappedFromCursorCol = null;
431
+ this.scrollOffset = 0;
432
+ if (this.onChange) this.onChange(this.getText());
433
+ } else {
434
+ this.setTextInternal("");
435
+ }
436
+ } else {
437
+ this.setTextInternal(this.history[this.historyIndex] || "", direction === -1 ? "start" : "end");
438
+ }
439
+ }
440
+
441
+ private exitHistoryBrowsing(): void {
442
+ this.historyIndex = -1;
443
+ this.historyDraft = null;
444
+ }
445
+
446
+ /** Internal setText that doesn't reset history state - used by navigateHistory */
447
+ private setTextInternal(text: string, cursorPlacement: "start" | "end" = "end"): void {
448
+ const lines = text.split("\n");
449
+ this.state.lines = lines.length === 0 ? [""] : lines;
450
+ this.state.cursorLine = cursorPlacement === "start" ? 0 : this.state.lines.length - 1;
451
+ this.setCursorCol(cursorPlacement === "start" ? 0 : this.state.lines[this.state.cursorLine]?.length || 0);
452
+ // Reset scroll - render() will adjust to show cursor
453
+ this.scrollOffset = 0;
454
+
455
+ if (this.onChange) {
456
+ this.onChange(this.getText());
457
+ }
458
+ }
459
+
460
+ invalidate(): void {
461
+ // No cached state to invalidate currently
462
+ }
463
+
464
+ render(width: number): string[] {
465
+ const maxPadding = Math.max(0, Math.floor((width - 1) / 2));
466
+ const paddingX = Math.min(this.paddingX, maxPadding);
467
+ const contentWidth = Math.max(1, width - paddingX * 2);
468
+
469
+ // Layout width: with padding the cursor can overflow into it,
470
+ // without padding we reserve 1 column for the cursor.
471
+ const layoutWidth = Math.max(1, contentWidth - (paddingX ? 0 : 1));
472
+
473
+ // Store for cursor navigation (must match wrapping width)
474
+ this.lastWidth = layoutWidth;
475
+
476
+ const horizontal = this.borderColor("─");
477
+
478
+ // Layout the text
479
+ const layoutLines = this.layoutText(layoutWidth);
480
+
481
+ // Calculate max visible lines: 30% of terminal height, minimum 5 lines
482
+ const terminalRows = this.tui.terminal.rows;
483
+ const maxVisibleLines = Math.max(5, Math.floor(terminalRows * 0.3));
484
+
485
+ // Find the cursor line index in layoutLines
486
+ let cursorLineIndex = layoutLines.findIndex((line) => line.hasCursor);
487
+ if (cursorLineIndex === -1) cursorLineIndex = 0;
488
+
489
+ // Adjust scroll offset to keep cursor visible
490
+ if (cursorLineIndex < this.scrollOffset) {
491
+ this.scrollOffset = cursorLineIndex;
492
+ } else if (cursorLineIndex >= this.scrollOffset + maxVisibleLines) {
493
+ this.scrollOffset = cursorLineIndex - maxVisibleLines + 1;
494
+ }
495
+
496
+ // Clamp scroll offset to valid range
497
+ const maxScrollOffset = Math.max(0, layoutLines.length - maxVisibleLines);
498
+ this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, maxScrollOffset));
499
+
500
+ // Get visible lines slice
501
+ const visibleLines = layoutLines.slice(this.scrollOffset, this.scrollOffset + maxVisibleLines);
502
+
503
+ const result: string[] = [];
504
+ const leftPadding = " ".repeat(paddingX);
505
+ const rightPadding = leftPadding;
506
+
507
+ // Render top border (with scroll indicator if scrolled down)
508
+ if (this.scrollOffset > 0) {
509
+ const indicator = `─── ↑ ${this.scrollOffset} more `;
510
+ const remaining = width - visibleWidth(indicator);
511
+ if (remaining >= 0) {
512
+ result.push(this.borderColor(indicator + "─".repeat(remaining)));
513
+ } else {
514
+ result.push(this.borderColor(truncateToWidth(indicator, width)));
515
+ }
516
+ } else {
517
+ result.push(horizontal.repeat(width));
518
+ }
519
+
520
+ // Render each visible layout line
521
+ // Emit hardware cursor marker when focused so TUI can position the
522
+ // hardware cursor for IME candidate-window placement even while
523
+ // autocomplete (e.g. slash-command menu) is visible.
524
+ const emitCursorMarker = this.focused;
525
+
526
+ for (const layoutLine of visibleLines) {
527
+ let displayText = layoutLine.text;
528
+ let lineVisibleWidth = visibleWidth(layoutLine.text);
529
+ let cursorInPadding = false;
530
+
531
+ // Add cursor if this line has it
532
+ if (layoutLine.hasCursor && layoutLine.cursorPos !== undefined) {
533
+ const before = displayText.slice(0, layoutLine.cursorPos);
534
+ const after = displayText.slice(layoutLine.cursorPos);
535
+
536
+ // Hardware cursor marker (zero-width, emitted before fake cursor for IME positioning)
537
+ const marker = emitCursorMarker ? CURSOR_MARKER : "";
538
+
539
+ if (after.length > 0) {
540
+ // Cursor is on a character (grapheme) - replace it with highlighted version
541
+ // Get the first grapheme from 'after'
542
+ const afterGraphemes = [...this.segment(after, "grapheme")];
543
+ const firstGrapheme = afterGraphemes[0]?.segment || "";
544
+ const restAfter = after.slice(firstGrapheme.length);
545
+ const cursor = `\x1b[7m${firstGrapheme}\x1b[0m`;
546
+ displayText = before + marker + cursor + restAfter;
547
+ // lineVisibleWidth stays the same - we're replacing, not adding
548
+ } else {
549
+ // Cursor is at the end - add highlighted space
550
+ const cursor = "\x1b[7m \x1b[0m";
551
+ displayText = before + marker + cursor;
552
+ lineVisibleWidth = lineVisibleWidth + 1;
553
+ // If cursor overflows content width into the padding, flag it
554
+ if (lineVisibleWidth > contentWidth && paddingX > 0) {
555
+ cursorInPadding = true;
556
+ }
557
+ }
558
+ }
559
+
560
+ // Calculate padding based on actual visible width
561
+ const padding = " ".repeat(Math.max(0, contentWidth - lineVisibleWidth));
562
+ const lineRightPadding = cursorInPadding ? rightPadding.slice(1) : rightPadding;
563
+
564
+ // Render the line (no side borders, just horizontal lines above and below)
565
+ result.push(`${leftPadding}${displayText}${padding}${lineRightPadding}`);
566
+ }
567
+
568
+ // Render bottom border (with scroll indicator if more content below)
569
+ const linesBelow = layoutLines.length - (this.scrollOffset + visibleLines.length);
570
+ if (linesBelow > 0) {
571
+ const indicator = `─── ↓ ${linesBelow} more `;
572
+ const remaining = width - visibleWidth(indicator);
573
+ result.push(this.borderColor(indicator + "─".repeat(Math.max(0, remaining))));
574
+ } else {
575
+ result.push(horizontal.repeat(width));
576
+ }
577
+
578
+ // Add autocomplete list if active
579
+ if (this.autocompleteState && this.autocompleteList) {
580
+ const autocompleteResult = this.autocompleteList.render(contentWidth);
581
+ for (const line of autocompleteResult) {
582
+ const lineWidth = visibleWidth(line);
583
+ const linePadding = " ".repeat(Math.max(0, contentWidth - lineWidth));
584
+ result.push(`${leftPadding}${line}${linePadding}${rightPadding}`);
585
+ }
586
+ }
587
+
588
+ return result;
589
+ }
590
+
591
+ handleInput(data: string): void {
592
+ const kb = getKeybindings();
593
+
594
+ // Handle character jump mode (awaiting next character to jump to)
595
+ if (this.jumpMode !== null) {
596
+ // Cancel if the hotkey is pressed again
597
+ if (kb.matches(data, "tui.editor.jumpForward") || kb.matches(data, "tui.editor.jumpBackward")) {
598
+ this.jumpMode = null;
599
+ return;
600
+ }
601
+
602
+ const printable = decodePrintableKey(data) ?? (data.charCodeAt(0) >= 32 ? data : undefined);
603
+ if (printable !== undefined) {
604
+ // Printable character - perform the jump
605
+ const direction = this.jumpMode;
606
+ this.jumpMode = null;
607
+ this.jumpToChar(printable, direction);
608
+ return;
609
+ }
610
+
611
+ // Control character - cancel and fall through to normal handling
612
+ this.jumpMode = null;
613
+ }
614
+
615
+ // Handle bracketed paste mode
616
+ if (data.includes("\x1b[200~")) {
617
+ this.isInPaste = true;
618
+ this.pasteBuffer = "";
619
+ data = data.replace("\x1b[200~", "");
620
+ }
621
+
622
+ if (this.isInPaste) {
623
+ this.pasteBuffer += data;
624
+ const endIndex = this.pasteBuffer.indexOf("\x1b[201~");
625
+ if (endIndex !== -1) {
626
+ const pasteContent = this.pasteBuffer.substring(0, endIndex);
627
+ if (pasteContent.length > 0) {
628
+ this.handlePaste(pasteContent);
629
+ }
630
+ this.isInPaste = false;
631
+ const remaining = this.pasteBuffer.substring(endIndex + 6);
632
+ this.pasteBuffer = "";
633
+ if (remaining.length > 0) {
634
+ this.handleInput(remaining);
635
+ }
636
+ return;
637
+ }
638
+ return;
639
+ }
640
+
641
+ // Ctrl+C - let parent handle (exit/clear)
642
+ if (kb.matches(data, "tui.input.copy")) {
643
+ return;
644
+ }
645
+
646
+ // Undo
647
+ if (kb.matches(data, "tui.editor.undo")) {
648
+ this.undo();
649
+ return;
650
+ }
651
+
652
+ // Handle autocomplete mode
653
+ if (this.autocompleteState && this.autocompleteList) {
654
+ if (kb.matches(data, "tui.select.cancel")) {
655
+ this.cancelAutocomplete();
656
+ return;
657
+ }
658
+
659
+ if (kb.matches(data, "tui.select.up") || kb.matches(data, "tui.select.down")) {
660
+ this.autocompleteList.handleInput(data);
661
+ return;
662
+ }
663
+
664
+ if (kb.matches(data, "tui.input.tab")) {
665
+ const selected = this.autocompleteList.getSelectedItem();
666
+ if (selected && this.autocompleteProvider) {
667
+ this.pushUndoSnapshot();
668
+ this.lastAction = null;
669
+ const result = this.autocompleteProvider.applyCompletion(
670
+ this.state.lines,
671
+ this.state.cursorLine,
672
+ this.state.cursorCol,
673
+ selected,
674
+ this.autocompletePrefix,
675
+ );
676
+ this.state.lines = result.lines;
677
+ this.state.cursorLine = result.cursorLine;
678
+ this.setCursorCol(result.cursorCol);
679
+ this.cancelAutocomplete();
680
+ if (this.onChange) this.onChange(this.getText());
681
+ }
682
+ return;
683
+ }
684
+
685
+ if (kb.matches(data, "tui.select.confirm")) {
686
+ const selected = this.autocompleteList.getSelectedItem();
687
+ if (selected && this.autocompleteProvider) {
688
+ this.pushUndoSnapshot();
689
+ this.lastAction = null;
690
+ const result = this.autocompleteProvider.applyCompletion(
691
+ this.state.lines,
692
+ this.state.cursorLine,
693
+ this.state.cursorCol,
694
+ selected,
695
+ this.autocompletePrefix,
696
+ );
697
+ this.state.lines = result.lines;
698
+ this.state.cursorLine = result.cursorLine;
699
+ this.setCursorCol(result.cursorCol);
700
+
701
+ if (this.autocompletePrefix.startsWith("/")) {
702
+ this.cancelAutocomplete();
703
+ // Fall through to submit
704
+ } else {
705
+ this.cancelAutocomplete();
706
+ if (this.onChange) this.onChange(this.getText());
707
+ return;
708
+ }
709
+ }
710
+ }
711
+ }
712
+
713
+ // Tab - trigger completion
714
+ if (kb.matches(data, "tui.input.tab") && !this.autocompleteState) {
715
+ this.handleTabCompletion();
716
+ return;
717
+ }
718
+
719
+ // Deletion actions
720
+ if (kb.matches(data, "tui.editor.deleteToLineEnd")) {
721
+ this.deleteToEndOfLine();
722
+ return;
723
+ }
724
+ if (kb.matches(data, "tui.editor.deleteToLineStart")) {
725
+ this.deleteToStartOfLine();
726
+ return;
727
+ }
728
+ if (kb.matches(data, "tui.editor.deleteWordBackward")) {
729
+ this.deleteWordBackwards();
730
+ return;
731
+ }
732
+ if (kb.matches(data, "tui.editor.deleteWordForward")) {
733
+ this.deleteWordForward();
734
+ return;
735
+ }
736
+ if (kb.matches(data, "tui.editor.deleteCharBackward") || matchesKey(data, "shift+backspace")) {
737
+ this.handleBackspace();
738
+ return;
739
+ }
740
+ if (kb.matches(data, "tui.editor.deleteCharForward") || matchesKey(data, "shift+delete")) {
741
+ this.handleForwardDelete();
742
+ return;
743
+ }
744
+
745
+ // Kill ring actions
746
+ if (kb.matches(data, "tui.editor.yank")) {
747
+ this.yank();
748
+ return;
749
+ }
750
+ if (kb.matches(data, "tui.editor.yankPop")) {
751
+ this.yankPop();
752
+ return;
753
+ }
754
+
755
+ // Cursor movement actions
756
+ if (kb.matches(data, "tui.editor.cursorLineStart")) {
757
+ this.moveToLineStart();
758
+ return;
759
+ }
760
+ if (kb.matches(data, "tui.editor.cursorLineEnd")) {
761
+ this.moveToLineEnd();
762
+ return;
763
+ }
764
+ if (kb.matches(data, "tui.editor.cursorWordLeft")) {
765
+ this.moveWordBackwards();
766
+ return;
767
+ }
768
+ if (kb.matches(data, "tui.editor.cursorWordRight")) {
769
+ this.moveWordForwards();
770
+ return;
771
+ }
772
+
773
+ // New line
774
+ if (
775
+ kb.matches(data, "tui.input.newLine") ||
776
+ (data.charCodeAt(0) === 10 && data.length > 1) ||
777
+ data === "\x1b\r" ||
778
+ data === "\x1b[13;2~" ||
779
+ (data.length > 1 && data.includes("\x1b") && data.includes("\r")) ||
780
+ (data === "\n" && data.length === 1)
781
+ ) {
782
+ if (this.shouldSubmitOnBackslashEnter(data, kb)) {
783
+ this.handleBackspace();
784
+ this.submitValue();
785
+ return;
786
+ }
787
+ this.addNewLine();
788
+ return;
789
+ }
790
+
791
+ // Submit (Enter)
792
+ if (kb.matches(data, "tui.input.submit")) {
793
+ if (this.disableSubmit) return;
794
+
795
+ // Workaround for terminals without Shift+Enter support:
796
+ // If char before cursor is \, delete it and insert newline instead of submitting.
797
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
798
+ if (this.state.cursorCol > 0 && currentLine[this.state.cursorCol - 1] === "\\") {
799
+ this.handleBackspace();
800
+ this.addNewLine();
801
+ return;
802
+ }
803
+
804
+ this.submitValue();
805
+ return;
806
+ }
807
+
808
+ // Arrow key navigation (with history support)
809
+ if (kb.matches(data, "tui.editor.cursorUp")) {
810
+ if (
811
+ this.isOnFirstVisualLine() &&
812
+ (this.isEditorEmpty() || this.historyIndex > -1 || this.state.cursorCol === 0)
813
+ ) {
814
+ this.navigateHistory(-1);
815
+ } else if (this.isOnFirstVisualLine()) {
816
+ // Already at top - jump to start of line
817
+ this.moveToLineStart();
818
+ } else {
819
+ this.moveCursor(-1, 0);
820
+ }
821
+ return;
822
+ }
823
+ if (kb.matches(data, "tui.editor.cursorDown")) {
824
+ if (this.historyIndex > -1 && this.isOnLastVisualLine()) {
825
+ this.navigateHistory(1);
826
+ } else if (this.isOnLastVisualLine()) {
827
+ // Already at bottom - jump to end of line
828
+ this.moveToLineEnd();
829
+ } else {
830
+ this.moveCursor(1, 0);
831
+ }
832
+ return;
833
+ }
834
+ if (kb.matches(data, "tui.editor.cursorRight")) {
835
+ this.moveCursor(0, 1);
836
+ return;
837
+ }
838
+ if (kb.matches(data, "tui.editor.cursorLeft")) {
839
+ this.moveCursor(0, -1);
840
+ return;
841
+ }
842
+
843
+ // Page up/down - scroll by page and move cursor
844
+ if (kb.matches(data, "tui.editor.pageUp")) {
845
+ this.pageScroll(-1);
846
+ return;
847
+ }
848
+ if (kb.matches(data, "tui.editor.pageDown")) {
849
+ this.pageScroll(1);
850
+ return;
851
+ }
852
+
853
+ // Character jump mode triggers
854
+ if (kb.matches(data, "tui.editor.jumpForward")) {
855
+ this.jumpMode = "forward";
856
+ return;
857
+ }
858
+ if (kb.matches(data, "tui.editor.jumpBackward")) {
859
+ this.jumpMode = "backward";
860
+ return;
861
+ }
862
+
863
+ // Shift+Space - insert regular space
864
+ if (matchesKey(data, "shift+space")) {
865
+ this.insertCharacter(" ");
866
+ return;
867
+ }
868
+
869
+ const printable = decodePrintableKey(data);
870
+ if (printable !== undefined) {
871
+ this.insertCharacter(printable);
872
+ return;
873
+ }
874
+
875
+ // Regular characters
876
+ if (data.charCodeAt(0) >= 32) {
877
+ this.insertCharacter(data);
878
+ }
879
+ }
880
+
881
+ private layoutText(contentWidth: number): LayoutLine[] {
882
+ const layoutLines: LayoutLine[] = [];
883
+
884
+ if (this.state.lines.length === 0 || (this.state.lines.length === 1 && this.state.lines[0] === "")) {
885
+ // Empty editor
886
+ layoutLines.push({
887
+ text: "",
888
+ hasCursor: true,
889
+ cursorPos: 0,
890
+ });
891
+ return layoutLines;
892
+ }
893
+
894
+ // Process each logical line
895
+ for (let i = 0; i < this.state.lines.length; i++) {
896
+ const line = this.state.lines[i] || "";
897
+ const isCurrentLine = i === this.state.cursorLine;
898
+ const lineVisibleWidth = visibleWidth(line);
899
+
900
+ if (lineVisibleWidth <= contentWidth) {
901
+ // Line fits in one layout line
902
+ if (isCurrentLine) {
903
+ layoutLines.push({
904
+ text: line,
905
+ hasCursor: true,
906
+ cursorPos: this.state.cursorCol,
907
+ });
908
+ } else {
909
+ layoutLines.push({
910
+ text: line,
911
+ hasCursor: false,
912
+ });
913
+ }
914
+ } else {
915
+ // Line needs wrapping - use word-aware wrapping
916
+ const chunks = wordWrapLine(line, contentWidth, [...this.segment(line, "grapheme")]);
917
+
918
+ for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
919
+ const chunk = chunks[chunkIndex];
920
+ if (!chunk) continue;
921
+
922
+ const cursorPos = this.state.cursorCol;
923
+ const isLastChunk = chunkIndex === chunks.length - 1;
924
+
925
+ // Determine if cursor is in this chunk
926
+ // For word-wrapped chunks, we need to handle the case where
927
+ // cursor might be in trimmed whitespace at end of chunk
928
+ let hasCursorInChunk = false;
929
+ let adjustedCursorPos = 0;
930
+
931
+ if (isCurrentLine) {
932
+ if (isLastChunk) {
933
+ // Last chunk: cursor belongs here if >= startIndex
934
+ hasCursorInChunk = cursorPos >= chunk.startIndex;
935
+ adjustedCursorPos = cursorPos - chunk.startIndex;
936
+ } else {
937
+ // Non-last chunk: cursor belongs here if in range [startIndex, endIndex)
938
+ // But we need to handle the visual position in the trimmed text
939
+ hasCursorInChunk = cursorPos >= chunk.startIndex && cursorPos < chunk.endIndex;
940
+ if (hasCursorInChunk) {
941
+ adjustedCursorPos = cursorPos - chunk.startIndex;
942
+ // Clamp to text length (in case cursor was in trimmed whitespace)
943
+ if (adjustedCursorPos > chunk.text.length) {
944
+ adjustedCursorPos = chunk.text.length;
945
+ }
946
+ }
947
+ }
948
+ }
949
+
950
+ if (hasCursorInChunk) {
951
+ layoutLines.push({
952
+ text: chunk.text,
953
+ hasCursor: true,
954
+ cursorPos: adjustedCursorPos,
955
+ });
956
+ } else {
957
+ layoutLines.push({
958
+ text: chunk.text,
959
+ hasCursor: false,
960
+ });
961
+ }
962
+ }
963
+ }
964
+ }
965
+
966
+ return layoutLines;
967
+ }
968
+
969
+ getText(): string {
970
+ return this.state.lines.join("\n");
971
+ }
972
+
973
+ private expandPasteMarkers(text: string): string {
974
+ let result = text;
975
+ for (const [pasteId, pasteContent] of this.pastes) {
976
+ const markerRegex = new RegExp(`\\[paste #${pasteId}( (\\+\\d+ lines|\\d+ chars))?\\]`, "g");
977
+ result = result.replace(markerRegex, () => pasteContent);
978
+ }
979
+ return result;
980
+ }
981
+
982
+ /**
983
+ * Get text with paste markers expanded to their actual content.
984
+ * Use this when you need the full content (e.g., for external editor).
985
+ */
986
+ getExpandedText(): string {
987
+ return this.expandPasteMarkers(this.state.lines.join("\n"));
988
+ }
989
+
990
+ getLines(): string[] {
991
+ return [...this.state.lines];
992
+ }
993
+
994
+ getCursor(): { line: number; col: number } {
995
+ return { line: this.state.cursorLine, col: this.state.cursorCol };
996
+ }
997
+
998
+ setText(text: string): void {
999
+ this.cancelAutocomplete();
1000
+ this.lastAction = null;
1001
+ this.exitHistoryBrowsing();
1002
+ this.pastes.clear();
1003
+ this.pasteCounter = 0;
1004
+ const normalized = this.normalizeText(text);
1005
+ // Push undo snapshot if content differs (makes programmatic changes undoable)
1006
+ if (this.getText() !== normalized) {
1007
+ this.pushUndoSnapshot();
1008
+ }
1009
+ this.setTextInternal(normalized);
1010
+ }
1011
+
1012
+ /**
1013
+ * Insert text at the current cursor position.
1014
+ * Used for programmatic insertion (e.g., clipboard image markers).
1015
+ * This is atomic for undo - single undo restores entire pre-insert state.
1016
+ */
1017
+ insertTextAtCursor(text: string): void {
1018
+ if (!text) return;
1019
+ this.cancelAutocomplete();
1020
+ this.pushUndoSnapshot();
1021
+ this.lastAction = null;
1022
+ this.exitHistoryBrowsing();
1023
+ this.insertTextAtCursorInternal(text);
1024
+ }
1025
+
1026
+ /**
1027
+ * Normalize text for editor storage:
1028
+ * - Normalize line endings (\r\n and \r -> \n)
1029
+ * - Expand tabs to 4 spaces
1030
+ */
1031
+ private normalizeText(text: string): string {
1032
+ return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\t/g, " ");
1033
+ }
1034
+
1035
+ /**
1036
+ * Internal text insertion at cursor. Handles single and multi-line text.
1037
+ * Does not push undo snapshots or trigger autocomplete - caller is responsible.
1038
+ * Normalizes line endings and calls onChange once at the end.
1039
+ */
1040
+ private insertTextAtCursorInternal(text: string): void {
1041
+ if (!text) return;
1042
+
1043
+ // Normalize line endings and tabs
1044
+ const normalized = this.normalizeText(text);
1045
+ const insertedLines = normalized.split("\n");
1046
+
1047
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1048
+ const beforeCursor = currentLine.slice(0, this.state.cursorCol);
1049
+ const afterCursor = currentLine.slice(this.state.cursorCol);
1050
+
1051
+ if (insertedLines.length === 1) {
1052
+ // Single line - insert at cursor position
1053
+ this.state.lines[this.state.cursorLine] = beforeCursor + normalized + afterCursor;
1054
+ this.setCursorCol(this.state.cursorCol + normalized.length);
1055
+ } else {
1056
+ // Multi-line insertion
1057
+ this.state.lines = [
1058
+ // All lines before current line
1059
+ ...this.state.lines.slice(0, this.state.cursorLine),
1060
+
1061
+ // The first inserted line merged with text before cursor
1062
+ beforeCursor + insertedLines[0],
1063
+
1064
+ // All middle inserted lines
1065
+ ...insertedLines.slice(1, -1),
1066
+
1067
+ // The last inserted line with text after cursor
1068
+ insertedLines[insertedLines.length - 1] + afterCursor,
1069
+
1070
+ // All lines after current line
1071
+ ...this.state.lines.slice(this.state.cursorLine + 1),
1072
+ ];
1073
+
1074
+ this.state.cursorLine += insertedLines.length - 1;
1075
+ this.setCursorCol((insertedLines[insertedLines.length - 1] || "").length);
1076
+ }
1077
+
1078
+ if (this.onChange) {
1079
+ this.onChange(this.getText());
1080
+ }
1081
+ }
1082
+
1083
+ // All the editor methods from before...
1084
+ private insertCharacter(char: string, skipUndoCoalescing?: boolean): void {
1085
+ this.exitHistoryBrowsing();
1086
+
1087
+ // Undo coalescing (fish-style):
1088
+ // - Consecutive word chars coalesce into one undo unit
1089
+ // - Space captures state before itself (so undo removes space+following word together)
1090
+ // - Each space is separately undoable
1091
+ // Skip coalescing when called from atomic operations (e.g., handlePaste)
1092
+ if (!skipUndoCoalescing) {
1093
+ if (isWhitespaceChar(char) || this.lastAction !== "type-word") {
1094
+ this.pushUndoSnapshot();
1095
+ }
1096
+ this.lastAction = "type-word";
1097
+ }
1098
+
1099
+ const line = this.state.lines[this.state.cursorLine] || "";
1100
+
1101
+ const before = line.slice(0, this.state.cursorCol);
1102
+ const after = line.slice(this.state.cursorCol);
1103
+
1104
+ this.state.lines[this.state.cursorLine] = before + char + after;
1105
+ this.setCursorCol(this.state.cursorCol + char.length);
1106
+
1107
+ if (this.onChange) {
1108
+ this.onChange(this.getText());
1109
+ }
1110
+
1111
+ // Check if we should trigger or update autocomplete
1112
+ if (!this.autocompleteState) {
1113
+ // Auto-trigger for "/" at the start of a line (slash commands)
1114
+ if (char === "/" && this.isAtStartOfMessage()) {
1115
+ this.tryTriggerAutocomplete();
1116
+ }
1117
+ // Auto-trigger for symbol-based completion like @, #, or provider triggers at token boundaries
1118
+ else if (this.autocompleteTriggerCharacters.includes(char)) {
1119
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1120
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
1121
+ const charBeforeSymbol = textBeforeCursor[textBeforeCursor.length - 2];
1122
+ if (textBeforeCursor.length === 1 || charBeforeSymbol === " " || charBeforeSymbol === "\t") {
1123
+ this.tryTriggerAutocomplete();
1124
+ }
1125
+ }
1126
+ // Also auto-trigger when typing letters in a slash command or symbol completion context
1127
+ else if (/[a-zA-Z0-9.\-_]/.test(char)) {
1128
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1129
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
1130
+ // Check if we're in a slash command (with or without space for arguments)
1131
+ if (this.isInSlashCommandContext(textBeforeCursor)) {
1132
+ this.tryTriggerAutocomplete();
1133
+ }
1134
+ // Check if we're in a symbol-based completion context like @, #, or provider triggers
1135
+ else if (this.autocompleteTriggerPattern.test(textBeforeCursor)) {
1136
+ this.tryTriggerAutocomplete();
1137
+ }
1138
+ }
1139
+ } else {
1140
+ this.updateAutocomplete();
1141
+ }
1142
+ }
1143
+
1144
+ private handlePaste(pastedText: string): void {
1145
+ this.cancelAutocomplete();
1146
+ this.exitHistoryBrowsing();
1147
+ this.lastAction = null;
1148
+
1149
+ this.pushUndoSnapshot();
1150
+
1151
+ // Some terminals (e.g. tmux popups with extended-keys-format=csi-u) re-encode
1152
+ // control bytes inside bracketed paste as CSI-u Ctrl+<letter> sequences
1153
+ // (ESC [ <codepoint> ; 5 u). Decode those back to their literal byte so the
1154
+ // per-char filter below preserves newlines instead of stripping ESC and
1155
+ // leaking the printable tail (e.g. "[106;5u") into the editor.
1156
+ const decodedText = pastedText.replace(/\x1b\[(\d+);5u/g, (match, code) => {
1157
+ const cp = Number(code);
1158
+ if (cp >= 97 && cp <= 122) return String.fromCharCode(cp - 96);
1159
+ if (cp >= 65 && cp <= 90) return String.fromCharCode(cp - 64);
1160
+ return match;
1161
+ });
1162
+
1163
+ // Clean the pasted text: normalize line endings, expand tabs
1164
+ const cleanText = this.normalizeText(decodedText);
1165
+
1166
+ // Filter out non-printable characters except newlines
1167
+ let filteredText = cleanText
1168
+ .split("")
1169
+ .filter((char) => char === "\n" || char.charCodeAt(0) >= 32)
1170
+ .join("");
1171
+
1172
+ // If pasting a file path (starts with /, ~, or .) and the character before
1173
+ // the cursor is a word character, prepend a space for better readability
1174
+ if (/^[/~.]/.test(filteredText)) {
1175
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1176
+ const charBeforeCursor = this.state.cursorCol > 0 ? currentLine[this.state.cursorCol - 1] : "";
1177
+ if (charBeforeCursor && /\w/.test(charBeforeCursor)) {
1178
+ filteredText = ` ${filteredText}`;
1179
+ }
1180
+ }
1181
+
1182
+ // Split into lines to check for large paste
1183
+ const pastedLines = filteredText.split("\n");
1184
+
1185
+ // Check if this is a large paste (> 10 lines or > 1000 characters)
1186
+ const totalChars = filteredText.length;
1187
+ if (pastedLines.length > 10 || totalChars > 1000) {
1188
+ // Store the paste and insert a marker
1189
+ this.pasteCounter++;
1190
+ const pasteId = this.pasteCounter;
1191
+ this.pastes.set(pasteId, filteredText);
1192
+
1193
+ // Insert marker like "[paste #1 +123 lines]" or "[paste #1 1234 chars]"
1194
+ const marker =
1195
+ pastedLines.length > 10
1196
+ ? `[paste #${pasteId} +${pastedLines.length} lines]`
1197
+ : `[paste #${pasteId} ${totalChars} chars]`;
1198
+ this.insertTextAtCursorInternal(marker);
1199
+ return;
1200
+ }
1201
+
1202
+ if (pastedLines.length === 1) {
1203
+ // Single line - insert atomically (do not trigger autocomplete during paste)
1204
+ this.insertTextAtCursorInternal(filteredText);
1205
+ return;
1206
+ }
1207
+
1208
+ // Multi-line paste - use direct state manipulation
1209
+ this.insertTextAtCursorInternal(filteredText);
1210
+ }
1211
+
1212
+ private addNewLine(): void {
1213
+ this.cancelAutocomplete();
1214
+ this.exitHistoryBrowsing();
1215
+ this.lastAction = null;
1216
+
1217
+ this.pushUndoSnapshot();
1218
+
1219
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1220
+
1221
+ const before = currentLine.slice(0, this.state.cursorCol);
1222
+ const after = currentLine.slice(this.state.cursorCol);
1223
+
1224
+ // Split current line
1225
+ this.state.lines[this.state.cursorLine] = before;
1226
+ this.state.lines.splice(this.state.cursorLine + 1, 0, after);
1227
+
1228
+ // Move cursor to start of new line
1229
+ this.state.cursorLine++;
1230
+ this.setCursorCol(0);
1231
+
1232
+ if (this.onChange) {
1233
+ this.onChange(this.getText());
1234
+ }
1235
+ }
1236
+
1237
+ private shouldSubmitOnBackslashEnter(data: string, kb: ReturnType<typeof getKeybindings>): boolean {
1238
+ if (this.disableSubmit) return false;
1239
+ if (!matchesKey(data, "enter")) return false;
1240
+ const submitKeys = kb.getKeys("tui.input.submit");
1241
+ const hasShiftEnter = submitKeys.includes("shift+enter") || submitKeys.includes("shift+return");
1242
+ if (!hasShiftEnter) return false;
1243
+
1244
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1245
+ return this.state.cursorCol > 0 && currentLine[this.state.cursorCol - 1] === "\\";
1246
+ }
1247
+
1248
+ private submitValue(): void {
1249
+ this.cancelAutocomplete();
1250
+ const result = this.expandPasteMarkers(this.state.lines.join("\n")).trim();
1251
+
1252
+ this.state = { lines: [""], cursorLine: 0, cursorCol: 0 };
1253
+ this.pastes.clear();
1254
+ this.pasteCounter = 0;
1255
+ this.exitHistoryBrowsing();
1256
+ this.scrollOffset = 0;
1257
+ this.undoStack.clear();
1258
+ this.lastAction = null;
1259
+
1260
+ if (this.onChange) this.onChange("");
1261
+ if (this.onSubmit) this.onSubmit(result);
1262
+ }
1263
+
1264
+ private handleBackspace(): void {
1265
+ this.exitHistoryBrowsing();
1266
+ this.lastAction = null;
1267
+
1268
+ if (this.state.cursorCol > 0) {
1269
+ this.pushUndoSnapshot();
1270
+
1271
+ // Delete grapheme before cursor (handles emojis, combining characters, etc.)
1272
+ let line = this.state.lines[this.state.cursorLine] || "";
1273
+ const beforeCursor = line.slice(0, this.state.cursorCol);
1274
+
1275
+ // Find the last grapheme in the text before cursor
1276
+ const graphemes = [...this.segment(beforeCursor, "grapheme")];
1277
+ const lastGrapheme = graphemes[graphemes.length - 1];
1278
+ const graphemeLength = lastGrapheme ? lastGrapheme.segment.length : 1;
1279
+ const isPastedSegmented = PASTE_MARKER_SINGLE.exec(lastGrapheme.segment);
1280
+
1281
+ if (isPastedSegmented) {
1282
+ // This contains the id part e.g 4 from [paste #4 +123 lines]
1283
+ const targetId = Number(isPastedSegmented[1]);
1284
+ this.pastes.delete(targetId);
1285
+ this.pasteCounter--;
1286
+
1287
+ // We got to update id of markers which are greater than the removed one
1288
+ this.state.lines = this.state.lines.map((line) =>
1289
+ line.replace(PASTE_MARKER_REGEX, (fullMatch, idGroup, suffixGroup) => {
1290
+ const x = Number(idGroup);
1291
+ if (x <= targetId) return fullMatch;
1292
+
1293
+ // [paste #3] become [paste #2] if we remove [paste #1]
1294
+ const newText = `[paste #${x - 1}${suffixGroup}]`;
1295
+ this.pastes.set(x - 1, this.pastes.get(x) ?? newText);
1296
+ this.pastes.delete(x);
1297
+ return newText;
1298
+ }),
1299
+ );
1300
+ }
1301
+
1302
+ line = this.state.lines[this.state.cursorLine] || "";
1303
+
1304
+ const before = line.slice(0, this.state.cursorCol - graphemeLength);
1305
+ const after = line.slice(this.state.cursorCol);
1306
+
1307
+ this.state.lines[this.state.cursorLine] = before + after;
1308
+ this.setCursorCol(this.state.cursorCol - graphemeLength);
1309
+ } else if (this.state.cursorLine > 0) {
1310
+ this.pushUndoSnapshot();
1311
+
1312
+ // Merge with previous line
1313
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1314
+ const previousLine = this.state.lines[this.state.cursorLine - 1] || "";
1315
+
1316
+ this.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;
1317
+ this.state.lines.splice(this.state.cursorLine, 1);
1318
+
1319
+ this.state.cursorLine--;
1320
+ this.setCursorCol(previousLine.length);
1321
+ }
1322
+
1323
+ if (this.onChange) {
1324
+ this.onChange(this.getText());
1325
+ }
1326
+
1327
+ // Update or re-trigger autocomplete after backspace
1328
+ if (this.autocompleteState) {
1329
+ this.updateAutocomplete();
1330
+ } else {
1331
+ // If autocomplete was cancelled (no matches), re-trigger if we're in a completable context
1332
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1333
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
1334
+ // Slash command context
1335
+ if (this.isInSlashCommandContext(textBeforeCursor)) {
1336
+ this.tryTriggerAutocomplete();
1337
+ }
1338
+ // Symbol-based completion context like @, #, or provider triggers
1339
+ else if (this.autocompleteTriggerPattern.test(textBeforeCursor)) {
1340
+ this.tryTriggerAutocomplete();
1341
+ }
1342
+ }
1343
+ }
1344
+
1345
+ /**
1346
+ * Set cursor column and clear preferredVisualCol.
1347
+ * Use this for all non-vertical cursor movements to reset sticky column behavior.
1348
+ */
1349
+ private setCursorCol(col: number): void {
1350
+ this.state.cursorCol = col;
1351
+ this.preferredVisualCol = null;
1352
+ this.snappedFromCursorCol = null;
1353
+ }
1354
+
1355
+ /**
1356
+ * Move cursor to a target visual line, applying sticky column logic.
1357
+ * Shared by moveCursor() and pageScroll().
1358
+ */
1359
+ private moveToVisualLine(
1360
+ visualLines: Array<{ logicalLine: number; startCol: number; length: number }>,
1361
+ currentVisualLine: number,
1362
+ targetVisualLine: number,
1363
+ ): void {
1364
+ const currentVL = visualLines[currentVisualLine];
1365
+ const targetVL = visualLines[targetVisualLine];
1366
+ if (!(currentVL && targetVL)) return;
1367
+
1368
+ // When the cursor was snapped to a segment start, resolve the pre-snap
1369
+ // position against the VL it belongs to. This gives the correct visual
1370
+ // column even after a resize reshuffles VLs.
1371
+ let currentVisualCol: number;
1372
+ if (this.snappedFromCursorCol !== null) {
1373
+ const vlIndex = this.findVisualLineAt(visualLines, currentVL.logicalLine, this.snappedFromCursorCol);
1374
+ currentVisualCol = this.snappedFromCursorCol - visualLines[vlIndex].startCol;
1375
+ } else {
1376
+ currentVisualCol = this.state.cursorCol - currentVL.startCol;
1377
+ }
1378
+
1379
+ // For non-last segments, clamp to length-1 to stay within the segment
1380
+ const isLastSourceSegment =
1381
+ currentVisualLine === visualLines.length - 1 ||
1382
+ visualLines[currentVisualLine + 1]?.logicalLine !== currentVL.logicalLine;
1383
+ const sourceMaxVisualCol = isLastSourceSegment ? currentVL.length : Math.max(0, currentVL.length - 1);
1384
+
1385
+ const isLastTargetSegment =
1386
+ targetVisualLine === visualLines.length - 1 ||
1387
+ visualLines[targetVisualLine + 1]?.logicalLine !== targetVL.logicalLine;
1388
+ const targetMaxVisualCol = isLastTargetSegment ? targetVL.length : Math.max(0, targetVL.length - 1);
1389
+
1390
+ const moveToVisualCol = this.computeVerticalMoveColumn(currentVisualCol, sourceMaxVisualCol, targetMaxVisualCol);
1391
+
1392
+ // Set cursor position
1393
+ this.state.cursorLine = targetVL.logicalLine;
1394
+ const targetCol = targetVL.startCol + moveToVisualCol;
1395
+ const logicalLine = this.state.lines[targetVL.logicalLine] || "";
1396
+ this.state.cursorCol = Math.min(targetCol, logicalLine.length);
1397
+
1398
+ // Snap cursor to atomic segment boundary (e.g. paste markers)
1399
+ // so the cursor never lands in the middle of a multi-grapheme unit.
1400
+ // Single-grapheme segments don't need snapping.
1401
+ const segments = [...this.segment(logicalLine, "grapheme")];
1402
+ for (const seg of segments) {
1403
+ if (seg.index > this.state.cursorCol) break;
1404
+ if (seg.segment.length <= 1) continue;
1405
+ if (this.state.cursorCol < seg.index + seg.segment.length) {
1406
+ const isContinuation = seg.index < targetVL.startCol;
1407
+ const isMovingDown = targetVisualLine > currentVisualLine;
1408
+
1409
+ if (isContinuation && isMovingDown) {
1410
+ // The segment started on a previous visual line, and we
1411
+ // already visited it on the way down. Skip all remaining
1412
+ // continuation VLs and land on the first VL past it.
1413
+ const segEnd = seg.index + seg.segment.length;
1414
+ let next = targetVisualLine + 1;
1415
+ while (
1416
+ next < visualLines.length &&
1417
+ visualLines[next].logicalLine === targetVL.logicalLine &&
1418
+ visualLines[next].startCol < segEnd
1419
+ ) {
1420
+ next++;
1421
+ }
1422
+ if (next < visualLines.length) {
1423
+ this.moveToVisualLine(visualLines, currentVisualLine, next);
1424
+ return;
1425
+ }
1426
+ }
1427
+
1428
+ // Snap to the start of the segment so it gets highlighted.
1429
+ // Store the pre-snap position so the next vertical move can
1430
+ // resolve it to the correct visual column.
1431
+ this.snappedFromCursorCol = this.state.cursorCol;
1432
+ this.state.cursorCol = seg.index;
1433
+ return;
1434
+ }
1435
+ }
1436
+
1437
+ // No snap occurred – we moved out of the atomic segment.
1438
+ this.snappedFromCursorCol = null;
1439
+ }
1440
+
1441
+ /**
1442
+ * Compute the target visual column for vertical cursor movement.
1443
+ * Implements the sticky column decision table:
1444
+ *
1445
+ * | P | S | T | U | Scenario | Set Preferred | Move To |
1446
+ * |---|---|---|---| ---------------------------------------------------- |---------------|-------------|
1447
+ * | 0 | * | 0 | - | Start nav, target fits | null | current |
1448
+ * | 0 | * | 1 | - | Start nav, target shorter | current | target end |
1449
+ * | 1 | 0 | 0 | 0 | Clamped, target fits preferred | null | preferred |
1450
+ * | 1 | 0 | 0 | 1 | Clamped, target longer but still can't fit preferred | keep | target end |
1451
+ * | 1 | 0 | 1 | - | Clamped, target even shorter | keep | target end |
1452
+ * | 1 | 1 | 0 | - | Rewrapped, target fits current | null | current |
1453
+ * | 1 | 1 | 1 | - | Rewrapped, target shorter than current | current | target end |
1454
+ *
1455
+ * Where:
1456
+ * - P = preferred col is set
1457
+ * - S = cursor in middle of source line (not clamped to end)
1458
+ * - T = target line shorter than current visual col
1459
+ * - U = target line shorter than preferred col
1460
+ */
1461
+ private computeVerticalMoveColumn(
1462
+ currentVisualCol: number,
1463
+ sourceMaxVisualCol: number,
1464
+ targetMaxVisualCol: number,
1465
+ ): number {
1466
+ const hasPreferred = this.preferredVisualCol !== null; // P
1467
+ const cursorInMiddle = currentVisualCol < sourceMaxVisualCol; // S
1468
+ const targetTooShort = targetMaxVisualCol < currentVisualCol; // T
1469
+
1470
+ if (!hasPreferred || cursorInMiddle) {
1471
+ if (targetTooShort) {
1472
+ // Cases 2 and 7
1473
+ this.preferredVisualCol = currentVisualCol;
1474
+ return targetMaxVisualCol;
1475
+ }
1476
+
1477
+ // Cases 1 and 6
1478
+ this.preferredVisualCol = null;
1479
+ return currentVisualCol;
1480
+ }
1481
+
1482
+ const targetCantFitPreferred = targetMaxVisualCol < this.preferredVisualCol!; // U
1483
+ if (targetTooShort || targetCantFitPreferred) {
1484
+ // Cases 4 and 5
1485
+ return targetMaxVisualCol;
1486
+ }
1487
+
1488
+ // Case 3
1489
+ const result = this.preferredVisualCol!;
1490
+ this.preferredVisualCol = null;
1491
+ return result;
1492
+ }
1493
+
1494
+ private moveToLineStart(): void {
1495
+ this.lastAction = null;
1496
+ this.setCursorCol(0);
1497
+ }
1498
+
1499
+ private moveToLineEnd(): void {
1500
+ this.lastAction = null;
1501
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1502
+ this.setCursorCol(currentLine.length);
1503
+ }
1504
+
1505
+ private deleteToStartOfLine(): void {
1506
+ this.exitHistoryBrowsing();
1507
+
1508
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1509
+
1510
+ if (this.state.cursorCol > 0) {
1511
+ this.pushUndoSnapshot();
1512
+
1513
+ // Calculate text to be deleted and save to kill ring (backward deletion = prepend)
1514
+ const deletedText = currentLine.slice(0, this.state.cursorCol);
1515
+ this.killRing.push(deletedText, { prepend: true, accumulate: this.lastAction === "kill" });
1516
+ this.lastAction = "kill";
1517
+
1518
+ // Delete from start of line up to cursor
1519
+ this.state.lines[this.state.cursorLine] = currentLine.slice(this.state.cursorCol);
1520
+ this.setCursorCol(0);
1521
+ } else if (this.state.cursorLine > 0) {
1522
+ this.pushUndoSnapshot();
1523
+
1524
+ // At start of line - merge with previous line, treating newline as deleted text
1525
+ this.killRing.push("\n", { prepend: true, accumulate: this.lastAction === "kill" });
1526
+ this.lastAction = "kill";
1527
+
1528
+ const previousLine = this.state.lines[this.state.cursorLine - 1] || "";
1529
+ this.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;
1530
+ this.state.lines.splice(this.state.cursorLine, 1);
1531
+ this.state.cursorLine--;
1532
+ this.setCursorCol(previousLine.length);
1533
+ }
1534
+
1535
+ if (this.onChange) {
1536
+ this.onChange(this.getText());
1537
+ }
1538
+ }
1539
+
1540
+ private deleteToEndOfLine(): void {
1541
+ this.exitHistoryBrowsing();
1542
+
1543
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1544
+
1545
+ if (this.state.cursorCol < currentLine.length) {
1546
+ this.pushUndoSnapshot();
1547
+
1548
+ // Calculate text to be deleted and save to kill ring (forward deletion = append)
1549
+ const deletedText = currentLine.slice(this.state.cursorCol);
1550
+ this.killRing.push(deletedText, { prepend: false, accumulate: this.lastAction === "kill" });
1551
+ this.lastAction = "kill";
1552
+
1553
+ // Delete from cursor to end of line
1554
+ this.state.lines[this.state.cursorLine] = currentLine.slice(0, this.state.cursorCol);
1555
+ } else if (this.state.cursorLine < this.state.lines.length - 1) {
1556
+ this.pushUndoSnapshot();
1557
+
1558
+ // At end of line - merge with next line, treating newline as deleted text
1559
+ this.killRing.push("\n", { prepend: false, accumulate: this.lastAction === "kill" });
1560
+ this.lastAction = "kill";
1561
+
1562
+ const nextLine = this.state.lines[this.state.cursorLine + 1] || "";
1563
+ this.state.lines[this.state.cursorLine] = currentLine + nextLine;
1564
+ this.state.lines.splice(this.state.cursorLine + 1, 1);
1565
+ }
1566
+
1567
+ if (this.onChange) {
1568
+ this.onChange(this.getText());
1569
+ }
1570
+ }
1571
+
1572
+ private deleteWordBackwards(): void {
1573
+ this.exitHistoryBrowsing();
1574
+
1575
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1576
+
1577
+ // If at start of line, behave like backspace at column 0 (merge with previous line)
1578
+ if (this.state.cursorCol === 0) {
1579
+ if (this.state.cursorLine > 0) {
1580
+ this.pushUndoSnapshot();
1581
+
1582
+ // Treat newline as deleted text (backward deletion = prepend)
1583
+ this.killRing.push("\n", { prepend: true, accumulate: this.lastAction === "kill" });
1584
+ this.lastAction = "kill";
1585
+
1586
+ const previousLine = this.state.lines[this.state.cursorLine - 1] || "";
1587
+ this.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;
1588
+ this.state.lines.splice(this.state.cursorLine, 1);
1589
+ this.state.cursorLine--;
1590
+ this.setCursorCol(previousLine.length);
1591
+ }
1592
+ } else {
1593
+ this.pushUndoSnapshot();
1594
+
1595
+ // Save lastAction before cursor movement (moveWordBackwards resets it)
1596
+ const wasKill = this.lastAction === "kill";
1597
+
1598
+ const oldCursorCol = this.state.cursorCol;
1599
+ this.moveWordBackwards();
1600
+ const deleteFrom = this.state.cursorCol;
1601
+ this.setCursorCol(oldCursorCol);
1602
+
1603
+ const deletedText = currentLine.slice(deleteFrom, this.state.cursorCol);
1604
+ this.killRing.push(deletedText, { prepend: true, accumulate: wasKill });
1605
+ this.lastAction = "kill";
1606
+
1607
+ this.state.lines[this.state.cursorLine] =
1608
+ currentLine.slice(0, deleteFrom) + currentLine.slice(this.state.cursorCol);
1609
+ this.setCursorCol(deleteFrom);
1610
+ }
1611
+
1612
+ if (this.onChange) {
1613
+ this.onChange(this.getText());
1614
+ }
1615
+ }
1616
+
1617
+ private deleteWordForward(): void {
1618
+ this.exitHistoryBrowsing();
1619
+
1620
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1621
+
1622
+ // If at end of line, merge with next line (delete the newline)
1623
+ if (this.state.cursorCol >= currentLine.length) {
1624
+ if (this.state.cursorLine < this.state.lines.length - 1) {
1625
+ this.pushUndoSnapshot();
1626
+
1627
+ // Treat newline as deleted text (forward deletion = append)
1628
+ this.killRing.push("\n", { prepend: false, accumulate: this.lastAction === "kill" });
1629
+ this.lastAction = "kill";
1630
+
1631
+ const nextLine = this.state.lines[this.state.cursorLine + 1] || "";
1632
+ this.state.lines[this.state.cursorLine] = currentLine + nextLine;
1633
+ this.state.lines.splice(this.state.cursorLine + 1, 1);
1634
+ }
1635
+ } else {
1636
+ this.pushUndoSnapshot();
1637
+
1638
+ // Save lastAction before cursor movement (moveWordForwards resets it)
1639
+ const wasKill = this.lastAction === "kill";
1640
+
1641
+ const oldCursorCol = this.state.cursorCol;
1642
+ this.moveWordForwards();
1643
+ const deleteTo = this.state.cursorCol;
1644
+ this.setCursorCol(oldCursorCol);
1645
+
1646
+ const deletedText = currentLine.slice(this.state.cursorCol, deleteTo);
1647
+ this.killRing.push(deletedText, { prepend: false, accumulate: wasKill });
1648
+ this.lastAction = "kill";
1649
+
1650
+ this.state.lines[this.state.cursorLine] =
1651
+ currentLine.slice(0, this.state.cursorCol) + currentLine.slice(deleteTo);
1652
+ }
1653
+
1654
+ if (this.onChange) {
1655
+ this.onChange(this.getText());
1656
+ }
1657
+ }
1658
+
1659
+ private handleForwardDelete(): void {
1660
+ this.exitHistoryBrowsing();
1661
+ this.lastAction = null;
1662
+
1663
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1664
+
1665
+ if (this.state.cursorCol < currentLine.length) {
1666
+ this.pushUndoSnapshot();
1667
+
1668
+ // Delete grapheme at cursor position (handles emojis, combining characters, etc.)
1669
+ const afterCursor = currentLine.slice(this.state.cursorCol);
1670
+
1671
+ // Find the first grapheme at cursor
1672
+ const graphemes = [...this.segment(afterCursor, "grapheme")];
1673
+ const firstGrapheme = graphemes[0];
1674
+ const graphemeLength = firstGrapheme ? firstGrapheme.segment.length : 1;
1675
+
1676
+ const before = currentLine.slice(0, this.state.cursorCol);
1677
+ const after = currentLine.slice(this.state.cursorCol + graphemeLength);
1678
+ this.state.lines[this.state.cursorLine] = before + after;
1679
+ } else if (this.state.cursorLine < this.state.lines.length - 1) {
1680
+ this.pushUndoSnapshot();
1681
+
1682
+ // At end of line - merge with next line
1683
+ const nextLine = this.state.lines[this.state.cursorLine + 1] || "";
1684
+ this.state.lines[this.state.cursorLine] = currentLine + nextLine;
1685
+ this.state.lines.splice(this.state.cursorLine + 1, 1);
1686
+ }
1687
+
1688
+ if (this.onChange) {
1689
+ this.onChange(this.getText());
1690
+ }
1691
+
1692
+ // Update or re-trigger autocomplete after forward delete
1693
+ if (this.autocompleteState) {
1694
+ this.updateAutocomplete();
1695
+ } else {
1696
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1697
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
1698
+ // Slash command context
1699
+ if (this.isInSlashCommandContext(textBeforeCursor)) {
1700
+ this.tryTriggerAutocomplete();
1701
+ }
1702
+ // Symbol-based completion context like @, #, or provider triggers
1703
+ else if (this.autocompleteTriggerPattern.test(textBeforeCursor)) {
1704
+ this.tryTriggerAutocomplete();
1705
+ }
1706
+ }
1707
+ }
1708
+
1709
+ /**
1710
+ * Build a mapping from visual lines to logical positions.
1711
+ * Returns an array where each element represents a visual line with:
1712
+ * - logicalLine: index into this.state.lines
1713
+ * - startCol: starting column in the logical line
1714
+ * - length: length of this visual line segment
1715
+ */
1716
+ private buildVisualLineMap(width: number): Array<{ logicalLine: number; startCol: number; length: number }> {
1717
+ const visualLines: Array<{ logicalLine: number; startCol: number; length: number }> = [];
1718
+
1719
+ for (let i = 0; i < this.state.lines.length; i++) {
1720
+ const line = this.state.lines[i] || "";
1721
+ const lineVisWidth = visibleWidth(line);
1722
+ if (line.length === 0) {
1723
+ // Empty line still takes one visual line
1724
+ visualLines.push({ logicalLine: i, startCol: 0, length: 0 });
1725
+ } else if (lineVisWidth <= width) {
1726
+ visualLines.push({ logicalLine: i, startCol: 0, length: line.length });
1727
+ } else {
1728
+ // Line needs wrapping - use word-aware wrapping
1729
+ const chunks = wordWrapLine(line, width, [...this.segment(line, "grapheme")]);
1730
+ for (const chunk of chunks) {
1731
+ visualLines.push({
1732
+ logicalLine: i,
1733
+ startCol: chunk.startIndex,
1734
+ length: chunk.endIndex - chunk.startIndex,
1735
+ });
1736
+ }
1737
+ }
1738
+ }
1739
+
1740
+ return visualLines;
1741
+ }
1742
+
1743
+ /**
1744
+ * Find the visual line index that contains the given logical position.
1745
+ */
1746
+ private findVisualLineAt(
1747
+ visualLines: Array<{ logicalLine: number; startCol: number; length: number }>,
1748
+ line: number,
1749
+ col: number,
1750
+ ): number {
1751
+ for (let i = 0; i < visualLines.length; i++) {
1752
+ const vl = visualLines[i];
1753
+ if (!vl || vl.logicalLine !== line) continue;
1754
+ const offset = col - vl.startCol;
1755
+ // Cursor is in this segment if it's within range. For the last
1756
+ // segment of a logical line, cursor can be at length (end position)
1757
+ const isLastSegmentOfLine = i === visualLines.length - 1 || visualLines[i + 1]?.logicalLine !== vl.logicalLine;
1758
+ if (offset >= 0 && (offset < vl.length || (isLastSegmentOfLine && offset === vl.length))) {
1759
+ return i;
1760
+ }
1761
+ }
1762
+ return visualLines.length - 1;
1763
+ }
1764
+
1765
+ /**
1766
+ * Find the visual line index for the current cursor position.
1767
+ */
1768
+ private findCurrentVisualLine(
1769
+ visualLines: Array<{ logicalLine: number; startCol: number; length: number }>,
1770
+ ): number {
1771
+ return this.findVisualLineAt(visualLines, this.state.cursorLine, this.state.cursorCol);
1772
+ }
1773
+
1774
+ private moveCursor(deltaLine: number, deltaCol: number): void {
1775
+ this.lastAction = null;
1776
+ const visualLines = this.buildVisualLineMap(this.lastWidth);
1777
+ const currentVisualLine = this.findCurrentVisualLine(visualLines);
1778
+
1779
+ if (deltaLine !== 0) {
1780
+ const targetVisualLine = currentVisualLine + deltaLine;
1781
+
1782
+ if (targetVisualLine >= 0 && targetVisualLine < visualLines.length) {
1783
+ this.moveToVisualLine(visualLines, currentVisualLine, targetVisualLine);
1784
+ }
1785
+ }
1786
+
1787
+ if (deltaCol !== 0) {
1788
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1789
+
1790
+ if (deltaCol > 0) {
1791
+ // Moving right - move by one grapheme (handles emojis, combining characters, etc.)
1792
+ if (this.state.cursorCol < currentLine.length) {
1793
+ const afterCursor = currentLine.slice(this.state.cursorCol);
1794
+ const graphemes = [...this.segment(afterCursor, "grapheme")];
1795
+ const firstGrapheme = graphemes[0];
1796
+ this.setCursorCol(this.state.cursorCol + (firstGrapheme ? firstGrapheme.segment.length : 1));
1797
+ } else if (this.state.cursorLine < this.state.lines.length - 1) {
1798
+ // Wrap to start of next logical line
1799
+ this.state.cursorLine++;
1800
+ this.setCursorCol(0);
1801
+ } else {
1802
+ // At end of last line - can't move, but set preferredVisualCol for up/down navigation
1803
+ const currentVL = visualLines[currentVisualLine];
1804
+ if (currentVL) {
1805
+ this.preferredVisualCol = this.state.cursorCol - currentVL.startCol;
1806
+ }
1807
+ }
1808
+ } else {
1809
+ // Moving left - move by one grapheme (handles emojis, combining characters, etc.)
1810
+ if (this.state.cursorCol > 0) {
1811
+ const beforeCursor = currentLine.slice(0, this.state.cursorCol);
1812
+ const graphemes = [...this.segment(beforeCursor, "grapheme")];
1813
+ const lastGrapheme = graphemes[graphemes.length - 1];
1814
+ this.setCursorCol(this.state.cursorCol - (lastGrapheme ? lastGrapheme.segment.length : 1));
1815
+ } else if (this.state.cursorLine > 0) {
1816
+ // Wrap to end of previous logical line
1817
+ this.state.cursorLine--;
1818
+ const prevLine = this.state.lines[this.state.cursorLine] || "";
1819
+ this.setCursorCol(prevLine.length);
1820
+ }
1821
+ }
1822
+ }
1823
+
1824
+ // Keep an open autocomplete picker in sync with the new cursor
1825
+ // position: cursor movement changes the text before the cursor, so a
1826
+ // picker computed for the old position is stale. Re-query so it
1827
+ // refreshes — or closes when the new position yields no suggestions —
1828
+ // mirroring insertCharacter()/handleBackspace(). Without this, arrowing
1829
+ // left from `/cmd ` back into the command name leaves the argument
1830
+ // picker showing against a `/cmd` prefix (and a Tab there would
1831
+ // concatenate the stale suggestion onto the partial command name).
1832
+ if (this.autocompleteState) {
1833
+ this.updateAutocomplete();
1834
+ }
1835
+ }
1836
+
1837
+ /**
1838
+ * Scroll by a page (direction: -1 for up, 1 for down).
1839
+ * Moves cursor by the page size while keeping it in bounds.
1840
+ */
1841
+ private pageScroll(direction: -1 | 1): void {
1842
+ this.lastAction = null;
1843
+ const terminalRows = this.tui.terminal.rows;
1844
+ const pageSize = Math.max(5, Math.floor(terminalRows * 0.3));
1845
+
1846
+ const visualLines = this.buildVisualLineMap(this.lastWidth);
1847
+ const currentVisualLine = this.findCurrentVisualLine(visualLines);
1848
+ const targetVisualLine = Math.max(0, Math.min(visualLines.length - 1, currentVisualLine + direction * pageSize));
1849
+
1850
+ this.moveToVisualLine(visualLines, currentVisualLine, targetVisualLine);
1851
+ }
1852
+
1853
+ private moveWordBackwards(): void {
1854
+ this.lastAction = null;
1855
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1856
+
1857
+ // If at start of line, move to end of previous line
1858
+ if (this.state.cursorCol === 0) {
1859
+ if (this.state.cursorLine > 0) {
1860
+ this.state.cursorLine--;
1861
+ const prevLine = this.state.lines[this.state.cursorLine] || "";
1862
+ this.setCursorCol(prevLine.length);
1863
+ }
1864
+ return;
1865
+ }
1866
+
1867
+ this.setCursorCol(
1868
+ findWordBackward(currentLine, this.state.cursorCol, {
1869
+ segment: (text) => this.segment(text, "word"),
1870
+ isAtomicSegment: isPasteMarker,
1871
+ }),
1872
+ );
1873
+ }
1874
+
1875
+ /**
1876
+ * Yank (paste) the most recent kill ring entry at cursor position.
1877
+ */
1878
+ private yank(): void {
1879
+ if (this.killRing.length === 0) return;
1880
+
1881
+ this.pushUndoSnapshot();
1882
+
1883
+ const text = this.killRing.peek()!;
1884
+ this.insertYankedText(text);
1885
+
1886
+ this.lastAction = "yank";
1887
+ }
1888
+
1889
+ /**
1890
+ * Cycle through kill ring (only works immediately after yank or yank-pop).
1891
+ * Replaces the last yanked text with the previous entry in the ring.
1892
+ */
1893
+ private yankPop(): void {
1894
+ // Only works if we just yanked and have more than one entry
1895
+ if (this.lastAction !== "yank" || this.killRing.length <= 1) return;
1896
+
1897
+ this.pushUndoSnapshot();
1898
+
1899
+ // Delete the previously yanked text (still at end of ring before rotation)
1900
+ this.deleteYankedText();
1901
+
1902
+ // Rotate the ring: move end to front
1903
+ this.killRing.rotate();
1904
+
1905
+ // Insert the new most recent entry (now at end after rotation)
1906
+ const text = this.killRing.peek()!;
1907
+ this.insertYankedText(text);
1908
+
1909
+ this.lastAction = "yank";
1910
+ }
1911
+
1912
+ /**
1913
+ * Insert text at cursor position (used by yank operations).
1914
+ */
1915
+ private insertYankedText(text: string): void {
1916
+ this.exitHistoryBrowsing();
1917
+ const lines = text.split("\n");
1918
+
1919
+ if (lines.length === 1) {
1920
+ // Single line - insert at cursor
1921
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1922
+ const before = currentLine.slice(0, this.state.cursorCol);
1923
+ const after = currentLine.slice(this.state.cursorCol);
1924
+ this.state.lines[this.state.cursorLine] = before + text + after;
1925
+ this.setCursorCol(this.state.cursorCol + text.length);
1926
+ } else {
1927
+ // Multi-line insert
1928
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1929
+ const before = currentLine.slice(0, this.state.cursorCol);
1930
+ const after = currentLine.slice(this.state.cursorCol);
1931
+
1932
+ // First line merges with text before cursor
1933
+ this.state.lines[this.state.cursorLine] = before + (lines[0] || "");
1934
+
1935
+ // Insert middle lines
1936
+ for (let i = 1; i < lines.length - 1; i++) {
1937
+ this.state.lines.splice(this.state.cursorLine + i, 0, lines[i] || "");
1938
+ }
1939
+
1940
+ // Last line merges with text after cursor
1941
+ const lastLineIndex = this.state.cursorLine + lines.length - 1;
1942
+ this.state.lines.splice(lastLineIndex, 0, (lines[lines.length - 1] || "") + after);
1943
+
1944
+ // Update cursor position
1945
+ this.state.cursorLine = lastLineIndex;
1946
+ this.setCursorCol((lines[lines.length - 1] || "").length);
1947
+ }
1948
+
1949
+ if (this.onChange) {
1950
+ this.onChange(this.getText());
1951
+ }
1952
+ }
1953
+
1954
+ /**
1955
+ * Delete the previously yanked text (used by yank-pop).
1956
+ * The yanked text is derived from killRing[end] since it hasn't been rotated yet.
1957
+ */
1958
+ private deleteYankedText(): void {
1959
+ const yankedText = this.killRing.peek();
1960
+ if (!yankedText) return;
1961
+
1962
+ const yankLines = yankedText.split("\n");
1963
+
1964
+ if (yankLines.length === 1) {
1965
+ // Single line - delete backward from cursor
1966
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1967
+ const deleteLen = yankedText.length;
1968
+ const before = currentLine.slice(0, this.state.cursorCol - deleteLen);
1969
+ const after = currentLine.slice(this.state.cursorCol);
1970
+ this.state.lines[this.state.cursorLine] = before + after;
1971
+ this.setCursorCol(this.state.cursorCol - deleteLen);
1972
+ } else {
1973
+ // Multi-line delete - cursor is at end of last yanked line
1974
+ const startLine = this.state.cursorLine - (yankLines.length - 1);
1975
+ const startCol = (this.state.lines[startLine] || "").length - (yankLines[0] || "").length;
1976
+
1977
+ // Get text after cursor on current line
1978
+ const afterCursor = (this.state.lines[this.state.cursorLine] || "").slice(this.state.cursorCol);
1979
+
1980
+ // Get text before yank start position
1981
+ const beforeYank = (this.state.lines[startLine] || "").slice(0, startCol);
1982
+
1983
+ // Remove all lines from startLine to cursorLine and replace with merged line
1984
+ this.state.lines.splice(startLine, yankLines.length, beforeYank + afterCursor);
1985
+
1986
+ // Update cursor
1987
+ this.state.cursorLine = startLine;
1988
+ this.setCursorCol(startCol);
1989
+ }
1990
+
1991
+ if (this.onChange) {
1992
+ this.onChange(this.getText());
1993
+ }
1994
+ }
1995
+
1996
+ private pushUndoSnapshot(): void {
1997
+ this.undoStack.push(this.state);
1998
+ }
1999
+
2000
+ private undo(): void {
2001
+ this.exitHistoryBrowsing();
2002
+ const snapshot = this.undoStack.pop();
2003
+ if (!snapshot) return;
2004
+ Object.assign(this.state, snapshot);
2005
+ this.lastAction = null;
2006
+ this.preferredVisualCol = null;
2007
+ if (this.onChange) {
2008
+ this.onChange(this.getText());
2009
+ }
2010
+ }
2011
+
2012
+ /**
2013
+ * Jump to the first occurrence of a character in the specified direction.
2014
+ * Multi-line search. Case-sensitive. Skips the current cursor position.
2015
+ */
2016
+ private jumpToChar(char: string, direction: "forward" | "backward"): void {
2017
+ this.lastAction = null;
2018
+ const isForward = direction === "forward";
2019
+ const lines = this.state.lines;
2020
+
2021
+ const end = isForward ? lines.length : -1;
2022
+ const step = isForward ? 1 : -1;
2023
+
2024
+ for (let lineIdx = this.state.cursorLine; lineIdx !== end; lineIdx += step) {
2025
+ const line = lines[lineIdx] || "";
2026
+ const isCurrentLine = lineIdx === this.state.cursorLine;
2027
+
2028
+ // Current line: start after/before cursor; other lines: search full line
2029
+ const searchFrom = isCurrentLine
2030
+ ? isForward
2031
+ ? this.state.cursorCol + 1
2032
+ : this.state.cursorCol - 1
2033
+ : undefined;
2034
+
2035
+ const idx = isForward ? line.indexOf(char, searchFrom) : line.lastIndexOf(char, searchFrom);
2036
+
2037
+ if (idx !== -1) {
2038
+ this.state.cursorLine = lineIdx;
2039
+ this.setCursorCol(idx);
2040
+ return;
2041
+ }
2042
+ }
2043
+ // No match found - cursor stays in place
2044
+ }
2045
+
2046
+ private moveWordForwards(): void {
2047
+ this.lastAction = null;
2048
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
2049
+
2050
+ // If at end of line, move to start of next line
2051
+ if (this.state.cursorCol >= currentLine.length) {
2052
+ if (this.state.cursorLine < this.state.lines.length - 1) {
2053
+ this.state.cursorLine++;
2054
+ this.setCursorCol(0);
2055
+ }
2056
+ return;
2057
+ }
2058
+
2059
+ this.setCursorCol(
2060
+ findWordForward(currentLine, this.state.cursorCol, {
2061
+ segment: (text) => this.segment(text, "word"),
2062
+ isAtomicSegment: isPasteMarker,
2063
+ }),
2064
+ );
2065
+ }
2066
+
2067
+ // Slash menu only allowed on the first line of the editor
2068
+ private isSlashMenuAllowed(): boolean {
2069
+ return this.state.cursorLine === 0;
2070
+ }
2071
+
2072
+ // Helper method to check if cursor is at start of message (for slash command detection)
2073
+ private isAtStartOfMessage(): boolean {
2074
+ if (!this.isSlashMenuAllowed()) return false;
2075
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
2076
+ const beforeCursor = currentLine.slice(0, this.state.cursorCol);
2077
+ return beforeCursor.trim() === "" || beforeCursor.trim() === "/";
2078
+ }
2079
+
2080
+ private isInSlashCommandContext(textBeforeCursor: string): boolean {
2081
+ return this.isSlashMenuAllowed() && textBeforeCursor.trimStart().startsWith("/");
2082
+ }
2083
+
2084
+ // Autocomplete methods
2085
+ /**
2086
+ * Find the best autocomplete item index for the given prefix.
2087
+ * Returns -1 if no match is found.
2088
+ *
2089
+ * Match priority:
2090
+ * 1. Exact match (prefix === item.value) -> always selected
2091
+ * 2. Prefix match -> first item whose value starts with prefix
2092
+ * 3. No match -> -1 (keep default highlight)
2093
+ *
2094
+ * Matching is case-sensitive and checks item.value only.
2095
+ */
2096
+ private getBestAutocompleteMatchIndex(items: Array<{ value: string; label: string }>, prefix: string): number {
2097
+ if (!prefix) return -1;
2098
+
2099
+ let firstPrefixIndex = -1;
2100
+
2101
+ for (let i = 0; i < items.length; i++) {
2102
+ const value = items[i]!.value;
2103
+ if (value === prefix) {
2104
+ return i; // Exact match always wins
2105
+ }
2106
+ if (firstPrefixIndex === -1 && value.startsWith(prefix)) {
2107
+ firstPrefixIndex = i;
2108
+ }
2109
+ }
2110
+
2111
+ return firstPrefixIndex;
2112
+ }
2113
+
2114
+ private createAutocompleteList(
2115
+ prefix: string,
2116
+ items: Array<{ value: string; label: string; description?: string }>,
2117
+ ): SelectList {
2118
+ const layout = prefix.startsWith("/") ? SLASH_COMMAND_SELECT_LIST_LAYOUT : undefined;
2119
+ return new SelectList(items, this.autocompleteMaxVisible, this.theme.selectList, layout);
2120
+ }
2121
+
2122
+ private tryTriggerAutocomplete(explicitTab: boolean = false): void {
2123
+ this.requestAutocomplete({ force: false, explicitTab });
2124
+ }
2125
+
2126
+ private handleTabCompletion(): void {
2127
+ if (!this.autocompleteProvider) return;
2128
+
2129
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
2130
+ const beforeCursor = currentLine.slice(0, this.state.cursorCol);
2131
+
2132
+ if (this.isInSlashCommandContext(beforeCursor) && !beforeCursor.trimStart().includes(" ")) {
2133
+ this.handleSlashCommandCompletion();
2134
+ } else {
2135
+ this.forceFileAutocomplete(true);
2136
+ }
2137
+ }
2138
+
2139
+ private handleSlashCommandCompletion(): void {
2140
+ this.requestAutocomplete({ force: false, explicitTab: true });
2141
+ }
2142
+
2143
+ private forceFileAutocomplete(explicitTab: boolean = false): void {
2144
+ this.requestAutocomplete({ force: true, explicitTab });
2145
+ }
2146
+
2147
+ private requestAutocomplete(options: { force: boolean; explicitTab: boolean }): void {
2148
+ if (!this.autocompleteProvider) return;
2149
+
2150
+ if (options.force) {
2151
+ const shouldTrigger =
2152
+ !this.autocompleteProvider.shouldTriggerFileCompletion ||
2153
+ this.autocompleteProvider.shouldTriggerFileCompletion(
2154
+ this.state.lines,
2155
+ this.state.cursorLine,
2156
+ this.state.cursorCol,
2157
+ );
2158
+ if (!shouldTrigger) {
2159
+ return;
2160
+ }
2161
+ }
2162
+
2163
+ this.cancelAutocompleteRequest();
2164
+ const startToken = ++this.autocompleteStartToken;
2165
+
2166
+ const debounceMs = this.getAutocompleteDebounceMs(options);
2167
+ if (debounceMs > 0) {
2168
+ this.autocompleteDebounceTimer = setTimeout(() => {
2169
+ this.autocompleteDebounceTimer = undefined;
2170
+ void this.startAutocompleteRequest(startToken, options);
2171
+ }, debounceMs);
2172
+ return;
2173
+ }
2174
+
2175
+ void this.startAutocompleteRequest(startToken, options);
2176
+ }
2177
+
2178
+ private async startAutocompleteRequest(
2179
+ startToken: number,
2180
+ options: { force: boolean; explicitTab: boolean },
2181
+ ): Promise<void> {
2182
+ const previousTask = this.autocompleteRequestTask;
2183
+ this.autocompleteRequestTask = (async () => {
2184
+ await previousTask;
2185
+ if (startToken !== this.autocompleteStartToken || !this.autocompleteProvider) {
2186
+ return;
2187
+ }
2188
+
2189
+ const controller = new AbortController();
2190
+ this.autocompleteAbort = controller;
2191
+ const requestId = ++this.autocompleteRequestId;
2192
+ const snapshotText = this.getText();
2193
+ const snapshotLine = this.state.cursorLine;
2194
+ const snapshotCol = this.state.cursorCol;
2195
+
2196
+ await this.runAutocompleteRequest(requestId, controller, snapshotText, snapshotLine, snapshotCol, options);
2197
+ })();
2198
+ await this.autocompleteRequestTask;
2199
+ }
2200
+
2201
+ private setAutocompleteTriggerCharacters(triggerCharacters: string[]): void {
2202
+ const next = [...DEFAULT_AUTOCOMPLETE_TRIGGER_CHARACTERS];
2203
+ for (const character of triggerCharacters) {
2204
+ if (character.length !== 1 || character === "/" || isWhitespaceChar(character) || next.includes(character)) {
2205
+ continue;
2206
+ }
2207
+ next.push(character);
2208
+ }
2209
+ this.autocompleteTriggerCharacters = next;
2210
+ this.autocompleteTriggerPattern = buildTriggerPattern(next);
2211
+ this.autocompleteDebouncePattern = buildDebouncePattern(next);
2212
+ }
2213
+
2214
+ private getAutocompleteDebounceMs(options: { force: boolean; explicitTab: boolean }): number {
2215
+ if (options.explicitTab || options.force) {
2216
+ return 0;
2217
+ }
2218
+
2219
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
2220
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
2221
+ return this.autocompleteDebouncePattern.test(textBeforeCursor) ? ATTACHMENT_AUTOCOMPLETE_DEBOUNCE_MS : 0;
2222
+ }
2223
+
2224
+ private async runAutocompleteRequest(
2225
+ requestId: number,
2226
+ controller: AbortController,
2227
+ snapshotText: string,
2228
+ snapshotLine: number,
2229
+ snapshotCol: number,
2230
+ options: { force: boolean; explicitTab: boolean },
2231
+ ): Promise<void> {
2232
+ if (!this.autocompleteProvider) return;
2233
+
2234
+ const suggestions = await this.autocompleteProvider.getSuggestions(
2235
+ this.state.lines,
2236
+ this.state.cursorLine,
2237
+ this.state.cursorCol,
2238
+ { signal: controller.signal, force: options.force },
2239
+ );
2240
+
2241
+ if (!this.isAutocompleteRequestCurrent(requestId, controller, snapshotText, snapshotLine, snapshotCol)) {
2242
+ return;
2243
+ }
2244
+
2245
+ this.autocompleteAbort = undefined;
2246
+
2247
+ if (!suggestions || !Array.isArray(suggestions.items) || suggestions.items.length === 0) {
2248
+ this.cancelAutocomplete();
2249
+ this.tui.requestRender();
2250
+ return;
2251
+ }
2252
+
2253
+ if (options.force && options.explicitTab && suggestions.items.length === 1) {
2254
+ const item = suggestions.items[0]!;
2255
+ this.pushUndoSnapshot();
2256
+ this.lastAction = null;
2257
+ const result = this.autocompleteProvider.applyCompletion(
2258
+ this.state.lines,
2259
+ this.state.cursorLine,
2260
+ this.state.cursorCol,
2261
+ item,
2262
+ suggestions.prefix,
2263
+ );
2264
+ this.state.lines = result.lines;
2265
+ this.state.cursorLine = result.cursorLine;
2266
+ this.setCursorCol(result.cursorCol);
2267
+ if (this.onChange) this.onChange(this.getText());
2268
+ this.tui.requestRender();
2269
+ return;
2270
+ }
2271
+
2272
+ this.applyAutocompleteSuggestions(suggestions, options.force ? "force" : "regular");
2273
+ this.tui.requestRender();
2274
+ }
2275
+
2276
+ private isAutocompleteRequestCurrent(
2277
+ requestId: number,
2278
+ controller: AbortController,
2279
+ snapshotText: string,
2280
+ snapshotLine: number,
2281
+ snapshotCol: number,
2282
+ ): boolean {
2283
+ return (
2284
+ !controller.signal.aborted &&
2285
+ requestId === this.autocompleteRequestId &&
2286
+ this.getText() === snapshotText &&
2287
+ this.state.cursorLine === snapshotLine &&
2288
+ this.state.cursorCol === snapshotCol
2289
+ );
2290
+ }
2291
+
2292
+ private applyAutocompleteSuggestions(suggestions: AutocompleteSuggestions, state: "regular" | "force"): void {
2293
+ this.autocompletePrefix = suggestions.prefix;
2294
+ this.autocompleteList = this.createAutocompleteList(suggestions.prefix, suggestions.items);
2295
+
2296
+ const bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);
2297
+ if (bestMatchIndex >= 0) {
2298
+ this.autocompleteList.setSelectedIndex(bestMatchIndex);
2299
+ }
2300
+
2301
+ this.autocompleteState = state;
2302
+ }
2303
+
2304
+ private cancelAutocompleteRequest(): void {
2305
+ this.autocompleteStartToken += 1;
2306
+ if (this.autocompleteDebounceTimer) {
2307
+ clearTimeout(this.autocompleteDebounceTimer);
2308
+ this.autocompleteDebounceTimer = undefined;
2309
+ }
2310
+ this.autocompleteAbort?.abort();
2311
+ this.autocompleteAbort = undefined;
2312
+ }
2313
+
2314
+ private clearAutocompleteUi(): void {
2315
+ this.autocompleteState = null;
2316
+ this.autocompleteList = undefined;
2317
+ this.autocompletePrefix = "";
2318
+ }
2319
+
2320
+ private cancelAutocomplete(): void {
2321
+ this.cancelAutocompleteRequest();
2322
+ this.clearAutocompleteUi();
2323
+ }
2324
+
2325
+ public isShowingAutocomplete(): boolean {
2326
+ return this.autocompleteState !== null;
2327
+ }
2328
+
2329
+ private updateAutocomplete(): void {
2330
+ if (!this.autocompleteState || !this.autocompleteProvider) return;
2331
+ this.requestAutocomplete({ force: this.autocompleteState === "force", explicitTab: false });
2332
+ }
2333
+ }