@jmoyers/harness 0.1.10 → 0.1.20

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 (239) hide show
  1. package/README.md +31 -35
  2. package/package.json +31 -11
  3. package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
  4. package/packages/harness-ai/src/stream-text.ts +13 -91
  5. package/packages/harness-ui/src/frame-primitives.ts +158 -0
  6. package/packages/harness-ui/src/index.ts +18 -0
  7. package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
  8. package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
  9. package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
  10. package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
  11. package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
  12. package/packages/harness-ui/src/interaction/input.ts +420 -0
  13. package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
  14. package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
  15. package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
  16. package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
  17. package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
  18. package/packages/harness-ui/src/kit.ts +476 -0
  19. package/packages/harness-ui/src/layout.ts +238 -0
  20. package/{src/ui/modals/manager.ts → packages/harness-ui/src/modal-manager.ts} +94 -64
  21. package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
  22. package/packages/harness-ui/src/surface.ts +252 -0
  23. package/packages/harness-ui/src/text-layout.ts +210 -0
  24. package/packages/nim-core/src/contracts.ts +239 -0
  25. package/packages/nim-core/src/event-store.ts +299 -0
  26. package/packages/nim-core/src/events.ts +53 -0
  27. package/packages/nim-core/src/index.ts +9 -0
  28. package/packages/nim-core/src/provider-router.ts +129 -0
  29. package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
  30. package/packages/nim-core/src/runtime-factory.ts +49 -0
  31. package/packages/nim-core/src/runtime.ts +1797 -0
  32. package/packages/nim-core/src/session-store.ts +516 -0
  33. package/packages/nim-core/src/telemetry.ts +48 -0
  34. package/packages/nim-test-tui/src/index.ts +150 -0
  35. package/packages/nim-ui-core/src/index.ts +1 -0
  36. package/packages/nim-ui-core/src/projection.ts +87 -0
  37. package/scripts/codex-live-mux-runtime.ts +2 -3721
  38. package/scripts/control-plane-daemon.ts +24 -2
  39. package/scripts/harness-bin.js +5 -0
  40. package/scripts/harness-commands.ts +300 -0
  41. package/scripts/harness-runtime.ts +82 -0
  42. package/scripts/harness.ts +33 -3007
  43. package/scripts/nim-tui-smoke.ts +748 -0
  44. package/src/cli/auth/runtime.ts +948 -0
  45. package/src/cli/default-gateway-pointer.ts +193 -0
  46. package/src/cli/gateway/runtime.ts +1872 -0
  47. package/src/cli/parsing/flags.ts +23 -0
  48. package/src/cli/parsing/session.ts +42 -0
  49. package/src/cli/runtime/context.ts +193 -0
  50. package/src/cli/runtime-app/application.ts +392 -0
  51. package/src/cli/runtime-infra/gateway-control.ts +729 -0
  52. package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
  53. package/src/cli/workflows/runtime.ts +965 -0
  54. package/src/clients/tui/left-rail-interactions.ts +519 -0
  55. package/src/clients/tui/main-pane-interactions.ts +509 -0
  56. package/src/clients/tui/modal-input-routing.ts +71 -0
  57. package/src/clients/tui/render-snapshot-adapter.ts +88 -0
  58. package/src/clients/web/synced-selectors.ts +132 -0
  59. package/src/codex/live-session.ts +82 -29
  60. package/src/config/config-core.ts +361 -10
  61. package/src/config/harness-paths.ts +4 -7
  62. package/src/config/harness-runtime-migration.ts +142 -19
  63. package/src/config/harness.config.template.jsonc +33 -0
  64. package/src/config/secrets-core.ts +92 -4
  65. package/src/control-plane/agent-realtime-api.ts +82 -427
  66. package/src/control-plane/prompt/thread-title-namer.ts +49 -23
  67. package/src/control-plane/session-summary.ts +10 -81
  68. package/src/control-plane/status/reducer-base.ts +12 -12
  69. package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
  70. package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
  71. package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
  72. package/src/control-plane/stream-client.ts +12 -2
  73. package/src/control-plane/stream-command-parser.ts +83 -143
  74. package/src/control-plane/stream-protocol.ts +53 -37
  75. package/src/control-plane/stream-server-background.ts +18 -2
  76. package/src/control-plane/stream-server-command.ts +376 -69
  77. package/src/control-plane/stream-server-session-runtime.ts +3 -2
  78. package/src/control-plane/stream-server.ts +943 -80
  79. package/src/control-plane/stream-session-runtime-types.ts +41 -0
  80. package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
  81. package/src/core/state/observed-stream-cursor.ts +43 -0
  82. package/src/core/state/synced-observed-state.ts +273 -0
  83. package/src/core/store/harness-synced-store.ts +81 -0
  84. package/src/diff/budget.ts +136 -0
  85. package/src/diff/build.ts +289 -0
  86. package/src/diff/chunker.ts +146 -0
  87. package/src/diff/git-invoke.ts +315 -0
  88. package/src/diff/git-parse.ts +472 -0
  89. package/src/diff/hash.ts +70 -0
  90. package/src/diff/index.ts +24 -0
  91. package/src/diff/normalize.ts +134 -0
  92. package/src/diff/types.ts +178 -0
  93. package/src/diff-ui/args.ts +346 -0
  94. package/src/diff-ui/commands.ts +123 -0
  95. package/src/diff-ui/finder.ts +94 -0
  96. package/src/diff-ui/highlight.ts +127 -0
  97. package/src/diff-ui/index.ts +2 -0
  98. package/src/diff-ui/model.ts +141 -0
  99. package/src/diff-ui/pager.ts +412 -0
  100. package/src/diff-ui/render.ts +337 -0
  101. package/src/diff-ui/runtime.ts +379 -0
  102. package/src/diff-ui/state.ts +224 -0
  103. package/src/diff-ui/types.ts +236 -0
  104. package/src/domain/conversations.ts +11 -7
  105. package/src/domain/workspace.ts +76 -4
  106. package/src/mux/control-plane-op-queue.ts +93 -7
  107. package/src/mux/conversation-rail.ts +28 -71
  108. package/src/mux/dual-pane-core.ts +13 -13
  109. package/src/mux/harness-core-ui.ts +313 -42
  110. package/src/mux/input-shortcuts.ts +22 -112
  111. package/src/mux/keybinding-catalog.ts +340 -0
  112. package/src/mux/keybinding-registry.ts +103 -0
  113. package/src/mux/live-mux/command-menu-open-in.ts +280 -0
  114. package/src/mux/live-mux/command-menu.ts +167 -4
  115. package/src/mux/live-mux/conversation-state.ts +13 -0
  116. package/src/mux/live-mux/directory-resolution.ts +1 -1
  117. package/src/mux/live-mux/git-parsing.ts +16 -0
  118. package/src/mux/live-mux/git-snapshot.ts +33 -2
  119. package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
  120. package/src/mux/live-mux/home-pane-drop.ts +1 -1
  121. package/src/mux/live-mux/home-pane-pointer.ts +10 -0
  122. package/src/mux/live-mux/input-forwarding.ts +59 -2
  123. package/src/mux/live-mux/left-nav-activation.ts +124 -7
  124. package/src/mux/live-mux/left-nav.ts +35 -0
  125. package/src/mux/live-mux/link-click.ts +292 -0
  126. package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
  127. package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
  128. package/src/mux/live-mux/modal-input-reducers.ts +106 -8
  129. package/src/mux/live-mux/modal-overlays.ts +210 -31
  130. package/src/mux/live-mux/modal-pointer.ts +3 -7
  131. package/src/mux/live-mux/modal-prompt-handlers.ts +107 -1
  132. package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
  133. package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
  134. package/src/mux/live-mux/pointer-routing.ts +5 -2
  135. package/src/mux/live-mux/project-pane-pointer.ts +8 -0
  136. package/src/mux/live-mux/rail-layout.ts +33 -30
  137. package/src/mux/live-mux/release-notes.ts +383 -0
  138. package/src/mux/live-mux/render-trace-analysis.ts +52 -7
  139. package/src/mux/live-mux/repository-folding.ts +3 -0
  140. package/src/mux/live-mux/selection.ts +0 -4
  141. package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
  142. package/src/mux/project-pane-github-review.ts +271 -0
  143. package/src/mux/render-frame.ts +4 -0
  144. package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
  145. package/src/mux/task-composer.ts +21 -14
  146. package/src/mux/task-focused-pane.ts +118 -117
  147. package/src/mux/task-screen-keybindings.ts +19 -82
  148. package/src/mux/workspace-rail-model.ts +270 -104
  149. package/src/mux/workspace-rail.ts +45 -22
  150. package/src/pty/session-broker.ts +1 -1
  151. package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
  152. package/src/services/control-plane.ts +50 -32
  153. package/src/services/conversation-lifecycle.ts +118 -87
  154. package/src/services/conversation-startup-hydration.ts +20 -12
  155. package/src/services/directory-hydration.ts +21 -16
  156. package/src/services/event-persistence.ts +7 -0
  157. package/src/services/left-rail-pointer-handler.ts +329 -0
  158. package/src/services/mux-ui-state-persistence.ts +5 -1
  159. package/src/services/recording.ts +34 -26
  160. package/src/services/runtime-command-menu-agent-tools.ts +1 -1
  161. package/src/services/runtime-control-actions.ts +79 -61
  162. package/src/services/runtime-control-plane-ops.ts +122 -83
  163. package/src/services/runtime-conversation-actions.ts +40 -26
  164. package/src/services/runtime-conversation-activation.ts +82 -30
  165. package/src/services/runtime-conversation-starter.ts +80 -48
  166. package/src/services/runtime-conversation-title-edit.ts +91 -80
  167. package/src/services/runtime-envelope-handler.ts +107 -105
  168. package/src/services/runtime-git-state.ts +42 -29
  169. package/src/services/runtime-layout-resize.ts +3 -1
  170. package/src/services/runtime-left-rail-render.ts +99 -63
  171. package/src/services/runtime-nim-cli-session.ts +438 -0
  172. package/src/services/runtime-nim-session.ts +705 -0
  173. package/src/services/runtime-nim-tool-bridge.ts +141 -0
  174. package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
  175. package/src/services/runtime-process-wiring.ts +29 -36
  176. package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
  177. package/src/services/runtime-render-flush.ts +63 -70
  178. package/src/services/runtime-render-lifecycle.ts +65 -64
  179. package/src/services/runtime-render-orchestrator.ts +55 -45
  180. package/src/services/runtime-render-pipeline.ts +106 -103
  181. package/src/services/runtime-render-state.ts +62 -49
  182. package/src/services/runtime-repository-actions.ts +97 -70
  183. package/src/services/runtime-right-pane-render.ts +80 -53
  184. package/src/services/runtime-shutdown.ts +38 -35
  185. package/src/services/runtime-stream-subscriptions.ts +35 -27
  186. package/src/services/runtime-task-composer-persistence.ts +71 -59
  187. package/src/services/runtime-task-composer-snapshot.ts +14 -0
  188. package/src/services/runtime-task-editor-actions.ts +46 -29
  189. package/src/services/runtime-task-pane-actions.ts +220 -134
  190. package/src/services/runtime-task-pane-shortcuts.ts +323 -123
  191. package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
  192. package/src/services/runtime-workspace-observed-events.ts +33 -184
  193. package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
  194. package/src/services/session-diagnostics-store.ts +217 -0
  195. package/src/services/startup-background-resume.ts +26 -21
  196. package/src/services/startup-orchestrator.ts +16 -13
  197. package/src/services/startup-paint-tracker.ts +29 -21
  198. package/src/services/startup-persisted-conversation-queue.ts +19 -13
  199. package/src/services/startup-settled-gate.ts +25 -15
  200. package/src/services/startup-shutdown.ts +18 -22
  201. package/src/services/startup-state-hydration.ts +44 -34
  202. package/src/services/startup-visibility.ts +12 -4
  203. package/src/services/task-pane-selection-actions.ts +89 -72
  204. package/src/services/task-planning-hydration.ts +24 -18
  205. package/src/services/task-planning-observed-events.ts +50 -52
  206. package/src/services/workspace-observed-events.ts +66 -63
  207. package/src/storage/storage-lifecycle-core.ts +438 -0
  208. package/src/store/control-plane-store-normalize.ts +33 -242
  209. package/src/store/control-plane-store-types.ts +1 -35
  210. package/src/store/control-plane-store.ts +396 -56
  211. package/src/store/event-store.ts +397 -3
  212. package/src/terminal/snapshot-oracle.ts +207 -94
  213. package/src/ui/mux-theme.ts +112 -8
  214. package/src/ui/panes/home-gridfire.ts +40 -31
  215. package/src/ui/panes/home.ts +10 -2
  216. package/src/ui/panes/nim.ts +315 -0
  217. package/src/mux/live-mux/actions-task.ts +0 -115
  218. package/src/mux/live-mux/left-rail-actions.ts +0 -118
  219. package/src/mux/live-mux/left-rail-conversation-click.ts +0 -82
  220. package/src/mux/live-mux/left-rail-pointer.ts +0 -74
  221. package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
  222. package/src/services/runtime-directory-actions.ts +0 -164
  223. package/src/services/runtime-input-pipeline.ts +0 -50
  224. package/src/services/runtime-input-router.ts +0 -189
  225. package/src/services/runtime-main-pane-input.ts +0 -230
  226. package/src/services/runtime-modal-input.ts +0 -119
  227. package/src/services/runtime-navigation-input.ts +0 -197
  228. package/src/services/runtime-rail-input.ts +0 -278
  229. package/src/services/runtime-task-pane.ts +0 -62
  230. package/src/services/runtime-workspace-actions.ts +0 -158
  231. package/src/ui/conversation-input-forwarder.ts +0 -114
  232. package/src/ui/conversation-selection-input.ts +0 -103
  233. package/src/ui/global-shortcut-input.ts +0 -89
  234. package/src/ui/input.ts +0 -238
  235. package/src/ui/kit.ts +0 -509
  236. package/src/ui/left-nav-input.ts +0 -80
  237. package/src/ui/left-rail-pointer-input.ts +0 -148
  238. package/src/ui/repository-fold-input.ts +0 -91
  239. package/src/ui/surface.ts +0 -224
