@scotthuang/agent-knock-knock 0.13.3 → 0.13.4

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 (259) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +13 -5
  3. package/dist/src/callback-outbox-policy.js +27 -6
  4. package/dist/src/callback-outbox-policy.js.map +1 -1
  5. package/dist/src/callback-outbox-service.d.ts +4 -30
  6. package/dist/src/callback-outbox-service.js +21 -154
  7. package/dist/src/callback-outbox-service.js.map +1 -1
  8. package/dist/src/callback-outbox-settlement.js +13 -8
  9. package/dist/src/callback-outbox-settlement.js.map +1 -1
  10. package/dist/src/callback-route-service.d.ts +46 -0
  11. package/dist/src/callback-route-service.js +155 -0
  12. package/dist/src/callback-route-service.js.map +1 -0
  13. package/dist/src/codex-process-incarnation.d.ts +8 -0
  14. package/dist/src/codex-process-incarnation.js +36 -0
  15. package/dist/src/codex-process-incarnation.js.map +1 -0
  16. package/dist/src/deferred-foreground-conflict-policy.d.ts +22 -0
  17. package/dist/src/deferred-foreground-conflict-policy.js +52 -0
  18. package/dist/src/deferred-foreground-conflict-policy.js.map +1 -0
  19. package/dist/src/durable-notification-kernel.d.ts +94 -0
  20. package/dist/src/durable-notification-kernel.js +68 -0
  21. package/dist/src/durable-notification-kernel.js.map +1 -0
  22. package/dist/src/herdr-socket-transport.d.ts +43 -0
  23. package/dist/src/herdr-socket-transport.js +170 -0
  24. package/dist/src/herdr-socket-transport.js.map +1 -0
  25. package/dist/src/herdr-terminal-control-provider.d.ts +2 -45
  26. package/dist/src/herdr-terminal-control-provider.js +2 -172
  27. package/dist/src/herdr-terminal-control-provider.js.map +1 -1
  28. package/dist/src/host-adapter-capabilities.d.ts +35 -0
  29. package/dist/src/host-adapter-capabilities.js +146 -0
  30. package/dist/src/host-adapter-capabilities.js.map +1 -0
  31. package/dist/src/host-adapter.d.ts +9 -3
  32. package/dist/src/host-adapter.js +33 -9
  33. package/dist/src/host-adapter.js.map +1 -1
  34. package/dist/src/host-bridge-tools.d.ts +3 -4
  35. package/dist/src/host-bridge-tools.js +49 -92
  36. package/dist/src/host-bridge-tools.js.map +1 -1
  37. package/dist/src/host-bridge.js +5 -5
  38. package/dist/src/host-bridge.js.map +1 -1
  39. package/dist/src/host-monitor-reconciliation.d.ts +6 -0
  40. package/dist/src/host-monitor-reconciliation.js +96 -0
  41. package/dist/src/host-monitor-reconciliation.js.map +1 -0
  42. package/dist/src/native-thread-lifecycle-cli-adapter.js +116 -60
  43. package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -1
  44. package/dist/src/openclaw-list-projection.d.ts +2 -0
  45. package/dist/src/openclaw-list-projection.js +3 -0
  46. package/dist/src/openclaw-list-projection.js.map +1 -0
  47. package/dist/src/openclaw-model-facing-field-policy.d.ts +2 -0
  48. package/dist/src/openclaw-model-facing-field-policy.js +3 -0
  49. package/dist/src/openclaw-model-facing-field-policy.js.map +1 -0
  50. package/dist/src/openclaw-plugin-command-adapter.d.ts +6 -29
  51. package/dist/src/openclaw-plugin-command-adapter.js +7 -2660
  52. package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
  53. package/dist/src/openclaw-plugin-helpers.d.ts +2 -148
  54. package/dist/src/openclaw-plugin-helpers.js +2 -1844
  55. package/dist/src/openclaw-plugin-helpers.js.map +1 -1
  56. package/dist/src/openclaw-plugin-schemas.d.ts +2 -563
  57. package/dist/src/openclaw-plugin-schemas.js +2 -572
  58. package/dist/src/openclaw-plugin-schemas.js.map +1 -1
  59. package/dist/src/openclaw-plugin-supervisor.d.ts +4 -5
  60. package/dist/src/openclaw-plugin-supervisor.js +5 -94
  61. package/dist/src/openclaw-plugin-supervisor.js.map +1 -1
  62. package/dist/src/openclaw-plugin-value-helpers.d.ts +2 -0
  63. package/dist/src/openclaw-plugin-value-helpers.js +3 -0
  64. package/dist/src/openclaw-plugin-value-helpers.js.map +1 -0
  65. package/dist/src/openclaw-private-authority-offers.d.ts +4 -39
  66. package/dist/src/openclaw-private-authority-offers.js +3 -258
  67. package/dist/src/openclaw-private-authority-offers.js.map +1 -1
  68. package/dist/src/semantic-private-authority-offers.d.ts +41 -0
  69. package/dist/src/semantic-private-authority-offers.js +261 -0
  70. package/dist/src/semantic-private-authority-offers.js.map +1 -0
  71. package/dist/src/semantic-tool-arguments.d.ts +9 -0
  72. package/dist/src/semantic-tool-arguments.js +64 -0
  73. package/dist/src/semantic-tool-arguments.js.map +1 -0
  74. package/dist/src/semantic-tool-catalog.d.ts +48 -0
  75. package/dist/src/semantic-tool-catalog.js +105 -0
  76. package/dist/src/semantic-tool-catalog.js.map +1 -0
  77. package/dist/src/semantic-tool-command-helpers.d.ts +145 -0
  78. package/dist/src/semantic-tool-command-helpers.js +1272 -0
  79. package/dist/src/semantic-tool-command-helpers.js.map +1 -0
  80. package/dist/src/semantic-tool-list-projection.d.ts +10 -0
  81. package/dist/src/semantic-tool-list-projection.js +497 -0
  82. package/dist/src/semantic-tool-list-projection.js.map +1 -0
  83. package/dist/src/semantic-tool-model-facing-field-policy.d.ts +3 -0
  84. package/dist/src/semantic-tool-model-facing-field-policy.js +66 -0
  85. package/dist/src/semantic-tool-model-facing-field-policy.js.map +1 -0
  86. package/dist/src/semantic-tool-presentation.d.ts +37 -0
  87. package/dist/src/semantic-tool-presentation.js +680 -0
  88. package/dist/src/semantic-tool-presentation.js.map +1 -0
  89. package/dist/src/semantic-tool-private-authority.d.ts +39 -0
  90. package/dist/src/semantic-tool-private-authority.js +602 -0
  91. package/dist/src/semantic-tool-private-authority.js.map +1 -0
  92. package/dist/src/semantic-tool-relay.d.ts +16 -0
  93. package/dist/src/semantic-tool-relay.js +175 -0
  94. package/dist/src/semantic-tool-relay.js.map +1 -0
  95. package/dist/src/semantic-tool-runtime.d.ts +10 -0
  96. package/dist/src/semantic-tool-runtime.js +1157 -0
  97. package/dist/src/semantic-tool-runtime.js.map +1 -0
  98. package/dist/src/semantic-tool-schemas.d.ts +563 -0
  99. package/dist/src/semantic-tool-schemas.js +573 -0
  100. package/dist/src/semantic-tool-schemas.js.map +1 -0
  101. package/dist/src/semantic-tool-value-helpers.d.ts +5 -0
  102. package/dist/src/semantic-tool-value-helpers.js +27 -0
  103. package/dist/src/semantic-tool-value-helpers.js.map +1 -0
  104. package/dist/src/store-event-presentation.d.ts +17 -0
  105. package/dist/src/store-event-presentation.js +33 -0
  106. package/dist/src/store-event-presentation.js.map +1 -0
  107. package/dist/src/store.d.ts +3 -16
  108. package/dist/src/store.js +2 -30
  109. package/dist/src/store.js.map +1 -1
  110. package/dist/src/terminal-action-projection.d.ts +2 -98
  111. package/dist/src/terminal-action-projection.js +13 -215
  112. package/dist/src/terminal-action-projection.js.map +1 -1
  113. package/dist/src/terminal-agent-bridge.d.ts +14 -340
  114. package/dist/src/terminal-agent-bridge.js +155 -3226
  115. package/dist/src/terminal-agent-bridge.js.map +1 -1
  116. package/dist/src/terminal-approval-cli-adapter.d.ts +17 -0
  117. package/dist/src/terminal-approval-cli-adapter.js +921 -0
  118. package/dist/src/terminal-approval-cli-adapter.js.map +1 -0
  119. package/dist/src/terminal-command-cli-adapter.d.ts +2 -429
  120. package/dist/src/terminal-command-cli-adapter.js +344 -6061
  121. package/dist/src/terminal-command-cli-adapter.js.map +1 -1
  122. package/dist/src/terminal-command-cli-ports.d.ts +443 -0
  123. package/dist/src/terminal-command-cli-ports.js +2 -0
  124. package/dist/src/terminal-command-cli-ports.js.map +1 -0
  125. package/dist/src/terminal-command-dispatch-preparation.d.ts +81 -0
  126. package/dist/src/terminal-command-dispatch-preparation.js +209 -0
  127. package/dist/src/terminal-command-dispatch-preparation.js.map +1 -0
  128. package/dist/src/terminal-command-dispatch-transport.d.ts +35 -0
  129. package/dist/src/terminal-command-dispatch-transport.js +859 -0
  130. package/dist/src/terminal-command-dispatch-transport.js.map +1 -0
  131. package/dist/src/terminal-command-foreground-proof.d.ts +32 -0
  132. package/dist/src/terminal-command-foreground-proof.js +72 -0
  133. package/dist/src/terminal-command-foreground-proof.js.map +1 -0
  134. package/dist/src/terminal-command-send-preflight.d.ts +41 -0
  135. package/dist/src/terminal-command-send-preflight.js +86 -0
  136. package/dist/src/terminal-command-send-preflight.js.map +1 -0
  137. package/dist/src/terminal-composer-classifier.d.ts +55 -0
  138. package/dist/src/terminal-composer-classifier.js +638 -0
  139. package/dist/src/terminal-composer-classifier.js.map +1 -0
  140. package/dist/src/terminal-dispatch-recovery-authority.d.ts +56 -0
  141. package/dist/src/terminal-dispatch-recovery-authority.js +251 -0
  142. package/dist/src/terminal-dispatch-recovery-authority.js.map +1 -0
  143. package/dist/src/terminal-dispatch-recovery-cli-adapter.d.ts +1 -9
  144. package/dist/src/terminal-dispatch-recovery-cli-adapter.js +5 -240
  145. package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -1
  146. package/dist/src/terminal-handoff-cli-adapter.d.ts +1 -11
  147. package/dist/src/terminal-handoff-cli-adapter.js +2 -39
  148. package/dist/src/terminal-handoff-cli-adapter.js.map +1 -1
  149. package/dist/src/terminal-human-explicit-send-cli-adapter.d.ts +25 -0
  150. package/dist/src/terminal-human-explicit-send-cli-adapter.js +903 -0
  151. package/dist/src/terminal-human-explicit-send-cli-adapter.js.map +1 -0
  152. package/dist/src/terminal-identity-authority-cli-adapter.d.ts +1 -6
  153. package/dist/src/terminal-identity-authority-cli-adapter.js +3 -33
  154. package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -1
  155. package/dist/src/terminal-interaction-cli-adapter.js +209 -210
  156. package/dist/src/terminal-interaction-cli-adapter.js.map +1 -1
  157. package/dist/src/terminal-interaction-core.d.ts +0 -2
  158. package/dist/src/terminal-interaction-core.js +4 -3
  159. package/dist/src/terminal-interaction-core.js.map +1 -1
  160. package/dist/src/terminal-interaction-protocol.d.ts +21 -0
  161. package/dist/src/terminal-interaction-protocol.js +74 -0
  162. package/dist/src/terminal-interaction-protocol.js.map +1 -1
  163. package/dist/src/terminal-interaction-response-bridge.d.ts +126 -0
  164. package/dist/src/terminal-interaction-response-bridge.js +579 -0
  165. package/dist/src/terminal-interaction-response-bridge.js.map +1 -0
  166. package/dist/src/terminal-interaction-response-transaction.d.ts +44 -0
  167. package/dist/src/terminal-interaction-response-transaction.js +56 -0
  168. package/dist/src/terminal-interaction-response-transaction.js.map +1 -0
  169. package/dist/src/terminal-list-action-policy.d.ts +43 -0
  170. package/dist/src/terminal-list-action-policy.js +164 -0
  171. package/dist/src/terminal-list-action-policy.js.map +1 -0
  172. package/dist/src/terminal-list-cli-adapter.d.ts +6 -36
  173. package/dist/src/terminal-list-cli-adapter.js +241 -1946
  174. package/dist/src/terminal-list-cli-adapter.js.map +1 -1
  175. package/dist/src/terminal-list-facts.d.ts +129 -0
  176. package/dist/src/terminal-list-facts.js +179 -0
  177. package/dist/src/terminal-list-facts.js.map +1 -0
  178. package/dist/src/terminal-list-inventory.d.ts +59 -0
  179. package/dist/src/terminal-list-inventory.js +194 -0
  180. package/dist/src/terminal-list-inventory.js.map +1 -0
  181. package/dist/src/terminal-list-ownership-service.d.ts +118 -0
  182. package/dist/src/terminal-list-ownership-service.js +1003 -0
  183. package/dist/src/terminal-list-ownership-service.js.map +1 -0
  184. package/dist/src/terminal-list-projection-service.d.ts +49 -0
  185. package/dist/src/terminal-list-projection-service.js +407 -0
  186. package/dist/src/terminal-list-projection-service.js.map +1 -0
  187. package/dist/src/terminal-list-renderer.d.ts +11 -11
  188. package/dist/src/terminal-list-renderer.js +168 -119
  189. package/dist/src/terminal-list-renderer.js.map +1 -1
  190. package/dist/src/terminal-managed-send-cli-adapter.d.ts +33 -0
  191. package/dist/src/terminal-managed-send-cli-adapter.js +994 -0
  192. package/dist/src/terminal-managed-send-cli-adapter.js.map +1 -0
  193. package/dist/src/terminal-managed-turn-list-action-policy.d.ts +47 -0
  194. package/dist/src/terminal-managed-turn-list-action-policy.js +64 -0
  195. package/dist/src/terminal-managed-turn-list-action-policy.js.map +1 -0
  196. package/dist/src/terminal-model-control-action-policy.d.ts +60 -0
  197. package/dist/src/terminal-model-control-action-policy.js +96 -0
  198. package/dist/src/terminal-model-control-action-policy.js.map +1 -0
  199. package/dist/src/terminal-model-control-availability.d.ts +68 -0
  200. package/dist/src/terminal-model-control-availability.js +157 -0
  201. package/dist/src/terminal-model-control-availability.js.map +1 -0
  202. package/dist/src/terminal-model-control-bridge.d.ts +48 -0
  203. package/dist/src/terminal-model-control-bridge.js +259 -0
  204. package/dist/src/terminal-model-control-bridge.js.map +1 -0
  205. package/dist/src/terminal-model-control-profile.d.ts +55 -0
  206. package/dist/src/terminal-model-control-profile.js +137 -0
  207. package/dist/src/terminal-model-control-profile.js.map +1 -0
  208. package/dist/src/terminal-model-control-subject.d.ts +34 -0
  209. package/dist/src/terminal-model-control-subject.js +58 -0
  210. package/dist/src/terminal-model-control-subject.js.map +1 -0
  211. package/dist/src/terminal-model-control-transaction.d.ts +19 -0
  212. package/dist/src/terminal-model-control-transaction.js +39 -0
  213. package/dist/src/terminal-model-control-transaction.js.map +1 -0
  214. package/dist/src/terminal-model-control.d.ts +37 -18
  215. package/dist/src/terminal-model-control.js +285 -194
  216. package/dist/src/terminal-model-control.js.map +1 -1
  217. package/dist/src/terminal-monitor-application-service.js +50 -17
  218. package/dist/src/terminal-monitor-application-service.js.map +1 -1
  219. package/dist/src/terminal-native-inspection-bridge.d.ts +211 -0
  220. package/dist/src/terminal-native-inspection-bridge.js +1127 -0
  221. package/dist/src/terminal-native-inspection-bridge.js.map +1 -0
  222. package/dist/src/terminal-runtime-cli-adapter.d.ts +2 -2
  223. package/dist/src/terminal-runtime-cli-adapter.js.map +1 -1
  224. package/dist/src/terminal-submission-retry-application.d.ts +24 -0
  225. package/dist/src/terminal-submission-retry-application.js +402 -0
  226. package/dist/src/terminal-submission-retry-application.js.map +1 -0
  227. package/dist/src/terminal-submission-retry-ports.d.ts +30 -0
  228. package/dist/src/terminal-submission-retry-ports.js +2 -0
  229. package/dist/src/terminal-submission-retry-ports.js.map +1 -0
  230. package/dist/src/terminal-submission-retry-reconciliation.d.ts +236 -0
  231. package/dist/src/terminal-submission-retry-reconciliation.js +1130 -0
  232. package/dist/src/terminal-submission-retry-reconciliation.js.map +1 -0
  233. package/dist/src/terminal-submission-retry-transport.d.ts +58 -0
  234. package/dist/src/terminal-submission-retry-transport.js +675 -0
  235. package/dist/src/terminal-submission-retry-transport.js.map +1 -0
  236. package/dist/src/terminal-text-submission-bridge.d.ts +200 -0
  237. package/dist/src/terminal-text-submission-bridge.js +632 -0
  238. package/dist/src/terminal-text-submission-bridge.js.map +1 -0
  239. package/dist/src/terminal-user-explicit-send-policy.d.ts +29 -0
  240. package/dist/src/terminal-user-explicit-send-policy.js +46 -0
  241. package/dist/src/terminal-user-explicit-send-policy.js.map +1 -0
  242. package/dist/src/terminal-watch-cli-adapter.js +91 -143
  243. package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
  244. package/dist/src/terminal-watch-codec.d.ts +9 -0
  245. package/dist/src/terminal-watch-codec.js +1051 -0
  246. package/dist/src/terminal-watch-codec.js.map +1 -0
  247. package/dist/src/terminal-watch-interaction-response-store.d.ts +19 -0
  248. package/dist/src/terminal-watch-interaction-response-store.js +87 -0
  249. package/dist/src/terminal-watch-interaction-response-store.js.map +1 -0
  250. package/dist/src/terminal-watch-record.d.ts +319 -0
  251. package/dist/src/terminal-watch-record.js +742 -0
  252. package/dist/src/terminal-watch-record.js.map +1 -0
  253. package/dist/src/terminal-watch-service.js +48 -27
  254. package/dist/src/terminal-watch-service.js.map +1 -1
  255. package/dist/src/terminal-watch-store.d.ts +3 -279
  256. package/dist/src/terminal-watch-store.js +5 -1779
  257. package/dist/src/terminal-watch-store.js.map +1 -1
  258. package/package.json +7 -2
  259. package/templates/openclaw-skills/agent-knock-knock/SKILL.md +1 -1
