@0xmaxma/claude-gateway 1.8.2 → 1.8.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 (99) hide show
  1. package/README.md +63 -2
  2. package/config.template.json +4 -0
  3. package/dist/agent/dreaming/config.d.ts +6 -0
  4. package/dist/agent/dreaming/config.d.ts.map +1 -1
  5. package/dist/agent/dreaming/config.js +17 -24
  6. package/dist/agent/dreaming/config.js.map +1 -1
  7. package/dist/agent/knowledge/config.d.ts.map +1 -1
  8. package/dist/agent/knowledge/config.js +7 -13
  9. package/dist/agent/knowledge/config.js.map +1 -1
  10. package/dist/agent/runner.d.ts +87 -13
  11. package/dist/agent/runner.d.ts.map +1 -1
  12. package/dist/agent/runner.js +342 -117
  13. package/dist/agent/runner.js.map +1 -1
  14. package/dist/agent/turn-stream.d.ts +202 -0
  15. package/dist/agent/turn-stream.d.ts.map +1 -0
  16. package/dist/agent/turn-stream.js +322 -0
  17. package/dist/agent/turn-stream.js.map +1 -0
  18. package/dist/api/apps-router.d.ts.map +1 -1
  19. package/dist/api/apps-router.js +14 -2
  20. package/dist/api/apps-router.js.map +1 -1
  21. package/dist/api/gateway-router.d.ts.map +1 -1
  22. package/dist/api/gateway-router.js +2 -3
  23. package/dist/api/gateway-router.js.map +1 -1
  24. package/dist/api/line-webhook-router.d.ts +43 -1
  25. package/dist/api/line-webhook-router.d.ts.map +1 -1
  26. package/dist/api/line-webhook-router.js +286 -38
  27. package/dist/api/line-webhook-router.js.map +1 -1
  28. package/dist/api/router.d.ts +7 -1
  29. package/dist/api/router.d.ts.map +1 -1
  30. package/dist/api/router.js +328 -114
  31. package/dist/api/router.js.map +1 -1
  32. package/dist/apps/agent-manager.d.ts.map +1 -1
  33. package/dist/apps/agent-manager.js +4 -1
  34. package/dist/apps/agent-manager.js.map +1 -1
  35. package/dist/apps/installer.d.ts +75 -2
  36. package/dist/apps/installer.d.ts.map +1 -1
  37. package/dist/apps/installer.js +198 -13
  38. package/dist/apps/installer.js.map +1 -1
  39. package/dist/cli/commands/debug-bundle.d.ts.map +1 -1
  40. package/dist/cli/commands/debug-bundle.js +9 -52
  41. package/dist/cli/commands/debug-bundle.js.map +1 -1
  42. package/dist/cli/commands/gateway.d.ts +6 -1
  43. package/dist/cli/commands/gateway.d.ts.map +1 -1
  44. package/dist/cli/commands/gateway.js +14 -3
  45. package/dist/cli/commands/gateway.js.map +1 -1
  46. package/dist/cli/commands/logs.d.ts +37 -0
  47. package/dist/cli/commands/logs.d.ts.map +1 -0
  48. package/dist/cli/commands/logs.js +356 -0
  49. package/dist/cli/commands/logs.js.map +1 -0
  50. package/dist/cli/index.d.ts.map +1 -1
  51. package/dist/cli/index.js +16 -5
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/cli/logs-dir.d.ts +69 -0
  54. package/dist/cli/logs-dir.d.ts.map +1 -0
  55. package/dist/cli/logs-dir.js +166 -0
  56. package/dist/cli/logs-dir.js.map +1 -0
  57. package/dist/config/agent-env.d.ts +41 -0
  58. package/dist/config/agent-env.d.ts.map +1 -0
  59. package/dist/config/agent-env.js +154 -0
  60. package/dist/config/agent-env.js.map +1 -0
  61. package/dist/config/loader.d.ts +33 -2
  62. package/dist/config/loader.d.ts.map +1 -1
  63. package/dist/config/loader.js +33 -9
  64. package/dist/config/loader.js.map +1 -1
  65. package/dist/config/watcher.d.ts +1 -0
  66. package/dist/config/watcher.d.ts.map +1 -1
  67. package/dist/config/watcher.js +22 -1
  68. package/dist/config/watcher.js.map +1 -1
  69. package/dist/index.js +50 -51
  70. package/dist/index.js.map +1 -1
  71. package/dist/load-dotenv.d.ts +17 -0
  72. package/dist/load-dotenv.d.ts.map +1 -1
  73. package/dist/load-dotenv.js +32 -9
  74. package/dist/load-dotenv.js.map +1 -1
  75. package/dist/logger.d.ts +52 -1
  76. package/dist/logger.d.ts.map +1 -1
  77. package/dist/logger.js +264 -2
  78. package/dist/logger.js.map +1 -1
  79. package/dist/session/store.d.ts +9 -0
  80. package/dist/session/store.d.ts.map +1 -1
  81. package/dist/session/store.js +12 -0
  82. package/dist/session/store.js.map +1 -1
  83. package/dist/shell/bypass-dialog.d.ts +234 -0
  84. package/dist/shell/bypass-dialog.d.ts.map +1 -0
  85. package/dist/shell/bypass-dialog.js +339 -0
  86. package/dist/shell/bypass-dialog.js.map +1 -0
  87. package/dist/shell/claude-pty-shell.js +67 -3
  88. package/dist/shell/claude-pty-shell.js.map +1 -1
  89. package/dist/shell/screen.d.ts +22 -11
  90. package/dist/shell/screen.d.ts.map +1 -1
  91. package/dist/shell/screen.js +50 -32
  92. package/dist/shell/screen.js.map +1 -1
  93. package/dist/types.d.ts +40 -0
  94. package/dist/types.d.ts.map +1 -1
  95. package/dist/utils/config-num.d.ts +22 -0
  96. package/dist/utils/config-num.d.ts.map +1 -0
  97. package/dist/utils/config-num.js +34 -0
  98. package/dist/utils/config-num.js.map +1 -0
  99. package/package.json +1 -1
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Decision logic for accepting Claude Code's "Bypass Permissions mode" dialog.
3
+ *
4
+ * The wrapper always injects --dangerously-skip-permissions, so the resulting
5
+ * confirmation modal is accepted on the operator's behalf. How to accept it
6
+ * depends on the Claude Code build, because the dialog's rendering changed:
7
+ *
8
+ * Claude Code <= 2.1.247 Claude Code >= 2.1.248
9
+ * ❯ 1. No, exit ❯ No, exit
10
+ * 2. Yes, I accept Yes, I accept
11
+ *
12
+ * The old shape is a numbered select — typing the digit picks *and* confirms
13
+ * the row in one keystroke. The new shape has no indexes, so a digit selects
14
+ * nothing at all: the dialog stays up, gets re-detected every
15
+ * DIALOG_ACTION_COOLDOWN_MS, and the session wedges until the watchdog kills
16
+ * it (issue #431). Both shapes start with the caret on "No, exit".
17
+ *
18
+ * Both renderings are still in the wild, so this decides per screen rather
19
+ * than per version — there is no version string to read from inside the PTY
20
+ * anyway, and the screen is the thing that actually has to be driven.
21
+ *
22
+ * SAFETY: choosing "No, exit" terminates Claude Code, which is not
23
+ * recoverable, while leaving the dialog up merely means an operator sees it.
24
+ * So Enter is only ever sent once the caret is *observed* on the accept row,
25
+ * the digit is read off the accept row instead of hard-coded (a future
26
+ * reordering must not send us to "No, exit"), and anything unparseable or
27
+ * ambiguous produces no keystroke at all.
28
+ *
29
+ * This module is the pure detection + decision — kept free of node-pty / screen
30
+ * imports so it is cheap to unit-test in isolation, same pattern as
31
+ * menu-probe.ts's decideProbeAttempt and menu-cancel.ts's decideMenuCancel.
32
+ * ScreenModel.detectDialog() calls isBypassDialogOnScreen() below; see
33
+ * Driver.maybeHandleDialog() in claude-pty-shell.ts for where it is wired in.
34
+ */
35
+ /**
36
+ * The dialog's two option labels. BYPASS_ACCEPT_LABEL is the same string
37
+ * screen.ts lists in TUI_BYPASS_PERMS as a detection marker; it is repeated
38
+ * here rather than imported to keep this module dependency-free, and a unit
39
+ * test asserts the two never drift apart (screen.ts: "all matchers live here
40
+ * so a UI change requires touching exactly one file").
41
+ */
42
+ export declare const BYPASS_ACCEPT_LABEL = "Yes, I accept";
43
+ export declare const BYPASS_DECLINE_LABEL = "No, exit";
44
+ /**
45
+ * The dialog's own confirm affordance, rendered a line or two below the option
46
+ * rows ("Enter to confirm · Esc to cancel"). Only the stable prefix is matched.
47
+ *
48
+ * Required by {@link isBypassDialogOnScreen} for the same reason
49
+ * TUI_REQUEST_TOO_LARGE_DISMISS is required by detectRequestTooLarge(): the
50
+ * labels alone appear in ordinary prose (this very dialog's warning text, a
51
+ * chat reply explaining it, re-injected history quoting it), whereas the
52
+ * footer is the affordance the live overlay renders — and it is exactly the
53
+ * affordance the accepting keystroke relies on, so gating on it keeps
54
+ * detection and the action consistent.
55
+ */
56
+ export declare const BYPASS_CONFIRM_FOOTER = "Enter to confirm";
57
+ /**
58
+ * The dialog's heading — the same string screen.ts lists first in
59
+ * TUI_BYPASS_PERMS, repeated here for the dependency-free reason above, with a
60
+ * unit test asserting the two never drift apart.
61
+ *
62
+ * Required by {@link isBypassDialogOnScreen} so that predicate is the COMPLETE
63
+ * rule rather than half of one split across two files. See the note there.
64
+ */
65
+ export declare const BYPASS_HEADING = "Bypass Permissions mode";
66
+ /**
67
+ * How far apart the dialog's own rows may sit before they stop being one block.
68
+ *
69
+ * The real capture has the two options on adjacent rows and the footer two rows
70
+ * below them (one blank row between). These bounds allow a little repaint slack
71
+ * while still requiring the elements to be visually together: without them the
72
+ * "structure" was only an ordering, so an option row, a second option row 15
73
+ * lines further down and any sentence containing "Enter to confirm" below that
74
+ * satisfied it — on a screen of ordinary conversation (review round 2, M3).
75
+ */
76
+ export declare const BYPASS_MAX_ROW_GAP = 2;
77
+ export declare const BYPASS_MAX_FOOTER_GAP = 3;
78
+ /** Arrow keystrokes used to walk the caret onto the accept row. */
79
+ export declare const BYPASS_KEY_DOWN = "\u001B[B";
80
+ export declare const BYPASS_KEY_UP = "\u001B[A";
81
+ /** Confirms the highlighted row ("Enter to confirm" per the dialog's own footer). */
82
+ export declare const BYPASS_KEY_ENTER = "\r";
83
+ /**
84
+ * Ceiling on keystrokes sent per dialog — arrow moves plus the accepting key
85
+ * itself. A healthy dialog needs at most two (one move, one Enter); the
86
+ * ceiling exists so a dialog that renders but never responds cannot draw
87
+ * *unbounded* key traffic, because those keys are not discarded by an
88
+ * unresponsive TUI — they queue and land in the prompt once it opens.
89
+ *
90
+ * Sized to the startup window rather than to the happy path. maybeHandleDialog()
91
+ * only runs while the shell is not ready, so the caller can make at most
92
+ * STARTUP_TIMEOUT_MS / DIALOG_ACTION_COOLDOWN_MS = 120000/2000 = 60 attempts
93
+ * before the startup timeout ends the process anyway; a test pins that
94
+ * arithmetic against the driver's own constants. A tighter ceiling would go
95
+ * silent partway through that window, so a dialog that merely swallowed its
96
+ * first keystrokes (still rendering, key handler not yet attached) would never
97
+ * be retried and would die at the startup timeout into a respawn loop — the
98
+ * exact #431 symptom. Overspending is cheap by comparison: an unresponsive TUI
99
+ * only ever collects arrows and digits here (Enter is sent solely after the
100
+ * caret is *observed* to have moved, which an unresponsive TUI never does), so
101
+ * the worst case is junk characters in an input draft the driver already
102
+ * clears, never a submitted line.
103
+ *
104
+ * Exhausting the ceiling falls back to 'wait' — the fail-safe visible dialog.
105
+ */
106
+ export declare const BYPASS_MAX_KEYS = 60;
107
+ /**
108
+ * Consecutive rounds with no dialog on screen before the keystroke ceiling is
109
+ * considered spent on a dialog that is gone.
110
+ *
111
+ * Not 1: detection is structural (see isBypassDialogOnScreen), so a mid-repaint
112
+ * frame — the option rows drawn but the footer not yet, or the caret momentarily
113
+ * on neither row — reads as "no dialog" for a single round while the dialog is
114
+ * still very much up. Resetting on one miss would zero the counter mid-dialog —
115
+ * precisely the case the ceiling exists for. Requiring sustained absence costs
116
+ * the next dialog one extra cooldown round at most.
117
+ */
118
+ export declare const BYPASS_RESET_AFTER_MISSES = 2;
119
+ export type BypassDialogAction =
120
+ /** Numbered rendering: type this digit, which selects and confirms in one key. */
121
+ {
122
+ kind: 'digit';
123
+ key: string;
124
+ }
125
+ /**
126
+ * Un-numbered rendering: the caret is on the decline row, so step it onto
127
+ * the accept row. Exactly one step — see decideBypassDialogAction(); a caret
128
+ * anywhere other than the decline row is ambiguous and yields 'wait'.
129
+ */
130
+ | {
131
+ kind: 'move';
132
+ key: string;
133
+ }
134
+ /** Caret is on the accept row — safe to confirm. */
135
+ | {
136
+ kind: 'confirm';
137
+ key: string;
138
+ }
139
+ /** Nothing safe to do this round; `reason` is for the log. */
140
+ | {
141
+ kind: 'wait';
142
+ reason: string;
143
+ };
144
+ /** Per-dialog bookkeeping. Reset once the dialog has left the screen. */
145
+ export interface BypassDialogState {
146
+ /** Keystrokes already sent for the current dialog. */
147
+ keys: number;
148
+ /** Consecutive rounds the dialog was not detected — see noteDialogAbsent(). */
149
+ misses: number;
150
+ }
151
+ /**
152
+ * Record a round in which no dialog was detected, clearing the keystroke count
153
+ * once the dialog has been absent for BYPASS_RESET_AFTER_MISSES consecutive
154
+ * rounds so the next dialog starts with a full allowance. Tolerating a single
155
+ * miss keeps a one-round detection flicker (a repaint catching the dialog
156
+ * half-drawn) from silently refilling the allowance mid-dialog.
157
+ */
158
+ export declare function noteDialogAbsent(state: BypassDialogState): void;
159
+ /** Record a round in which the dialog *was* detected, restarting the miss run. */
160
+ export declare function noteDialogPresent(state: BypassDialogState): void;
161
+ /** Exported only so a unit test can prove the escaping above. */
162
+ export declare function rowPattern(label: string): RegExp;
163
+ /**
164
+ * Is a live, drivable bypass-permissions dialog on this screen?
165
+ *
166
+ * This replaced a positional test — "both marker substrings inside the bottom
167
+ * 20 rows" — which assumed a modal is always anchored to the bottom. This
168
+ * dialog is not: it renders at boot, before there is any conversation to push
169
+ * it down, so on a clean start it sits at the TOP of the screen with the rest
170
+ * blank. Both markers then fall outside the window, detection returns null,
171
+ * nothing is pressed, and the session dies at the 120 s startup timeout and
172
+ * respawns into the same dialog (issue #436). Whether a given boot emitted
173
+ * enough output to push the dialog into the window is what made the wedge look
174
+ * random.
175
+ *
176
+ * The window was never really about position, though — it was a cheap proxy for
177
+ * "this is the live modal, not text that quotes it". Quoted text is a genuine
178
+ * hazard: an agent explaining this dialog, or re-injected conversation history,
179
+ * puts the same characters on the same screen, and a mis-aimed Enter can land on
180
+ * "No, exit" and kill Claude Code unrecoverably. So the proxy is replaced with
181
+ * the thing it was proxying for — structural properties, all position-
182
+ * independent, all read off a verbatim capture of the real dialog:
183
+ *
184
+ * 1. The dialog's heading is somewhere on screen. Cheap substring reject.
185
+ * 2. Both option labels occupy a WHOLE row (findRow's anchored patterns), so
186
+ * prose that mentions them mid-sentence never qualifies.
187
+ * 3. Exactly ONE of those two rows carries the ❯ caret. Zero means the frame
188
+ * is still rendering (or is not a live select); two is a shape we do not
189
+ * understand.
190
+ * 4. The rows and the footer form one BLOCK — the options within
191
+ * BYPASS_MAX_ROW_GAP rows of each other, the footer within
192
+ * BYPASS_MAX_FOOTER_GAP below them. Without this the three elements only
193
+ * had to appear in order, so an option row, an unrelated option row 15
194
+ * lines later and a sentence containing "Enter to confirm" further down
195
+ * still qualified (review round 2, finding M3).
196
+ * 5. The dialog's own confirm footer renders BELOW the option rows.
197
+ * 6. Nothing but blank rows renders below that footer.
198
+ *
199
+ * (6) is the load-bearing one, and it is what the old bottom-region test was
200
+ * actually encoding: a live modal owns the screen. Quoted text never can — the
201
+ * input box, its border and the status bar always render underneath it — so
202
+ * scrollback, a pasted dialog, and re-injected history all fail (6) no matter
203
+ * where on the screen they land.
204
+ *
205
+ * (6) is deliberately ZERO-tolerance: not even a box border may follow the
206
+ * footer. A future Claude Code that draws this dialog inside a box, or paints a
207
+ * status line beneath it, would therefore stop being detected. That is the
208
+ * intended direction of the trade — a miss is fail-safe (the operator sees the
209
+ * dialog, and maybeHandleDialog() now logs a warning naming this exact case),
210
+ * whereas relaxing (6) to tolerate border rows would admit a quoted dialog
211
+ * sitting above an EMPTY input box, whose rows are themselves nothing but
212
+ * border characters. Given "No, exit" is unrecoverable and a visible dialog is
213
+ * not, a miss beats a false accept (review round 2, finding M2 — accepted as
214
+ * accurate, resolved this way rather than by relaxing the rule).
215
+ *
216
+ * Fail-safe in the same direction as before: anything unrecognised reads as "no
217
+ * dialog", which means no keystroke and an operator who simply sees the dialog.
218
+ */
219
+ export declare function isBypassDialogOnScreen(screenText: string): boolean;
220
+ /**
221
+ * Decide the single keystroke to send at a bypass-permissions dialog, given
222
+ * the screen text the dialog was detected on.
223
+ *
224
+ * Callers send at most one key per call and re-read the screen before the
225
+ * next one (the DIALOG_ACTION_COOLDOWN_MS re-entry in maybeHandleDialog()),
226
+ * so multi-step acceptance is a screen-driven state machine rather than a
227
+ * blind key sequence: every keystroke is justified by what is on screen at
228
+ * the moment it is sent.
229
+ *
230
+ * `state.keys` is only read here; the caller advances it for every action that
231
+ * actually sends a key.
232
+ */
233
+ export declare function decideBypassDialogAction(dialogText: string, state: BypassDialogState): BypassDialogAction;
234
+ //# sourceMappingURL=bypass-dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bypass-dialog.d.ts","sourceRoot":"","sources":["../../src/shell/bypass-dialog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,4BAA4B,CAAC;AAExD;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,mEAAmE;AACnE,eAAO,MAAM,eAAe,aAAW,CAAC;AACxC,eAAO,MAAM,aAAa,aAAW,CAAC;AACtC,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,MAAM,MAAM,kBAAkB;AAC5B,kFAAkF;AAChF;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AAChC;;;;GAIG;GACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AAC/B,oDAAoD;GAClD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AAClC,8DAA8D;GAC5D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAG/D;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAEhE;AAmCD,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhD;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAuBlE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,iBAAiB,GACvB,kBAAkB,CA4DpB"}
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ /**
3
+ * Decision logic for accepting Claude Code's "Bypass Permissions mode" dialog.
4
+ *
5
+ * The wrapper always injects --dangerously-skip-permissions, so the resulting
6
+ * confirmation modal is accepted on the operator's behalf. How to accept it
7
+ * depends on the Claude Code build, because the dialog's rendering changed:
8
+ *
9
+ * Claude Code <= 2.1.247 Claude Code >= 2.1.248
10
+ * ❯ 1. No, exit ❯ No, exit
11
+ * 2. Yes, I accept Yes, I accept
12
+ *
13
+ * The old shape is a numbered select — typing the digit picks *and* confirms
14
+ * the row in one keystroke. The new shape has no indexes, so a digit selects
15
+ * nothing at all: the dialog stays up, gets re-detected every
16
+ * DIALOG_ACTION_COOLDOWN_MS, and the session wedges until the watchdog kills
17
+ * it (issue #431). Both shapes start with the caret on "No, exit".
18
+ *
19
+ * Both renderings are still in the wild, so this decides per screen rather
20
+ * than per version — there is no version string to read from inside the PTY
21
+ * anyway, and the screen is the thing that actually has to be driven.
22
+ *
23
+ * SAFETY: choosing "No, exit" terminates Claude Code, which is not
24
+ * recoverable, while leaving the dialog up merely means an operator sees it.
25
+ * So Enter is only ever sent once the caret is *observed* on the accept row,
26
+ * the digit is read off the accept row instead of hard-coded (a future
27
+ * reordering must not send us to "No, exit"), and anything unparseable or
28
+ * ambiguous produces no keystroke at all.
29
+ *
30
+ * This module is the pure detection + decision — kept free of node-pty / screen
31
+ * imports so it is cheap to unit-test in isolation, same pattern as
32
+ * menu-probe.ts's decideProbeAttempt and menu-cancel.ts's decideMenuCancel.
33
+ * ScreenModel.detectDialog() calls isBypassDialogOnScreen() below; see
34
+ * Driver.maybeHandleDialog() in claude-pty-shell.ts for where it is wired in.
35
+ */
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.BYPASS_RESET_AFTER_MISSES = exports.BYPASS_MAX_KEYS = exports.BYPASS_KEY_ENTER = exports.BYPASS_KEY_UP = exports.BYPASS_KEY_DOWN = exports.BYPASS_MAX_FOOTER_GAP = exports.BYPASS_MAX_ROW_GAP = exports.BYPASS_HEADING = exports.BYPASS_CONFIRM_FOOTER = exports.BYPASS_DECLINE_LABEL = exports.BYPASS_ACCEPT_LABEL = void 0;
38
+ exports.noteDialogAbsent = noteDialogAbsent;
39
+ exports.noteDialogPresent = noteDialogPresent;
40
+ exports.rowPattern = rowPattern;
41
+ exports.isBypassDialogOnScreen = isBypassDialogOnScreen;
42
+ exports.decideBypassDialogAction = decideBypassDialogAction;
43
+ /**
44
+ * The dialog's two option labels. BYPASS_ACCEPT_LABEL is the same string
45
+ * screen.ts lists in TUI_BYPASS_PERMS as a detection marker; it is repeated
46
+ * here rather than imported to keep this module dependency-free, and a unit
47
+ * test asserts the two never drift apart (screen.ts: "all matchers live here
48
+ * so a UI change requires touching exactly one file").
49
+ */
50
+ exports.BYPASS_ACCEPT_LABEL = 'Yes, I accept';
51
+ exports.BYPASS_DECLINE_LABEL = 'No, exit';
52
+ /**
53
+ * The dialog's own confirm affordance, rendered a line or two below the option
54
+ * rows ("Enter to confirm · Esc to cancel"). Only the stable prefix is matched.
55
+ *
56
+ * Required by {@link isBypassDialogOnScreen} for the same reason
57
+ * TUI_REQUEST_TOO_LARGE_DISMISS is required by detectRequestTooLarge(): the
58
+ * labels alone appear in ordinary prose (this very dialog's warning text, a
59
+ * chat reply explaining it, re-injected history quoting it), whereas the
60
+ * footer is the affordance the live overlay renders — and it is exactly the
61
+ * affordance the accepting keystroke relies on, so gating on it keeps
62
+ * detection and the action consistent.
63
+ */
64
+ exports.BYPASS_CONFIRM_FOOTER = 'Enter to confirm';
65
+ /**
66
+ * The dialog's heading — the same string screen.ts lists first in
67
+ * TUI_BYPASS_PERMS, repeated here for the dependency-free reason above, with a
68
+ * unit test asserting the two never drift apart.
69
+ *
70
+ * Required by {@link isBypassDialogOnScreen} so that predicate is the COMPLETE
71
+ * rule rather than half of one split across two files. See the note there.
72
+ */
73
+ exports.BYPASS_HEADING = 'Bypass Permissions mode';
74
+ /**
75
+ * How far apart the dialog's own rows may sit before they stop being one block.
76
+ *
77
+ * The real capture has the two options on adjacent rows and the footer two rows
78
+ * below them (one blank row between). These bounds allow a little repaint slack
79
+ * while still requiring the elements to be visually together: without them the
80
+ * "structure" was only an ordering, so an option row, a second option row 15
81
+ * lines further down and any sentence containing "Enter to confirm" below that
82
+ * satisfied it — on a screen of ordinary conversation (review round 2, M3).
83
+ */
84
+ exports.BYPASS_MAX_ROW_GAP = 2;
85
+ exports.BYPASS_MAX_FOOTER_GAP = 3;
86
+ /** Arrow keystrokes used to walk the caret onto the accept row. */
87
+ exports.BYPASS_KEY_DOWN = '\x1b[B';
88
+ exports.BYPASS_KEY_UP = '\x1b[A';
89
+ /** Confirms the highlighted row ("Enter to confirm" per the dialog's own footer). */
90
+ exports.BYPASS_KEY_ENTER = '\r';
91
+ /**
92
+ * Ceiling on keystrokes sent per dialog — arrow moves plus the accepting key
93
+ * itself. A healthy dialog needs at most two (one move, one Enter); the
94
+ * ceiling exists so a dialog that renders but never responds cannot draw
95
+ * *unbounded* key traffic, because those keys are not discarded by an
96
+ * unresponsive TUI — they queue and land in the prompt once it opens.
97
+ *
98
+ * Sized to the startup window rather than to the happy path. maybeHandleDialog()
99
+ * only runs while the shell is not ready, so the caller can make at most
100
+ * STARTUP_TIMEOUT_MS / DIALOG_ACTION_COOLDOWN_MS = 120000/2000 = 60 attempts
101
+ * before the startup timeout ends the process anyway; a test pins that
102
+ * arithmetic against the driver's own constants. A tighter ceiling would go
103
+ * silent partway through that window, so a dialog that merely swallowed its
104
+ * first keystrokes (still rendering, key handler not yet attached) would never
105
+ * be retried and would die at the startup timeout into a respawn loop — the
106
+ * exact #431 symptom. Overspending is cheap by comparison: an unresponsive TUI
107
+ * only ever collects arrows and digits here (Enter is sent solely after the
108
+ * caret is *observed* to have moved, which an unresponsive TUI never does), so
109
+ * the worst case is junk characters in an input draft the driver already
110
+ * clears, never a submitted line.
111
+ *
112
+ * Exhausting the ceiling falls back to 'wait' — the fail-safe visible dialog.
113
+ */
114
+ exports.BYPASS_MAX_KEYS = 60;
115
+ /**
116
+ * Consecutive rounds with no dialog on screen before the keystroke ceiling is
117
+ * considered spent on a dialog that is gone.
118
+ *
119
+ * Not 1: detection is structural (see isBypassDialogOnScreen), so a mid-repaint
120
+ * frame — the option rows drawn but the footer not yet, or the caret momentarily
121
+ * on neither row — reads as "no dialog" for a single round while the dialog is
122
+ * still very much up. Resetting on one miss would zero the counter mid-dialog —
123
+ * precisely the case the ceiling exists for. Requiring sustained absence costs
124
+ * the next dialog one extra cooldown round at most.
125
+ */
126
+ exports.BYPASS_RESET_AFTER_MISSES = 2;
127
+ /**
128
+ * Record a round in which no dialog was detected, clearing the keystroke count
129
+ * once the dialog has been absent for BYPASS_RESET_AFTER_MISSES consecutive
130
+ * rounds so the next dialog starts with a full allowance. Tolerating a single
131
+ * miss keeps a one-round detection flicker (a repaint catching the dialog
132
+ * half-drawn) from silently refilling the allowance mid-dialog.
133
+ */
134
+ function noteDialogAbsent(state) {
135
+ state.misses++;
136
+ if (state.misses >= exports.BYPASS_RESET_AFTER_MISSES)
137
+ state.keys = 0;
138
+ }
139
+ /** Record a round in which the dialog *was* detected, restarting the miss run. */
140
+ function noteDialogPresent(state) {
141
+ state.misses = 0;
142
+ }
143
+ /**
144
+ * Match an option row: optional box border, optional ❯ caret, optional `N.`
145
+ * index, then the label and nothing else.
146
+ *
147
+ * Anchored at both ends on purpose. The dialog's own warning prose mentions
148
+ * accepting responsibility and Bypass Permissions mode, and a chat reply can
149
+ * quote the labels outright; requiring the label to be the *whole* row keeps
150
+ * those from being mistaken for a selectable option.
151
+ *
152
+ * The caret is U+276F only — never the ASCII '>' that TUI_MENU_OPTION_RE
153
+ * tolerates for row content — because the real TUI never renders '>' as a
154
+ * caret while prose (markdown blockquotes) renders it constantly.
155
+ *
156
+ * The label is escaped before interpolation. These patterns are built at module
157
+ * load, and the labels above are meant to be edited when the TUI changes: an
158
+ * unescaped label containing '(' would throw SyntaxError at import time and take
159
+ * every PTY session down with it, and a '.' would silently become a wildcard
160
+ * that widens what counts as a selectable row.
161
+ */
162
+ function escapeForRegExp(literal) {
163
+ return literal.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
164
+ }
165
+ /** Exported only so a unit test can prove the escaping above. */
166
+ function rowPattern(label) {
167
+ return new RegExp(`^[\\s│]*(❯)?\\s*(?:(\\d+)\\.)?\\s*${escapeForRegExp(label)}[\\s│]*$`);
168
+ }
169
+ const ACCEPT_ROW_RE = rowPattern(exports.BYPASS_ACCEPT_LABEL);
170
+ const DECLINE_ROW_RE = rowPattern(exports.BYPASS_DECLINE_LABEL);
171
+ /**
172
+ * Find the option row nearest the bottom of the screen. A live modal is the
173
+ * bottom-most thing on screen, so scanning upward from the end means quoted
174
+ * scrollback above it can never be the row we act on.
175
+ */
176
+ function findRow(lines, re) {
177
+ for (let i = lines.length - 1; i >= 0; i--) {
178
+ const m = re.exec(lines[i]);
179
+ if (m)
180
+ return { line: i, caret: m[1] === '❯', digit: m[2] ? Number(m[2]) : null };
181
+ }
182
+ return null;
183
+ }
184
+ /**
185
+ * Is a live, drivable bypass-permissions dialog on this screen?
186
+ *
187
+ * This replaced a positional test — "both marker substrings inside the bottom
188
+ * 20 rows" — which assumed a modal is always anchored to the bottom. This
189
+ * dialog is not: it renders at boot, before there is any conversation to push
190
+ * it down, so on a clean start it sits at the TOP of the screen with the rest
191
+ * blank. Both markers then fall outside the window, detection returns null,
192
+ * nothing is pressed, and the session dies at the 120 s startup timeout and
193
+ * respawns into the same dialog (issue #436). Whether a given boot emitted
194
+ * enough output to push the dialog into the window is what made the wedge look
195
+ * random.
196
+ *
197
+ * The window was never really about position, though — it was a cheap proxy for
198
+ * "this is the live modal, not text that quotes it". Quoted text is a genuine
199
+ * hazard: an agent explaining this dialog, or re-injected conversation history,
200
+ * puts the same characters on the same screen, and a mis-aimed Enter can land on
201
+ * "No, exit" and kill Claude Code unrecoverably. So the proxy is replaced with
202
+ * the thing it was proxying for — structural properties, all position-
203
+ * independent, all read off a verbatim capture of the real dialog:
204
+ *
205
+ * 1. The dialog's heading is somewhere on screen. Cheap substring reject.
206
+ * 2. Both option labels occupy a WHOLE row (findRow's anchored patterns), so
207
+ * prose that mentions them mid-sentence never qualifies.
208
+ * 3. Exactly ONE of those two rows carries the ❯ caret. Zero means the frame
209
+ * is still rendering (or is not a live select); two is a shape we do not
210
+ * understand.
211
+ * 4. The rows and the footer form one BLOCK — the options within
212
+ * BYPASS_MAX_ROW_GAP rows of each other, the footer within
213
+ * BYPASS_MAX_FOOTER_GAP below them. Without this the three elements only
214
+ * had to appear in order, so an option row, an unrelated option row 15
215
+ * lines later and a sentence containing "Enter to confirm" further down
216
+ * still qualified (review round 2, finding M3).
217
+ * 5. The dialog's own confirm footer renders BELOW the option rows.
218
+ * 6. Nothing but blank rows renders below that footer.
219
+ *
220
+ * (6) is the load-bearing one, and it is what the old bottom-region test was
221
+ * actually encoding: a live modal owns the screen. Quoted text never can — the
222
+ * input box, its border and the status bar always render underneath it — so
223
+ * scrollback, a pasted dialog, and re-injected history all fail (6) no matter
224
+ * where on the screen they land.
225
+ *
226
+ * (6) is deliberately ZERO-tolerance: not even a box border may follow the
227
+ * footer. A future Claude Code that draws this dialog inside a box, or paints a
228
+ * status line beneath it, would therefore stop being detected. That is the
229
+ * intended direction of the trade — a miss is fail-safe (the operator sees the
230
+ * dialog, and maybeHandleDialog() now logs a warning naming this exact case),
231
+ * whereas relaxing (6) to tolerate border rows would admit a quoted dialog
232
+ * sitting above an EMPTY input box, whose rows are themselves nothing but
233
+ * border characters. Given "No, exit" is unrecoverable and a visible dialog is
234
+ * not, a miss beats a false accept (review round 2, finding M2 — accepted as
235
+ * accurate, resolved this way rather than by relaxing the rule).
236
+ *
237
+ * Fail-safe in the same direction as before: anything unrecognised reads as "no
238
+ * dialog", which means no keystroke and an operator who simply sees the dialog.
239
+ */
240
+ function isBypassDialogOnScreen(screenText) {
241
+ // The heading gate lives HERE rather than in ScreenModel.detectDialog() so
242
+ // that this predicate is the whole rule. When detectDialog() held it, the
243
+ // action layer re-applied only the structural half, and a screen carrying a
244
+ // perfect dialog shape without the heading produced a live Enter if
245
+ // decideBypassDialogAction() was ever called from anywhere else — the exact
246
+ // drift the two-layer design claimed to prevent (review round 2, finding H1).
247
+ if (!screenText.includes(exports.BYPASS_HEADING))
248
+ return false;
249
+ const lines = screenText.split('\n');
250
+ const accept = findRow(lines, ACCEPT_ROW_RE);
251
+ const decline = findRow(lines, DECLINE_ROW_RE);
252
+ if (!accept || !decline)
253
+ return false;
254
+ // Exactly one caret: equal flags mean either none (still rendering) or both
255
+ // (unrecognised shape). Same rule decideBypassDialogAction() acts on.
256
+ if (accept.caret === decline.caret)
257
+ return false;
258
+ if (Math.abs(accept.line - decline.line) > exports.BYPASS_MAX_ROW_GAP)
259
+ return false;
260
+ const lastOptionRow = Math.max(accept.line, decline.line);
261
+ const footer = lines.findIndex((l, i) => i > lastOptionRow && l.includes(exports.BYPASS_CONFIRM_FOOTER));
262
+ if (footer === -1)
263
+ return false;
264
+ if (footer - lastOptionRow > exports.BYPASS_MAX_FOOTER_GAP)
265
+ return false;
266
+ return lines.slice(footer + 1).every((l) => l.trim() === '');
267
+ }
268
+ /**
269
+ * Decide the single keystroke to send at a bypass-permissions dialog, given
270
+ * the screen text the dialog was detected on.
271
+ *
272
+ * Callers send at most one key per call and re-read the screen before the
273
+ * next one (the DIALOG_ACTION_COOLDOWN_MS re-entry in maybeHandleDialog()),
274
+ * so multi-step acceptance is a screen-driven state machine rather than a
275
+ * blind key sequence: every keystroke is justified by what is on screen at
276
+ * the moment it is sent.
277
+ *
278
+ * `state.keys` is only read here; the caller advances it for every action that
279
+ * actually sends a key.
280
+ */
281
+ function decideBypassDialogAction(dialogText, state) {
282
+ // Budget applies to every keystroke, not just the arrows: a dialog still on
283
+ // screen after this many keys is not responding to us, and the fix for #431
284
+ // is precisely that repeating a key at a dialog that ignores it is worse
285
+ // than doing nothing.
286
+ if (state.keys >= exports.BYPASS_MAX_KEYS) {
287
+ return { kind: 'wait', reason: `keystroke budget exhausted after ${state.keys} keys` };
288
+ }
289
+ // The caller only gets here after detectDialog() fired, and both now read the
290
+ // whole screen — so "act only on what was detected" can no longer be enforced
291
+ // by handing this function a narrow region. It is enforced by applying the
292
+ // same structural rule here instead, which also keeps the two layers from
293
+ // drifting apart if either is edited alone.
294
+ if (!isBypassDialogOnScreen(dialogText)) {
295
+ return { kind: 'wait', reason: 'no live bypass dialog on screen' };
296
+ }
297
+ // The checks below are reached only for a screen that already satisfies that
298
+ // rule, so several are belt-and-braces. They are kept deliberately: each one
299
+ // states an invariant this decision depends on, and a keystroke sent on a
300
+ // wrong assumption here can pick "No, exit" and end the session for good.
301
+ const lines = dialogText.split('\n');
302
+ const accept = findRow(lines, ACCEPT_ROW_RE);
303
+ if (!accept)
304
+ return { kind: 'wait', reason: 'accept row not parseable on screen' };
305
+ // Numbered rendering (<= 2.1.247): the digit selects and confirms in one
306
+ // keystroke, the same key the pre-#431 code sent — but read off the accept
307
+ // row rather than hard-coded, so a reordered dialog cannot make us type the
308
+ // digit that means "No, exit". A wider index is not one keystroke, and how
309
+ // such a dialog responds to arrows has never been observed, so it stops here
310
+ // rather than guessing its way toward Enter.
311
+ if (accept.digit !== null) {
312
+ if (accept.digit >= 1 && accept.digit <= 9) {
313
+ return { kind: 'digit', key: String(accept.digit) };
314
+ }
315
+ return { kind: 'wait', reason: `accept row index ${accept.digit} is not a single keystroke` };
316
+ }
317
+ // Un-numbered rendering (>= 2.1.248): navigate, then confirm.
318
+ const decline = findRow(lines, DECLINE_ROW_RE);
319
+ if (accept.caret && !decline?.caret)
320
+ return { kind: 'confirm', key: exports.BYPASS_KEY_ENTER };
321
+ // Not on the accept row. Only move when the caret is unambiguously on the
322
+ // decline row: a screen with no caret at all is still rendering (or is not
323
+ // the live dialog), and one with a caret on both rows is not a shape we
324
+ // understand. Guessing either way risks confirming "No, exit".
325
+ //
326
+ // So this is a single step between two known rows, not a search: on a dialog
327
+ // whose caret sits on some third row, every round yields 'wait' and the
328
+ // operator sees the dialog. Both observed renderings have exactly these two
329
+ // options, and stepping blindly toward a row we cannot see the caret on is
330
+ // how Enter ends up on "No, exit".
331
+ if (!decline?.caret || accept.caret) {
332
+ return { kind: 'wait', reason: 'no unambiguous caret on the dialog rows' };
333
+ }
334
+ return {
335
+ kind: 'move',
336
+ key: accept.line > decline.line ? exports.BYPASS_KEY_DOWN : exports.BYPASS_KEY_UP,
337
+ };
338
+ }
339
+ //# sourceMappingURL=bypass-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bypass-dialog.js","sourceRoot":"","sources":["../../src/shell/bypass-dialog.ts"],"names":[],"mappings":"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"}