@@ -0,0 +1,132 @@
1
+ import type {
2
+ ControlPlaneConversationRecord,
3
+ ControlPlaneDirectoryRecord,
4
+ ControlPlaneTaskRecord,
5
+ } from '../../core/contracts/records.ts';
6
+ import type {
7
+ HarnessSyncedStore,
8
+ HarnessSyncedStoreState,
9
+ } from '../../core/store/harness-synced-store.ts';
10
+
11
+ export interface WebConversationListItem {
12
+ readonly conversationId: string;
13
+ readonly directoryId: string;
14
+ readonly title: string;
15
+ readonly agentType: string;
16
+ readonly runtimeStatus: ControlPlaneConversationRecord['runtimeStatus'];
17
+ readonly phase: NonNullable<ControlPlaneConversationRecord['runtimeStatusModel']>['phase'] | null;
18
+ readonly activityHint:
19
+ | NonNullable<ControlPlaneConversationRecord['runtimeStatusModel']>['activityHint']
20
+ | null;
21
+ }
22
+
23
+ export interface WebTaskListItem {
24
+ readonly taskId: string;
25
+ readonly repositoryId: string | null;
26
+ readonly title: string;
27
+ readonly status: ControlPlaneTaskRecord['status'];
28
+ readonly orderIndex: number;
29
+ }
30
+
31
+ interface MemoizedProjection<TResult> {
32
+ readonly read: (state: HarnessSyncedStoreState) => TResult;
33
+ }
34
+
35
+ function createMemoizedProjection<TById extends Readonly<Record<string, unknown>>, TResult>(
36
+ selectById: (state: HarnessSyncedStoreState) => TById,
37
+ project: (byId: TById) => TResult,
38
+ ): MemoizedProjection<TResult> {
39
+ let previousById: TById | null = null;
40
+ let previousResult: TResult | null = null;
41
+ return {
42
+ read: (state) => {
43
+ const currentById = selectById(state);
44
+ if (previousById === currentById && previousResult !== null) {
45
+ return previousResult;
46
+ }
47
+ const nextResult = project(currentById);
48
+ previousById = currentById;
49
+ previousResult = nextResult;
50
+ return nextResult;
51
+ },
52
+ };
53
+ }
54
+
55
+ export function createWebConversationListSelector(): (
56
+ state: HarnessSyncedStoreState,
57
+ ) => readonly WebConversationListItem[] {
58
+ const memoized = createMemoizedProjection(
59
+ (state) => state.synced.conversationsById,
60
+ (conversationsById) =>
61
+ Object.values(conversationsById)
62
+ .sort((left, right) => left.conversationId.localeCompare(right.conversationId))
63
+ .map((conversation) => ({
64
+ conversationId: conversation.conversationId,
65
+ directoryId: conversation.directoryId,
66
+ title: conversation.title,
67
+ agentType: conversation.agentType,
68
+ runtimeStatus: conversation.runtimeStatus,
69
+ phase: conversation.runtimeStatusModel?.phase ?? null,
70
+ activityHint: conversation.runtimeStatusModel?.activityHint ?? null,
71
+ })),
72
+ );
73
+ return (state) => memoized.read(state);
74
+ }
75
+
76
+ export function createWebTaskListSelector(): (
77
+ state: HarnessSyncedStoreState,
78
+ ) => readonly WebTaskListItem[] {
79
+ const memoized = createMemoizedProjection(
80
+ (state) => state.synced.tasksById,
81
+ (tasksById) =>
82
+ Object.values(tasksById)
83
+ .sort((left, right) => left.taskId.localeCompare(right.taskId))
84
+ .sort((left, right) => {
85
+ if (left.orderIndex !== right.orderIndex) {
86
+ return left.orderIndex - right.orderIndex;
87
+ }
88
+ return left.taskId.localeCompare(right.taskId);
89
+ })
90
+ .map((task) => ({
91
+ taskId: task.taskId,
92
+ repositoryId: task.repositoryId,
93
+ title: task.title,
94
+ status: task.status,
95
+ orderIndex: task.orderIndex,
96
+ })),
97
+ );
98
+ return (state) => memoized.read(state);
99
+ }
100
+
101
+ export function selectWebDirectoryList(
102
+ state: HarnessSyncedStoreState,
103
+ ): readonly ControlPlaneDirectoryRecord[] {
104
+ return Object.values(state.synced.directoriesById).sort((left, right) =>
105
+ left.directoryId.localeCompare(right.directoryId),
106
+ );
107
+ }
108
+
109
+ export function selectWebConversationById(
110
+ state: HarnessSyncedStoreState,
111
+ conversationId: string,
112
+ ): ControlPlaneConversationRecord | null {
113
+ return state.synced.conversationsById[conversationId] ?? null;
114
+ }
115
+
116
+ export function subscribeStoreSelector<TSelected>(
117
+ store: HarnessSyncedStore,
118
+ select: (state: HarnessSyncedStoreState) => TSelected,
119
+ onChange: (selected: TSelected, previous: TSelected) => void,
120
+ equals: (left: TSelected, right: TSelected) => boolean = Object.is,
121
+ ): () => void {
122
+ let previousSelected = select(store.getState());
123
+ return store.subscribe((state) => {
124
+ const nextSelected = select(state);
125
+ if (equals(nextSelected, previousSelected)) {
126
+ return;
127
+ }
128
+ const before = previousSelected;
129
+ previousSelected = nextSelected;
130
+ onChange(nextSelected, before);
131
+ });
132
+ }
@@ -172,6 +172,68 @@ const DEFAULT_DA2_REPLY = '\u001b[>1;10;0c';
172
172
  const CELL_PIXEL_HEIGHT = 16;