@@ -0,0 +1,1127 @@
1
+ import { createHash } from "node:crypto";
2
+ import { TerminalControlInputNotSentError } from "./terminal-control-provider.js";
3
+ import { sameTerminalControlIncarnation } from "./terminal-control-ref.js";
4
+ import { CODEX_EXACT_CANDIDATE_GRACE_CAPTURES, CODEX_MULTILINE_SETTLE_POLL_MS, CODEX_MULTILINE_SETTLE_SCROLLBACK_LINES, CODEX_MULTILINE_STABLE_CAPTURES, CODEX_PASTE_ENTER_SETTLE_MS } from "./terminal-text-submission-bridge.js";
5
+ export const CODEX_COMPOSER_MARKER = /^[›»](?:\s|$)/u;
6
+ export const CODEX_COMPOSER_FOOTER = /^(?:gpt-[\w.-]+(?:\s|$)|[-\w.]+ default ·)/u;
7
+ // Keep every exact slash-completion shape closed per behavior profile so a
8
+ // version adding another matching command cannot silently become an authorized
9
+ // native command surface.
10
+ const CODEX_NATIVE_STATUS_POPUP_BY_PROFILE = {
11
+ "codex-tui-0.146.0": [
12
+ " /status show current session configuration and token usage"
13
+ ],
14
+ "codex-tui-0.146.1": [
15
+ " /status show current session configuration and token usage"
16
+ ],
17
+ "codex-tui-0.147.0": [
18
+ " /status show current session configuration and token usage",
19
+ " /statusline configure which items appear in the status line"
20
+ ],
21
+ "codex-tui-0.148.0": [
22
+ " /status show current session configuration and token usage",
23
+ " /statusline configure which items appear in the status line"
24
+ ],
25
+ "codex-tui-0.149.1": [
26
+ " /status show current session configuration and token usage",
27
+ " /statusline configure which items appear in the status line"
28
+ ],
29
+ "codex-tui-0.150.1": [
30
+ " /status show current session configuration and token usage",
31
+ " /statusline configure which items appear in the status line"
32
+ ],
33
+ "codex-tui-0.151.0": [
34
+ " /status show current session configuration and token usage",
35
+ " /statusline configure which items appear in the status line"
36
+ ],
37
+ "codex-tui-0.153.0": [
38
+ " /status show current session configuration and token usage",
39
+ " /statusline configure which items appear in the status line"
40
+ ],
41
+ "codex-tui-0.153.4": [
42
+ " /status show current session configuration and token usage",
43
+ " /statusline configure which items appear in the status line"
44
+ ],
45
+ "codex-tui-0.154.0": [
46
+ " /status show current session configuration and token usage",
47
+ " /statusline configure which items appear in the status line"
48
+ ],
49
+ "codex-tui-generic-v1": [
50
+ " /status show current session configuration and token usage",
51
+ " /statusline configure which items appear in the status line"
52
+ ]
53
+ };
54
+ // Codex's verified `/status` profiles are exercised against its canonical
55
+ // 80-column status surface. Narrower layouts can truncate the 36-character
56
+ // Session UUID after a dynamically sized label column. Exact provider geometry
57
+ // is required before input; ANSI visible-buffer width is only a conservative
58
+ // fallback diagnostic and never upgrades unknown geometry to safe.
59
+ const CODEX_NATIVE_STATUS_MIN_VIEWPORT_BY_PROFILE = {
60
+ "codex-tui-0.146.0": 80,
61
+ "codex-tui-0.146.1": 80,
62
+ "codex-tui-0.147.0": 80,
63
+ "codex-tui-0.148.0": 80,
64
+ "codex-tui-0.149.1": 80,
65
+ "codex-tui-0.150.1": 80,
66
+ "codex-tui-0.151.0": 80,
67
+ "codex-tui-0.153.0": 80,
68
+ "codex-tui-0.153.4": 80,
69
+ "codex-tui-0.154.0": 80,
70
+ "codex-tui-generic-v1": 80
71
+ };
72
+ const CLAUDE_NATIVE_STATUS_POPUP_BY_PROFILE = {
73
+ "claude-code-2.1.218-native-status": [
74
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
75
+ "/statusline Set up Claude Code's status line UI",
76
+ "/ide Manage IDE integrations and show status",
77
+ "/usage Show session cost, plan usage, and activity stats"
78
+ ],
79
+ "claude-code-2.1.226-native-status": [
80
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
81
+ "/statusline Set up Claude Code's status line UI",
82
+ "/ide Manage IDE integrations and show status",
83
+ "/usage Show session cost, plan usage, and activity stats"
84
+ ],
85
+ "claude-code-2.1.237-native-status": [
86
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
87
+ "/statusline Set up Claude Code's status line UI",
88
+ "/ide Manage IDE integrations and show status",
89
+ "/usage Show session cost, plan usage, and activity stats"
90
+ ],
91
+ "claude-code-2.1.251-native-status": [
92
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
93
+ "/statusline Set up Claude Code's status line UI",
94
+ "/ide Manage IDE integrations and show status",
95
+ "/usage Show session cost, plan usage, and activity stats"
96
+ ],
97
+ "claude-code-2.1.259-native-status": [
98
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
99
+ "/statusline Set up Claude Code's status line UI",
100
+ "/ide Manage IDE integrations and show status",
101
+ "/usage Show session cost, plan usage, and activity stats"
102
+ ],
103
+ "claude-code-2.1.263-native-status": [
104
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
105
+ "/statusline Set up Claude Code's status line UI",
106
+ "/ide Manage IDE integrations and show status",
107
+ "/usage Show session cost, plan usage, and activity stats"
108
+ ],
109
+ "claude-code-2.1.266-native-status": [
110
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
111
+ "/statusline Set up Claude Code's status line UI",
112
+ "/ide Manage IDE integrations and show status",
113
+ "/usage Show session cost, plan usage, and activity stats"
114
+ ],
115
+ "claude-code-2.1.267-native-status": [
116
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
117
+ "/statusline Set up Claude Code's status line UI",
118
+ "/ide Manage IDE integrations and show status",
119
+ "/usage Show session cost, plan usage, and activity stats"
120
+ ],
121
+ "claude-code-unverified-native-status-v1": [
122
+ "/status Show Claude Code status including version, model, account, API connectivity, and tool statuses",
123
+ "/statusline Set up Claude Code's status line UI",
124
+ "/ide Manage IDE integrations and show status",
125
+ "/usage Show session cost, plan usage, and activity stats"
126
+ ]
127
+ };
128
+ const CLAUDE_NATIVE_STATUS_SETTLE_BY_PROFILE = {
129
+ "claude-code-2.1.218-native-status": {
130
+ minimumStableMs: 80,
131
+ maximumSettleMs: 2_000
132
+ },
133
+ "claude-code-2.1.226-native-status": {
134
+ minimumStableMs: 80,
135
+ maximumSettleMs: 5_000
136
+ },
137
+ "claude-code-2.1.237-native-status": {
138
+ minimumStableMs: 80,
139
+ maximumSettleMs: 5_000
140
+ },
141
+ "claude-code-2.1.251-native-status": {
142
+ minimumStableMs: 80,
143
+ maximumSettleMs: 5_000
144
+ },
145
+ "claude-code-2.1.259-native-status": {
146
+ minimumStableMs: 80,
147
+ maximumSettleMs: 5_000
148
+ },
149
+ "claude-code-2.1.263-native-status": {
150
+ minimumStableMs: 80,
151
+ maximumSettleMs: 5_000
152
+ },
153
+ "claude-code-2.1.266-native-status": {
154
+ minimumStableMs: 80,
155
+ maximumSettleMs: 5_000
156
+ },
157
+ "claude-code-2.1.267-native-status": {
158
+ minimumStableMs: 80,
159
+ maximumSettleMs: 5_000
160
+ },
161
+ "claude-code-unverified-native-status-v1": {
162
+ minimumStableMs: 80,
163
+ maximumSettleMs: 5_000
164
+ }
165
+ };
166
+ /**
167
+ * A fail-closed native-inspection submission result.
168
+ *
169
+ * Only `not_started` proves that a caller may safely retry. Once text has
170
+ * reached the composer, or Enter has been attempted, automated retries could
171
+ * duplicate input or execute a different command after terminal drift.
172
+ */
173
+ export class NativeInspectionSubmissionError extends Error {
174
+ stage;
175
+ code = "AKK_NATIVE_INSPECTION_SUBMISSION_FAILED";
176
+ doNotRetry;
177
+ constructor(stage, message, options = {}) {
178
+ super(message, options);
179
+ this.stage = stage;
180
+ this.name = "NativeInspectionSubmissionError";
181
+ this.doNotRetry = stage !== "not_started";
182
+ this.diagnostic = options.diagnostic;
183
+ }
184
+ diagnostic;
185
+ }
186
+ class NativeInspectionDiagnosticError extends Error {
187
+ diagnostic;
188
+ constructor(diagnostic, message, options = {}) {
189
+ super(message, options);
190
+ this.diagnostic = diagnostic;
191
+ this.name = "NativeInspectionDiagnosticError";
192
+ }
193
+ }
194
+ /** A verified modal could not be dismissed across one exact key attempt. */
195
+ export class NativeInspectionDismissalError extends Error {
196
+ code = "AKK_NATIVE_INSPECTION_DISMISSAL_FAILED";
197
+ doNotRetry = true;
198
+ constructor(message, options = {}) {
199
+ super(message, options);
200
+ this.name = "NativeInspectionDismissalError";
201
+ }
202
+ }
203
+ /**
204
+ * Closed native-inspection transport. Terminal locking, Store authority, and
205
+ * the caller's one-shot input ledger remain outside this internal service.
206
+ */
207
+ export class TerminalNativeInspectionBridge {
208
+ registry;
209
+ terminalProvider;
210
+ constructor(options) {
211
+ this.registry = options.registry;
212
+ this.terminalProvider = options.terminalProvider;
213
+ this.runtime = options.runtime;
214
+ }
215
+ runtime;
216
+ captureInspection(adapter, terminalControl, options = {}) {
217
+ return this.runtime.captureInspection(adapter, terminalControl, options);
218
+ }
219
+ verifyTerminalIdentity(agent, terminalControl, runtime) {
220
+ return this.runtime.verifyIdentity(agent, terminalControl, runtime);
221
+ }
222
+ nowMs() {
223
+ return this.runtime.nowMs();
224
+ }
225
+ sleep(milliseconds) {
226
+ return this.runtime.sleep(milliseconds);
227
+ }
228
+ /**
229
+ * Submit one closed, adapter-owned native inspection command.
230
+ *
231
+ * This intentionally does not use `send()`: native slash commands need a
232
+ * stricter composer proof, and failures after text injection must leave the
233
+ * draft untouched instead of issuing the legacy best-effort C-u cleanup.
234
+ */
235
+ async submitNativeInspection(agent, terminalControl, plan, options = {}) {
236
+ return this.submitClosedNativeInspection(agent, terminalControl, plan, options, { requireCodexReadyComposer: agent === "codex" });
237
+ }
238
+ /**
239
+ * Submit Codex's closed, version-profiled `/status` probe.
240
+ *
241
+ * Unlike the generic native-inspection entry point, callers provide only
242
+ * the detected Codex version, never a command or plan. The bridge proves an
243
+ * exact empty (or fully dim replace-on-type) ANSI composer before injecting
244
+ * text, then crosses Codex's paste suppression window under the same exact
245
+ * composer and terminal-identity fences used by native inspection.
246
+ */
247
+ async submitCodexStatusProbe(terminalControl, agentVersion, options = {}) {
248
+ const adapter = this.registry.require("codex");
249
+ let plan;
250
+ try {
251
+ const capability = adapter.probeNativeInspection?.(agentVersion);
252
+ if (capability?.status !== "supported" ||
253
+ capability.statusInspection !== true) {
254
+ throw new NativeInspectionDiagnosticError("unsupported_profile", capability?.reason ??
255
+ `Codex ${agentVersion} has no closed /status behavior profile`);
256
+ }
257
+ const planned = adapter.planNativeInspection?.({ kind: "status" }, capability);
258
+ if (!planned) {
259
+ throw new NativeInspectionDiagnosticError("unsupported_profile", `Codex ${agentVersion} did not produce a closed /status plan`);
260
+ }
261
+ plan = planned;
262
+ }
263
+ catch (error) {
264
+ throw nativeInspectionSubmissionError("not_started", error, "unsupported_profile");
265
+ }
266
+ const result = await this.submitClosedNativeInspection("codex", terminalControl, plan, options, { requireCodexReadyComposer: true });
267
+ if (!result.preTextScreenDigest) {
268
+ throw new Error("Codex /status pre-text composer evidence is missing");
269
+ }
270
+ return {
271
+ ...result,
272
+ agent: "codex",
273
+ preTextScreenDigest: result.preTextScreenDigest,
274
+ observationBaselineDigest: bareDigestFromNativeInspectionScreenFingerprint(result.preEnterScreenDigest),
275
+ observationScrollbackLines: CODEX_MULTILINE_SETTLE_SCROLLBACK_LINES
276
+ };
277
+ }
278
+ async submitClosedNativeInspection(agent, terminalControl, plan, options, safety = {}) {
279
+ const adapter = this.registry.require(agent);
280
+ try {
281
+ assertClosedStatusInspectionPlan(adapter, terminalControl, plan);
282
+ assertTerminalMutationCapabilities({
283
+ provider: this.terminalProvider,
284
+ terminal: this.terminalProvider.endpoint(terminalControl),
285
+ semantic: ["send_keys", "screen_status"],
286
+ transport: [
287
+ "stable_resource_resolution",
288
+ "screen_capture",
289
+ "text_delivery",
290
+ "key_delivery",
291
+ ...(safety.requireCodexReadyComposer
292
+ ? ["ansi_capture"]
293
+ : [])
294
+ ]
295
+ });
296
+ }
297
+ catch (error) {
298
+ throw nativeInspectionSubmissionError("not_started", error, "capability_unavailable");
299
+ }
300
+ let verifiedForText;
301
+ try {
302
+ verifiedForText = await this.verifyTerminalIdentity(adapter.agent, terminalControl, options.runtime);
303
+ }
304
+ catch (error) {
305
+ throw nativeInspectionSubmissionError("not_started", error, "identity_unverified");
306
+ }
307
+ let preTextScreenDigest;
308
+ if (safety.requireCodexReadyComposer) {
309
+ try {
310
+ const ready = await this.captureCodexReadyComposer(adapter, verifiedForText, plan, options.runtime);
311
+ verifiedForText = ready.terminalControl;
312
+ preTextScreenDigest = ready.screenDigest;
313
+ }
314
+ catch (error) {
315
+ throw nativeInspectionSubmissionError("not_started", error, "composer_not_ready");
316
+ }
317
+ }
318
+ try {
319
+ if (!verifiedForText.capabilities.includes("send_keys")) {
320
+ throw new TerminalControlInputNotSentError(`${adapter.displayName} native inspection input capability changed`);
321
+ }
322
+ await this.terminalProvider.sendText(this.terminalProvider.endpoint(verifiedForText), plan.command);
323
+ }
324
+ catch (error) {
325
+ if (error instanceof TerminalControlInputNotSentError) {
326
+ throw nativeInspectionSubmissionError("not_started", error, "text_delivery_unproven");
327
+ }
328
+ // The transport cannot prove whether an untyped failure happened before
329
+ // or after tmux accepted the literal input. Fail closed as injected.
330
+ throw nativeInspectionSubmissionError("text_injected", error, "text_delivery_unproven");
331
+ }
332
+ let settled;
333
+ try {
334
+ settled = await this.settleNativeInspectionComposer(adapter, terminalControl, plan, options.runtime);
335
+ await options.beforeEnter?.({
336
+ agent: adapter.agent,
337
+ terminalControl: settled.terminalControl,
338
+ plan,
339
+ preEnterScreenDigest: settled.screenDigest,
340
+ materialization: settled.materialization
341
+ });
342
+ if (safety.requireCodexReadyComposer) {
343
+ settled = {
344
+ ...settled,
345
+ terminalControl: await this.assertFinalCodexStatusViewport(settled.terminalControl, plan, options.runtime)
346
+ };
347
+ }
348
+ // Viewport inspection may await provider I/O and the PTY may receive
349
+ // human input while that proof is in flight. Keep the exact composer
350
+ // capture as the final substantive asynchronous evidence before the
351
+ // single Enter attempt.
352
+ settled = await this.revalidateNativeInspectionComposer(adapter, settled.terminalControl, plan, settled.materialization, options.runtime);
353
+ }
354
+ catch (error) {
355
+ throw nativeInspectionSubmissionError("text_injected", error, "composer_not_exact");
356
+ }
357
+ try {
358
+ // Exactly one Enter attempt. Any error is submission-uncertain and must
359
+ // never trigger a fallback executable or a blind second Enter.
360
+ await this.terminalProvider.sendKeys(this.terminalProvider.endpoint(settled.terminalControl), ["C-m"]);
361
+ }
362
+ catch (error) {
363
+ throw nativeInspectionSubmissionError("enter_uncertain", error, "enter_uncertain");
364
+ }
365
+ return {
366
+ stage: "enter_dispatched",
367
+ agent: adapter.agent,
368
+ terminalControl: settled.terminalControl,
369
+ command: plan.command,
370
+ behaviorProfile: plan.behaviorProfile,
371
+ preEnterScreenDigest: settled.screenDigest,
372
+ preEnterEvidenceInventory: settled.evidenceInventory,
373
+ materialization: settled.materialization,
374
+ enterCount: 1,
375
+ ...(preTextScreenDigest ? { preTextScreenDigest } : {})
376
+ };
377
+ }
378
+ async observeNativeInspection(agent, terminalControl, request, options = {}) {
379
+ const adapter = this.registry.require(agent);
380
+ if (!adapter.observeNativeInspection) {
381
+ throw new Error(`${adapter.displayName} native inspection observation is not supported`);
382
+ }
383
+ const captured = await this.captureInspection(adapter, terminalControl, {
384
+ runtime: options.runtime,
385
+ scrollbackLines: options.scrollbackLines
386
+ });
387
+ const screenDigest = nativeInspectionScreenFingerprint(captured.screen);
388
+ const adapterObservation = adapter.observeNativeInspection({
389
+ operation: request.operation,
390
+ previousScreenFingerprint: request.previousScreenFingerprint,
391
+ preEnterEvidenceInventory: request.preEnterEvidenceInventory,
392
+ expectedNativeThreadId: request.expectedNativeThreadId,
393
+ expectedAgentVersion: request.expectedAgentVersion,
394
+ expectedCwd: request.expectedCwd,
395
+ screen: captured.screen
396
+ });
397
+ const observation = adapter.agent === "claude" &&
398
+ options.runtime?.requireExactClaudeAgentRow === true &&
399
+ adapterObservation.status === "observed" &&
400
+ adapterObservation.evidence === "claude_status_panel"
401
+ ? {
402
+ ...adapterObservation,
403
+ evidence: "claude_status_panel+claude_agents_exact_pid"
404
+ }
405
+ : adapterObservation;
406
+ return {
407
+ terminalControl: captured.terminalControl,
408
+ status: this.runtime.statusFromInspection(adapter, captured.terminalControl, captured.inspection, { screen: captured.screen, runtime: options.runtime }),
409
+ screenDigest,
410
+ observation
411
+ };
412
+ }
413
+ /**
414
+ * Dismiss one exact adapter-owned modal result after re-observing the same
415
+ * evidence under the terminal identity fence. There is exactly one key
416
+ * attempt and no automated retry across an uncertain dismissal boundary.
417
+ */
418
+ async dismissNativeInspection(agent, terminalControl, plan, request, expectedEvidenceFingerprint, options = {}) {
419
+ const adapter = this.registry.require(agent);
420
+ try {
421
+ assertClosedStatusInspectionPlan(adapter, terminalControl, plan);
422
+ assertClosedNativeInspectionDismissal(plan);
423
+ }
424
+ catch (error) {
425
+ throw new NativeInspectionDismissalError(error instanceof Error ? error.message : String(error), { cause: error });
426
+ }
427
+ const observeExactPanel = async (control) => {
428
+ const captured = await this.captureInspection(adapter, control, {
429
+ runtime: options.runtime,
430
+ scrollbackLines: options.scrollbackLines
431
+ });
432
+ const observation = adapter.observeNativeInspection?.({
433
+ operation: request.operation,
434
+ previousScreenFingerprint: request.previousScreenFingerprint,
435
+ preEnterEvidenceInventory: request.preEnterEvidenceInventory,
436
+ expectedNativeThreadId: request.expectedNativeThreadId,
437
+ expectedAgentVersion: request.expectedAgentVersion,
438
+ expectedCwd: request.expectedCwd,
439
+ screen: captured.screen
440
+ });
441
+ if (!observation ||
442
+ observation.status !== "observed" ||
443
+ observation.result?.kind !== "native_status" ||
444
+ observation.evidenceFingerprint !== expectedEvidenceFingerprint) {
445
+ throw new Error(observation?.reason ??
446
+ "the exact fresh native status panel changed before dismissal");
447
+ }
448
+ const verified = await this.verifyTerminalIdentity(adapter.agent, captured.terminalControl, options.runtime);
449
+ if (!sameTerminalControlIdentity(captured.terminalControl, verified)) {
450
+ throw new Error("terminal control identity changed before native status dismissal");
451
+ }
452
+ return verified;
453
+ };
454
+ try {
455
+ let verified = await observeExactPanel(terminalControl);
456
+ await options.beforeDismiss?.({
457
+ agent: adapter.agent,
458
+ terminalControl: verified,
459
+ plan,
460
+ evidenceFingerprint: expectedEvidenceFingerprint
461
+ });
462
+ verified = await observeExactPanel(verified);
463
+ try {
464
+ await this.terminalProvider.sendKeys(this.terminalProvider.endpoint(verified), plan.expectedResult.dismissal.keys);
465
+ }
466
+ catch (error) {
467
+ throw new NativeInspectionDismissalError("native status modal dismissal outcome is uncertain; do not retry automatically", { cause: error });
468
+ }
469
+ return {
470
+ stage: "dismiss_dispatched",
471
+ agent: adapter.agent,
472
+ terminalControl: verified,
473
+ keys: plan.expectedResult.dismissal.keys,
474
+ dismissCount: 1
475
+ };
476
+ }
477
+ catch (error) {
478
+ if (error instanceof NativeInspectionDismissalError) {
479
+ throw error;
480
+ }
481
+ throw new NativeInspectionDismissalError(error instanceof Error ? error.message : String(error), { cause: error });
482
+ }
483
+ }
484
+ async captureCodexReadyComposer(adapter, terminalControl, plan, runtime) {
485
+ if (adapter.agent !== "codex") {
486
+ throw new NativeInspectionDiagnosticError("unsupported_profile", "the closed Codex /status probe requires the Codex adapter");
487
+ }
488
+ const minimumViewport = CODEX_NATIVE_STATUS_MIN_VIEWPORT_BY_PROFILE[plan.behaviorProfile];
489
+ if (minimumViewport === undefined) {
490
+ throw new NativeInspectionDiagnosticError("unsupported_profile", `Codex ${plan.behaviorProfile} has no exact /status viewport profile`);
491
+ }
492
+ const captureReady = async (control) => {
493
+ const verified = await this.verifyTerminalIdentity(adapter.agent, control, runtime);
494
+ const endpoint = this.terminalProvider.endpoint(verified);
495
+ const viewportInspector = this.terminalProvider.inspectViewport;
496
+ let exactViewport;
497
+ let viewportUnavailableReason = "terminal provider has no exact viewport inspector";
498
+ if (viewportInspector) {
499
+ let viewport;
500
+ try {
501
+ viewport = await viewportInspector.call(this.terminalProvider, endpoint);
502
+ }
503
+ catch (error) {
504
+ throw new NativeInspectionDiagnosticError("viewport_unavailable", `Codex /status viewport inspection failed before terminal input: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
505
+ }
506
+ if (viewport) {
507
+ if (!Number.isSafeInteger(viewport.columns) ||
508
+ viewport.columns <= 0 ||
509
+ !Number.isSafeInteger(viewport.rows) ||
510
+ viewport.rows <= 0) {
511
+ throw new NativeInspectionDiagnosticError("viewport_unavailable", "Codex /status viewport inspector returned invalid geometry");
512
+ }
513
+ exactViewport = viewport.columns;
514
+ }
515
+ else {
516
+ viewportUnavailableReason =
517
+ "terminal provider could not prove exact viewport geometry";
518
+ }
519
+ }
520
+ const styledScreen = await this.terminalProvider.capture(endpoint, { scrollbackLines: 40, preserveEscapes: true });
521
+ const plainScreen = stripTerminalEscapeSequences(styledScreen);
522
+ const inspection = adapter.inspectScreen({ screen: plainScreen, runtime });
523
+ assertNativeInspectionComposerSafe(inspection, adapter.displayName);
524
+ const composer = exactCodexReadyStyledComposerCapture(styledScreen);
525
+ if (!composer) {
526
+ throw new NativeInspectionDiagnosticError("composer_not_ready", "Codex composer contains non-placeholder input or is not at the exact idle prompt");
527
+ }
528
+ const inferredViewport = inferCodexVisibleViewportColumns(styledScreen);
529
+ const observedViewport = exactViewport ?? inferredViewport;
530
+ if ((observedViewport !== undefined && observedViewport < minimumViewport) ||
531
+ hasTruncatedCodexStatusSessionLine(plainScreen)) {
532
+ throw new NativeInspectionDiagnosticError("viewport_too_narrow", `Codex /status requires a proven viewport of at least ` +
533
+ `${minimumViewport} columns to preserve the complete Session UUID` +
534
+ `${observedViewport === undefined
535
+ ? ""
536
+ : `; observed ${observedViewport}`}; widen or zoom the pane before retrying`);
537
+ }
538
+ if (exactViewport === undefined) {
539
+ throw new NativeInspectionDiagnosticError("viewport_unavailable", `Codex /status requires exact terminal viewport geometry before input; ` +
540
+ viewportUnavailableReason +
541
+ `${inferredViewport === undefined
542
+ ? ""
543
+ : ` (ANSI fallback estimated ${inferredViewport} columns)`}`);
544
+ }
545
+ const reverified = await this.verifyTerminalIdentity(adapter.agent, verified, runtime);
546
+ if (!sameTerminalControlIdentity(verified, reverified)) {
547
+ throw new NativeInspectionDiagnosticError("identity_unverified", "terminal control identity changed after the Codex pre-text composer capture");
548
+ }
549
+ return {
550
+ terminalControl: reverified,
551
+ screenDigest: nativeInspectionScreenFingerprint(styledScreen),
552
+ composerDigest: composer.digest
553
+ };
554
+ };
555
+ const first = await captureReady(terminalControl);
556
+ const second = await captureReady(first.terminalControl);
557
+ if (second.composerDigest !== first.composerDigest) {
558
+ throw new NativeInspectionDiagnosticError("composer_not_ready", "Codex empty composer changed across its stable pre-text captures");
559
+ }
560
+ return {
561
+ terminalControl: second.terminalControl,
562
+ screenDigest: second.screenDigest
563
+ };
564
+ }
565
+ async assertFinalCodexStatusViewport(terminalControl, plan, runtime) {
566
+ const minimumViewport = CODEX_NATIVE_STATUS_MIN_VIEWPORT_BY_PROFILE[plan.behaviorProfile];
567
+ if (minimumViewport === undefined) {
568
+ throw new NativeInspectionDiagnosticError("unsupported_profile", `Codex ${plan.behaviorProfile} has no exact /status viewport profile`);
569
+ }
570
+ const verified = await this.verifyTerminalIdentity("codex", terminalControl, runtime);
571
+ const viewportInspector = this.terminalProvider.inspectViewport;
572
+ if (!viewportInspector) {
573
+ throw new NativeInspectionDiagnosticError("viewport_unavailable", "Codex /status requires exact terminal viewport geometry immediately before Enter");
574
+ }
575
+ let viewport;
576
+ try {
577
+ viewport = await viewportInspector.call(this.terminalProvider, this.terminalProvider.endpoint(verified));
578
+ }
579
+ catch (error) {
580
+ throw new NativeInspectionDiagnosticError("viewport_unavailable", `Codex /status viewport inspection failed immediately before Enter: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
581
+ }
582
+ if (!viewport ||
583
+ !Number.isSafeInteger(viewport.columns) ||
584
+ viewport.columns <= 0 ||
585
+ !Number.isSafeInteger(viewport.rows) ||
586
+ viewport.rows <= 0) {
587
+ throw new NativeInspectionDiagnosticError("viewport_unavailable", "Codex /status exact terminal viewport became unavailable immediately before Enter");
588
+ }
589
+ if (viewport.columns < minimumViewport) {
590
+ throw new NativeInspectionDiagnosticError("viewport_too_narrow", `Codex /status viewport narrowed to ${viewport.columns} columns before Enter; ` +
591
+ `at least ${minimumViewport} are required to preserve the complete Session UUID`);
592
+ }
593
+ const reverified = await this.verifyTerminalIdentity("codex", verified, runtime);
594
+ if (!sameTerminalControlIdentity(verified, reverified)) {
595
+ throw new NativeInspectionDiagnosticError("identity_unverified", "terminal control identity changed after the final Codex viewport proof");
596
+ }
597
+ return reverified;
598
+ }
599
+ async settleNativeInspectionComposer(adapter, terminalControl, plan, runtime) {
600
+ const startedAt = this.nowMs();
601
+ const settleTimeoutMs = plan.composer.maximumSettleMs;
602
+ let stableDigest;
603
+ let stableKind;
604
+ let stableSince;
605
+ let stableCaptures = 0;
606
+ let capturesBeyondDeadline = 0;
607
+ let lastMismatchDiagnostic = "composer_not_exact";
608
+ while (true) {
609
+ const captured = await this.captureInspection(adapter, terminalControl, {
610
+ runtime,
611
+ scrollbackLines: CODEX_MULTILINE_SETTLE_SCROLLBACK_LINES
612
+ });
613
+ assertNativeInspectionComposerSafe(captured.inspection);
614
+ const materialized = exactNativeInspectionComposerCapture(adapter.agent, captured.screen, plan);
615
+ const now = this.nowMs();
616
+ if (materialized) {
617
+ if (materialized.digest === stableDigest &&
618
+ materialized.kind === stableKind) {
619
+ stableCaptures += 1;
620
+ }
621
+ else {
622
+ stableDigest = materialized.digest;
623
+ stableKind = materialized.kind;
624
+ stableSince = now;
625
+ stableCaptures = 1;
626
+ }
627
+ const stableForMs = stableSince === undefined ? 0 : now - stableSince;
628
+ if (stableCaptures >= CODEX_MULTILINE_STABLE_CAPTURES &&
629
+ stableForMs >= plan.composer.minimumStableMs) {
630
+ const evidence = {
631
+ kind: materialized.kind,
632
+ digest: materialized.digest,
633
+ stableForMs,
634
+ stableCaptures
635
+ };
636
+ return this.revalidateNativeInspectionComposer(adapter, captured.terminalControl, plan, evidence, runtime);
637
+ }
638
+ }
639
+ else {
640
+ lastMismatchDiagnostic = adapter.agent === "codex"
641
+ ? codexNativeInspectionComposerMismatchDiagnostic(captured.screen, plan)
642
+ : "composer_not_exact";
643
+ stableDigest = undefined;
644
+ stableKind = undefined;
645
+ stableSince = undefined;
646
+ stableCaptures = 0;
647
+ }
648
+ const elapsed = this.nowMs() - startedAt;
649
+ const remaining = settleTimeoutMs - elapsed;
650
+ if (remaining <= 0 && !materialized) {
651
+ break;
652
+ }
653
+ if (remaining <= 0) {
654
+ capturesBeyondDeadline += 1;
655
+ if (capturesBeyondDeadline > CODEX_EXACT_CANDIDATE_GRACE_CAPTURES) {
656
+ break;
657
+ }
658
+ }
659
+ const remainingStableMs = plan.composer.minimumStableMs -
660
+ (stableSince === undefined ? 0 : this.nowMs() - stableSince);
661
+ await this.sleep(Math.min(CODEX_MULTILINE_SETTLE_POLL_MS, remaining > 0
662
+ ? remaining
663
+ : Math.max(1, remainingStableMs)));
664
+ }
665
+ throw new NativeInspectionDiagnosticError(lastMismatchDiagnostic, lastMismatchDiagnostic === "composer_viewport_truncated"
666
+ ? `${adapter.displayName} /status slash popup was truncated by the viewport; widen or zoom the pane before retrying manually`
667
+ : `${adapter.displayName} /status composer did not become exact, idle, and stable before the bounded submit deadline`);
668
+ }
669
+ async revalidateNativeInspectionComposer(adapter, terminalControl, plan, expected, runtime) {
670
+ const captured = await this.captureInspection(adapter, terminalControl, {
671
+ runtime,
672
+ scrollbackLines: CODEX_MULTILINE_SETTLE_SCROLLBACK_LINES
673
+ });
674
+ assertNativeInspectionComposerSafe(captured.inspection);
675
+ const materialized = exactNativeInspectionComposerCapture(adapter.agent, captured.screen, plan);
676
+ if (!materialized ||
677
+ materialized.digest !== expected.digest ||
678
+ materialized.kind !== expected.kind) {
679
+ throw new NativeInspectionDiagnosticError("composer_drift", `${adapter.displayName} /status composer changed after its stable pre-submit capture`);
680
+ }
681
+ const baseline = adapter.observeNativeInspection?.({
682
+ operation: plan.operation,
683
+ screen: captured.screen
684
+ });
685
+ if (!baseline ||
686
+ baseline.status === "ambiguous" ||
687
+ !Array.isArray(baseline.evidenceInventory)) {
688
+ throw new NativeInspectionDiagnosticError("evidence_unproven", baseline?.reason ??
689
+ `${adapter.displayName} /status pre-Enter evidence inventory was not proven`);
690
+ }
691
+ const verifiedImmediatelyBeforeEnter = await this.verifyTerminalIdentity(adapter.agent, captured.terminalControl, runtime);
692
+ if (!sameTerminalControlIdentity(captured.terminalControl, verifiedImmediatelyBeforeEnter)) {
693
+ throw new NativeInspectionDiagnosticError("identity_unverified", `terminal control identity changed after the final ${adapter.displayName} /status composer capture`);
694
+ }
695
+ return {
696
+ terminalControl: verifiedImmediatelyBeforeEnter,
697
+ screenDigest: nativeInspectionScreenFingerprint(captured.screen),
698
+ evidenceInventory: baseline.evidenceInventory,
699
+ materialization: expected
700
+ };
701
+ }
702
+ }
703
+ function sameTerminalControlIdentity(left, right) {
704
+ return sameTerminalControlIncarnation(left, right);
705
+ }
706
+ export function assertTerminalMutationCapabilities({ provider, terminal, semantic, transport }) {
707
+ const missingSemantic = semantic.filter((capability) => !provider.supportedCapabilities.includes(capability) ||
708
+ !terminal.capabilities.includes(capability));
709
+ const missingTransport = transport.filter((capability) => !provider.providerCapabilities.includes(capability));
710
+ if (missingSemantic.length === 0 && missingTransport.length === 0) {
711
+ return;
712
+ }
713
+ const missing = [
714
+ ...missingSemantic.map((value) => `terminal:${value}`),
715
+ ...missingTransport.map((value) => `provider:${value}`)
716
+ ];
717
+ throw new TerminalControlInputNotSentError(`terminal action capability preflight failed for ` +
718
+ `${terminal.identity.providerKind}:${terminal.route.label}: ` +
719
+ missing.join(", "));
720
+ }
721
+ function nativeInspectionSubmissionError(stage, error, fallbackDiagnostic) {
722
+ const diagnostic = error instanceof NativeInspectionSubmissionError
723
+ ? error.diagnostic
724
+ : error instanceof NativeInspectionDiagnosticError
725
+ ? error.diagnostic
726
+ : fallbackDiagnostic;
727
+ if (error instanceof NativeInspectionSubmissionError) {
728
+ const stageRank = {
729
+ not_started: 0,
730
+ text_injected: 1,
731
+ enter_uncertain: 2
732
+ };
733
+ if (stageRank[error.stage] >= stageRank[stage]) {
734
+ return error;
735
+ }
736
+ return new NativeInspectionSubmissionError(stage, error.message, {
737
+ cause: error,
738
+ diagnostic
739
+ });
740
+ }
741
+ return new NativeInspectionSubmissionError(stage, error instanceof Error ? error.message : String(error), { cause: error, diagnostic });
742
+ }
743
+ function assertClosedStatusInspectionPlan(adapter, terminalControl, plan) {
744
+ if (!terminalControl.capabilities.includes("send_keys")) {
745
+ throw new Error(`${adapter.displayName} terminal input is not supported`);
746
+ }
747
+ if (!terminalControl.capabilities.includes("screen_status")) {
748
+ throw new Error(`${adapter.displayName} terminal screen inspection is not supported`);
749
+ }
750
+ const codexProfile = adapter.agent === "codex" &&
751
+ CODEX_NATIVE_STATUS_POPUP_BY_PROFILE[plan.behaviorProfile] !== undefined;
752
+ const claudeProfile = adapter.agent === "claude" &&
753
+ CLAUDE_NATIVE_STATUS_POPUP_BY_PROFILE[plan.behaviorProfile] !== undefined;
754
+ const expectedSettle = codexProfile
755
+ ? { minimumStableMs: CODEX_PASTE_ENTER_SETTLE_MS, maximumSettleMs: 2_000 }
756
+ : CLAUDE_NATIVE_STATUS_SETTLE_BY_PROFILE[plan.behaviorProfile];
757
+ const exactPresentation = codexProfile
758
+ ? plan.expectedResult.presentation === "inline" &&
759
+ plan.expectedResult.dismissal === undefined &&
760
+ plan.composer.minimumStableMs === expectedSettle?.minimumStableMs &&
761
+ plan.composer.maximumSettleMs === expectedSettle.maximumSettleMs
762
+ : claudeProfile
763
+ ? plan.expectedResult.presentation === "modal" &&
764
+ plan.composer.minimumStableMs === expectedSettle?.minimumStableMs &&
765
+ plan.composer.maximumSettleMs === expectedSettle.maximumSettleMs &&
766
+ plan.expectedResult.dismissal?.expected === "idle_empty_composer" &&
767
+ JSON.stringify(plan.expectedResult.dismissal.keys) ===
768
+ JSON.stringify(["Escape"])
769
+ : false;
770
+ if (plan.operation.kind !== "status" ||
771
+ plan.command !== "/status" ||
772
+ plan.effect !== "read_only" ||
773
+ plan.requiresIdle !== true ||
774
+ plan.composer.kind !== "exact" ||
775
+ !Number.isFinite(plan.composer.minimumStableMs) ||
776
+ plan.composer.minimumStableMs < 0 ||
777
+ !Number.isFinite(plan.composer.maximumSettleMs) ||
778
+ plan.composer.maximumSettleMs < plan.composer.minimumStableMs ||
779
+ plan.expectedResult.kind !== "native_status" ||
780
+ !exactPresentation) {
781
+ throw new Error("refusing a non-closed native inspection plan");
782
+ }
783
+ }
784
+ function assertClosedNativeInspectionDismissal(plan) {
785
+ if (plan.expectedResult.presentation !== "modal" ||
786
+ plan.expectedResult.dismissal?.expected !== "idle_empty_composer" ||
787
+ JSON.stringify(plan.expectedResult.dismissal.keys) !==
788
+ JSON.stringify(["Escape"])) {
789
+ throw new Error("native inspection has no closed modal dismissal plan");
790
+ }
791
+ }
792
+ function assertNativeInspectionComposerSafe(inspection, displayName = "terminal agent") {
793
+ if (inspection.approval.blocked ||
794
+ inspection.activity.state === "awaiting_approval" ||
795
+ inspection.activity.state === "working") {
796
+ throw new NativeInspectionDiagnosticError("composer_not_ready", `${displayName} became busy or blocked while its /status composer was settling`);
797
+ }
798
+ // Codex's generic activity parser deliberately reports a non-empty slash
799
+ // composer as unknown. At this stage the caller has already proved an idle,
800
+ // empty styled composer under the terminal lock; the exact-current composer
801
+ // capture below is the stronger continuation proof after AKK injected only
802
+ // the adapter-owned /status command.
803
+ }
804
+ function exactNativeInspectionComposerCapture(agent, screen, plan) {
805
+ return agent === "codex"
806
+ ? exactCodexNativeInspectionComposerCapture(screen, plan)
807
+ : exactClaudeNativeInspectionComposerCapture(screen, plan);
808
+ }
809
+ function exactCodexNativeInspectionComposerCapture(screen, plan) {
810
+ const lines = screen.replace(/\r\n?/gu, "\n").split("\n");
811
+ let currentComposerIndex = -1;
812
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
813
+ if (CODEX_COMPOSER_MARKER.test(lines[index])) {
814
+ currentComposerIndex = index;
815
+ break;
816
+ }
817
+ }
818
+ if (currentComposerIndex < 0) {
819
+ return undefined;
820
+ }
821
+ const composerText = lines[currentComposerIndex]
822
+ .replace(/^[›»]\s?/u, "")
823
+ .trimEnd();
824
+ if (composerText !== plan.command) {
825
+ return undefined;
826
+ }
827
+ const footerIndex = lines.findIndex((line, candidateIndex) => candidateIndex > currentComposerIndex &&
828
+ CODEX_COMPOSER_FOOTER.test(line.trim()));
829
+ const region = lines.slice(currentComposerIndex, footerIndex < 0 ? lines.length : footerIndex);
830
+ while (region.length > 1 && region.at(-1)?.trim() === "") {
831
+ region.pop();
832
+ }
833
+ const popupRows = region.slice(1).filter((line) => line.trim().length > 0);
834
+ let kind;
835
+ if (popupRows.length === 0) {
836
+ kind = "exact_slash_composer";
837
+ }
838
+ else if (JSON.stringify(popupRows.map((line) => line.trimEnd())) ===
839
+ JSON.stringify(CODEX_NATIVE_STATUS_POPUP_BY_PROFILE[plan.behaviorProfile])) {
840
+ kind = "exact_slash_popup";
841
+ }
842
+ else {
843
+ return undefined;
844
+ }
845
+ return {
846
+ kind,
847
+ digest: createHash("sha256").update(region.join("\n")).digest("hex")
848
+ };
849
+ }
850
+ function exactClaudeNativeInspectionComposerCapture(screen, plan) {
851
+ const frame = exactClaudeComposerFrame(screen);
852
+ if (!frame) {
853
+ return undefined;
854
+ }
855
+ const { lines, openIndex, closeIndex, composerRows, trailing } = frame;
856
+ if (composerRows.length !== 1 ||
857
+ !/^\s*❯(?:\s|$)/u.test(composerRows[0]) ||
858
+ composerRows[0].replace(/^\s*❯\s?/u, "").trimEnd() !== plan.command) {
859
+ return undefined;
860
+ }
861
+ let kind;
862
+ if (trailing.length === 0 || claudeNativeInspectionTrailingIsFooter(trailing)) {
863
+ kind = "exact_slash_composer";
864
+ }
865
+ else {
866
+ const suggestions = [];
867
+ for (const line of trailing) {
868
+ const trimmed = line.trim();
869
+ if (trimmed.startsWith("/")) {
870
+ suggestions.push(trimmed.replace(/\s+/gu, " "));
871
+ }
872
+ else if (suggestions.length > 0) {
873
+ suggestions[suggestions.length - 1] +=
874
+ ` ${trimmed.replace(/\s+/gu, " ")}`;
875
+ }
876
+ else {
877
+ return undefined;
878
+ }
879
+ }
880
+ if (!closedClaudeNativeStatusSuggestionsMatch(suggestions, CLAUDE_NATIVE_STATUS_POPUP_BY_PROFILE[plan.behaviorProfile])) {
881
+ return undefined;
882
+ }
883
+ kind = "exact_slash_popup";
884
+ }
885
+ return {
886
+ kind,
887
+ digest: createHash("sha256")
888
+ .update(lines.slice(openIndex, closeIndex + 1).concat(trailing).join("\n"))
889
+ .digest("hex")
890
+ };
891
+ }
892
+ /**
893
+ * Claude truncates a suggestion row with a Unicode ellipsis at narrow pane
894
+ * widths. Keep authorization closed by accepting only an exact ordered row or
895
+ * an explicit ellipsis whose preceding text is an exact, non-trivial prefix of
896
+ * that same profiled row. A caller still cannot introduce, omit, or reorder a
897
+ * slash command.
898
+ */
899
+ function closedClaudeNativeStatusSuggestionsMatch(observed, expected) {
900
+ if (!expected || observed.length !== expected.length) {
901
+ return false;
902
+ }
903
+ return observed.every((row, index) => {
904
+ const exact = expected[index];
905
+ if (row === exact) {
906
+ return true;
907
+ }
908
+ if (!row.endsWith("…")) {
909
+ return false;
910
+ }
911
+ const prefix = row.slice(0, -1);
912
+ const commandEnd = exact.indexOf(" ");
913
+ return (commandEnd > 0 &&
914
+ prefix.length >= commandEnd + 12 &&
915
+ exact.startsWith(prefix));
916
+ });
917
+ }
918
+ /**
919
+ * Prove Claude Code's exact current idle input frame. This is shared by every
920
+ * automated-input path: a loose or historical `❯` prompt is not authority to
921
+ * inject text into the terminal.
922
+ */
923
+ export function isExactClaudeIdleComposer(screen) {
924
+ const frame = exactClaudeComposerFrame(screen);
925
+ if (!frame) {
926
+ return false;
927
+ }
928
+ return (frame.composerRows.length === 1 &&
929
+ /^\s*❯\s*$/u.test(frame.composerRows[0]) &&
930
+ (frame.trailing.length === 0 ||
931
+ claudeNativeInspectionTrailingIsFooter(frame.trailing)));
932
+ }
933
+ /**
934
+ * Compatibility export retained for callers that adopted the native-status
935
+ * name before the same exact-frame proof was reused by lifecycle handoff.
936
+ */
937
+ export function isExactClaudeNativeInspectionIdleComposer(screen) {
938
+ return isExactClaudeIdleComposer(screen);
939
+ }
940
+ export function exactClaudeComposerFrame(screen) {
941
+ const lines = screen.replace(/\r\n?/gu, "\n").replace(/\u00a0/gu, " ")
942
+ .split("\n");
943
+ const dividerIndexes = lines
944
+ .map((line, index) => /^\s*[─━]{8,}\s*$/u.test(line) ? index : -1)
945
+ .filter((index) => index >= 0);
946
+ if (dividerIndexes.length < 2) {
947
+ return undefined;
948
+ }
949
+ const closeIndex = dividerIndexes.at(-1);
950
+ const openIndex = dividerIndexes.at(-2);
951
+ return {
952
+ lines,
953
+ openIndex,
954
+ closeIndex,
955
+ composerRows: lines.slice(openIndex + 1, closeIndex)
956
+ .filter((line) => line.trim().length > 0),
957
+ trailing: lines.slice(closeIndex + 1)
958
+ .filter((line) => line.trim().length > 0)
959
+ };
960
+ }
961
+ export function claudeNativeInspectionTrailingIsFooter(lines) {
962
+ return lines.length <= 2 && lines.every(isClaudeNativeComposerFooterLine);
963
+ }
964
+ function isClaudeNativeComposerFooterLine(line) {
965
+ return /^\s*(?:[⏵⏴⏸]{1,2}|\?)\s*.*(?:manual mode|shift\+tab|accept edits|bypass permissions|for shortcuts|← for agents)/iu
966
+ .test(line);
967
+ }
968
+ function nativeInspectionScreenFingerprint(screen) {
969
+ return `sha256:${createHash("sha256").update(screen).digest("hex")}`;
970
+ }
971
+ function bareDigestFromNativeInspectionScreenFingerprint(fingerprint) {
972
+ const match = /^sha256:([0-9a-f]{64})$/u.exec(fingerprint);
973
+ if (!match) {
974
+ throw new Error("native inspection screen fingerprint is malformed");
975
+ }
976
+ return match[1];
977
+ }
978
+ export function stripTerminalEscapeSequences(value) {
979
+ return value.replace(/\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/gu, "");
980
+ }
981
+ export function exactCodexReadyStyledComposerCapture(screen) {
982
+ const lines = screen.replace(/\r\n?/gu, "\n").split("\n");
983
+ while (lines.length > 0 &&
984
+ stripTerminalEscapeSequences(lines.at(-1) ?? "").trim().length === 0) {
985
+ lines.pop();
986
+ }
987
+ const composerLine = [...lines.slice(-12)].reverse().find((line) => CODEX_COMPOSER_MARKER.test(stripTerminalEscapeSequences(line).trimEnd()));
988
+ if (composerLine === undefined) {
989
+ return undefined;
990
+ }
991
+ let dim = false;
992
+ const visible = [];
993
+ for (let index = 0; index < composerLine.length;) {
994
+ if (composerLine[index] === "\x1b") {
995
+ const escape = /^(?:\x1B\[([0-9;]*)m|\x1B\][^\x07]*(?:\x07|\x1B\\))/u
996
+ .exec(composerLine.slice(index));
997
+ if (escape) {
998
+ if (escape[1] !== undefined) {
999
+ const codes = escape[1] === ""
1000
+ ? [0]
1001
+ : escape[1].split(";").map((value) => Number(value));
1002
+ for (let codeIndex = 0; codeIndex < codes.length; codeIndex += 1) {
1003
+ const code = codes[codeIndex];
1004
+ if ([38, 48, 58].includes(code) &&
1005
+ codes[codeIndex + 1] === 2) {
1006
+ codeIndex += 4;
1007
+ continue;
1008
+ }
1009
+ if ([38, 48, 58].includes(code) &&
1010
+ codes[codeIndex + 1] === 5) {
1011
+ codeIndex += 2;
1012
+ continue;
1013
+ }
1014
+ if (code === 0 || code === 22) {
1015
+ dim = false;
1016
+ }
1017
+ else if (code === 2) {
1018
+ dim = true;
1019
+ }
1020
+ }
1021
+ }
1022
+ index += escape[0].length;
1023
+ continue;
1024
+ }
1025
+ }
1026
+ const codePoint = composerLine.codePointAt(index);
1027
+ if (codePoint === undefined) {
1028
+ break;
1029
+ }
1030
+ const character = String.fromCodePoint(codePoint);
1031
+ visible.push({ character, dim });
1032
+ index += character.length;
1033
+ }
1034
+ const promptIndex = visible.findIndex(({ character }) => character === "›" || character === "»");
1035
+ if (promptIndex < 0) {
1036
+ return undefined;
1037
+ }
1038
+ const content = visible.slice(promptIndex + 1)
1039
+ .filter(({ character }) => !/^\s$/u.test(character));
1040
+ if (content.length > 0 && !content.every((entry) => entry.dim)) {
1041
+ return undefined;
1042
+ }
1043
+ return {
1044
+ digest: createHash("sha256").update(composerLine).digest("hex")
1045
+ };
1046
+ }
1047
+ /**
1048
+ * Infer a viewport only from fixed-width visible-buffer rows. Trimmed captures
1049
+ * deliberately return undefined: a short content row is not proof of a short
1050
+ * terminal. This keeps the fallback provider-neutral and fail-closed only on
1051
+ * positive geometry evidence.
1052
+ */
1053
+ export function inferCodexVisibleViewportColumns(screen) {
1054
+ const rows = screen.replace(/\r\n?/gu, "\n").split("\n")
1055
+ .map(stripTerminalEscapeSequences);
1056
+ const widthOneRows = rows.filter((row) => /^[\x20-\x7e›»·─━╭╮╰╯│]*$/u.test(row));
1057
+ const maxWidth = widthOneRows.reduce((maximum, row) => Math.max(maximum, Array.from(row).length), 0);
1058
+ if (maxWidth < 20) {
1059
+ return undefined;
1060
+ }
1061
+ const paddedAtMax = widthOneRows.filter((row) => row.endsWith(" ") && Array.from(row).length === maxWidth);
1062
+ const composerAtMax = paddedAtMax.some((row) => CODEX_COMPOSER_MARKER.test(row.trimEnd()));
1063
+ return paddedAtMax.length >= 3 && composerAtMax
1064
+ ? maxWidth
1065
+ : undefined;
1066
+ }
1067
+ function hasTruncatedCodexStatusSessionLine(screen) {
1068
+ return screen.replace(/\r\n?/gu, "\n").split("\n").some((line) => {
1069
+ const match = /^\s*│\s*Session:\s*([^│\s]+).*│?\s*$/iu.exec(line);
1070
+ if (!match) {
1071
+ return false;
1072
+ }
1073
+ return !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu
1074
+ .test(match[1]);
1075
+ });
1076
+ }
1077
+ function codexNativeInspectionComposerMismatchDiagnostic(screen, plan) {
1078
+ const expectedRows = CODEX_NATIVE_STATUS_POPUP_BY_PROFILE[plan.behaviorProfile];
1079
+ if (!expectedRows) {
1080
+ return "composer_not_exact";
1081
+ }
1082
+ const lines = screen.replace(/\r\n?/gu, "\n").split("\n");
1083
+ let composerIndex = -1;
1084
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
1085
+ if (CODEX_COMPOSER_MARKER.test(lines[index])) {
1086
+ composerIndex = index;
1087
+ break;
1088
+ }
1089
+ }
1090
+ if (composerIndex < 0 ||
1091
+ lines[composerIndex].replace(/^[›»]\s?/u, "").trimEnd() !== plan.command) {
1092
+ return "composer_not_exact";
1093
+ }
1094
+ const footerIndex = lines.findIndex((line, index) => index > composerIndex && CODEX_COMPOSER_FOOTER.test(line.trim()));
1095
+ const popupRows = lines.slice(composerIndex + 1, footerIndex < 0 ? lines.length : footerIndex).filter((line) => line.trim().length > 0);
1096
+ if (popupRows.length === 0) {
1097
+ return "composer_not_exact";
1098
+ }
1099
+ const logicalRows = [];
1100
+ let observedTruncation = false;
1101
+ for (const row of popupRows) {
1102
+ const trimmed = row.trim().replace(/\s+/gu, " ");
1103
+ if (trimmed.startsWith("/")) {
1104
+ logicalRows.push(trimmed);
1105
+ }
1106
+ else if (logicalRows.length > 0) {
1107
+ logicalRows[logicalRows.length - 1] += ` ${trimmed}`;
1108
+ observedTruncation = true;
1109
+ }
1110
+ else {
1111
+ return "composer_not_exact";
1112
+ }
1113
+ observedTruncation ||= trimmed.endsWith("…");
1114
+ }
1115
+ const normalizedExpected = expectedRows.map((row) => row.trim().replace(/\s+/gu, " "));
1116
+ const everyKnownPrefix = logicalRows.length <= normalizedExpected.length &&
1117
+ logicalRows.every((row, index) => {
1118
+ const withoutEllipsis = row.endsWith("…")
1119
+ ? row.slice(0, -1).trimEnd()
1120
+ : row;
1121
+ return normalizedExpected[index]?.startsWith(withoutEllipsis) === true;
1122
+ });
1123
+ return observedTruncation && everyKnownPrefix
1124
+ ? "composer_viewport_truncated"
1125
+ : "composer_not_exact";
1126
+ }
1127
+ //# sourceMappingURL=terminal-native-inspection-bridge.js.map