173
173
  const CELL_PIXEL_WIDTH = 8;
174
174
 
175
+ function isAsciiDigits(value: string, allowEmpty = false): boolean {
176
+ if (!allowEmpty && value.length === 0) {
177
+ return false;
178
+ }
179
+ for (let index = 0; index < value.length; index += 1) {
180
+ const code = value.charCodeAt(index);
181
+ if (code < 0x30 || code > 0x39) {
182
+ return false;
183
+ }
184
+ }
185
+ return true;
186
+ }
187
+
188
+ function isAsciiDigitsOrSemicolons(value: string): boolean {
189
+ for (let index = 0; index < value.length; index += 1) {
190
+ const code = value.charCodeAt(index);
191
+ if (code === 0x3b) {
192
+ continue;
193
+ }
194
+ if (code < 0x30 || code > 0x39) {
195
+ return false;
196
+ }
197
+ }
198
+ return true;
199
+ }
200
+
201
+ function parseAsciiInt(value: string): number | null {
202
+ if (!isAsciiDigits(value, false)) {
203
+ return null;
204
+ }
205
+ let parsed = 0;
206
+ for (let index = 0; index < value.length; index += 1) {
207
+ parsed = parsed * 10 + (value.charCodeAt(index) - 0x30);
208
+ }
209
+ return parsed;
210
+ }
211
+
212
+ function parseKittyKeyboardFlags(payload: string): number | null {
213
+ if (!payload.startsWith('>') || !payload.endsWith('u')) {
214
+ return null;
215
+ }
216
+ return parseAsciiInt(payload.slice(1, -1));
217
+ }
218
+
219
+ function parseModifyOtherKeysLevel(payload: string): number | null {
220
+ if (payload === '>4m') {
221
+ return 0;
222
+ }
223
+ if (!payload.startsWith('>4;') || !payload.endsWith('m')) {
224
+ return null;
225
+ }
226
+ const rawLevel = payload.slice(3, -1);
227
+ if (!isAsciiDigits(rawLevel, false)) {
228
+ return null;
229
+ }
230
+ const parsedLevel = parseAsciiInt(rawLevel);
231
+ if (parsedLevel === null) {
232
+ return null;
233
+ }
234
+ return Math.max(0, Math.min(2, parsedLevel));
235
+ }
236
+
175
237
  function tsRuntimeArgs(scriptPath: string, args: readonly string[] = []): string[] {
176
238
  return [scriptPath, ...args];
177
239
  }
@@ -381,8 +443,7 @@ class TerminalQueryResponder {
381
443
  };
382
444
  let handled = false;
383
445
 
384
- const modifyOtherKeysQueryMatch = payload.match(/^>4;\?m$/u);
385
- if (!handled && modifyOtherKeysQueryMatch !== null) {
446
+ if (!handled && payload === '>4;?m') {
386
447
  this.writeReply(`\u001b[>4;${String(this.modifyOtherKeysLevel)}m`);
387
448
  handled = true;
388
449
  }
@@ -446,29 +507,15 @@ class TerminalQueryResponder {
446
507
  }
447
508
 
448
509
  private observeCsiCommand(payload: string): void {
449
- const kittyKeyboardMatch = payload.match(/^>(\d+)u$/u);
450
- if (kittyKeyboardMatch !== null) {
451
- const nextFlags = Number.parseInt(kittyKeyboardMatch[1]!, 10);
452
- if (Number.isFinite(nextFlags)) {
453
- this.kittyKeyboardFlags = Math.max(0, nextFlags);
454
- }
510
+ const nextFlags = parseKittyKeyboardFlags(payload);
511
+ if (nextFlags !== null) {
512
+ this.kittyKeyboardFlags = Math.max(0, nextFlags);
455
513
  return;
456
514
  }
457
515
 
458
- const modifyOtherKeysMatch = payload.match(/^>4(?:;(\d+))?m$/u);
459
- if (modifyOtherKeysMatch !== null) {
460
- const rawLevel = modifyOtherKeysMatch[1];
461
- if (rawLevel === undefined) {
462
- this.modifyOtherKeysLevel = 0;
463
- return;
464
- }
465
-
466
- const parsedLevel = Number.parseInt(rawLevel, 10);
467
- if (!Number.isFinite(parsedLevel)) {
468
- return;
469
- }
470
-
471
- this.modifyOtherKeysLevel = Math.max(0, Math.min(2, parsedLevel));
516
+ const modifyOtherKeysLevel = parseModifyOtherKeysLevel(payload);
517
+ if (modifyOtherKeysLevel !== null) {
518
+ this.modifyOtherKeysLevel = modifyOtherKeysLevel;
472
519
  }
473
520
  }
474
521
 
@@ -491,22 +538,28 @@ class TerminalQueryResponder {
491
538
  }
492
539
 
493
540
  private isLikelyCsiQueryPayload(payload: string): boolean {
494
- if (/^(?:c|0c|>c|>0c)$/.test(payload)) {
541
+ if (payload === 'c' || payload === '0c' || payload === '>c' || payload === '>0c') {
495
542
  return true;
496
543
  }
497
- if (/^[0-9]*n$/.test(payload)) {
544
+ if (payload.endsWith('n') && isAsciiDigits(payload.slice(0, -1), true)) {
498
545
  return true;
499
546
  }
500
- if (/^(?:14|16|18)t$/.test(payload)) {
547
+ if (payload === '14t' || payload === '16t' || payload === '18t') {
501
548
  return true;
502
549
  }
503
- if (/^>0q$/.test(payload)) {
550
+ if (payload === '>0q') {
504
551
  return true;
505
552
  }
506
- if (/^>4;\?m$/.test(payload)) {
553
+ if (payload === '>4;?m') {
507
554
  return true;
508
555
  }
509
- if (/^\?[0-9;]*\$p$/.test(payload)) {
556
+ if (
557
+ payload.length >= 3 &&
558
+ payload.startsWith('?') &&
559
+ payload.endsWith('p') &&
560
+ payload.charCodeAt(payload.length - 2) === 0x24 &&
561
+ isAsciiDigitsOrSemicolons(payload.slice(1, -2))
562
+ ) {
510
563
  return true;
511
564
  }
512
565
  if (payload === '?u') {
@@ -616,7 +669,7 @@ class CodexLiveSession {
616
669
  this.emit({
617
670
  type: 'terminal-output',
618
671
  cursor: event.cursor,
619
- chunk: Buffer.from(event.chunk),
672
+ chunk: event.chunk,
620
673
  });
621
674
  },
622
675
  onExit: (exit: PtyExit) => {