@namzu/cli 14.3.0 → 15.1.0

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 (149) hide show
  1. package/README.md +117 -17
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +26 -26
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/acp.d.ts.map +1 -1
  6. package/dist/commands/acp.js +3 -2
  7. package/dist/commands/acp.js.map +1 -1
  8. package/dist/commands/doctor.d.ts.map +1 -1
  9. package/dist/commands/doctor.js +2 -0
  10. package/dist/commands/doctor.js.map +1 -1
  11. package/dist/commands/login.d.ts +2 -0
  12. package/dist/commands/login.d.ts.map +1 -1
  13. package/dist/commands/login.js +45 -7
  14. package/dist/commands/login.js.map +1 -1
  15. package/dist/commands/run.d.ts.map +1 -1
  16. package/dist/commands/run.js +36 -12
  17. package/dist/commands/run.js.map +1 -1
  18. package/dist/commands/{stubs.d.ts → serve.d.ts} +1 -10
  19. package/dist/commands/serve.d.ts.map +1 -0
  20. package/dist/commands/{stubs.js → serve.js} +1 -24
  21. package/dist/commands/serve.js.map +1 -0
  22. package/dist/commands/skills.d.ts +19 -0
  23. package/dist/commands/skills.d.ts.map +1 -0
  24. package/dist/commands/skills.js +107 -0
  25. package/dist/commands/skills.js.map +1 -0
  26. package/dist/integrations/mcp/servers.d.ts +7 -0
  27. package/dist/integrations/mcp/servers.d.ts.map +1 -1
  28. package/dist/integrations/mcp/servers.js +33 -8
  29. package/dist/integrations/mcp/servers.js.map +1 -1
  30. package/dist/integrations/sessions/store.d.ts +8 -0
  31. package/dist/integrations/sessions/store.d.ts.map +1 -1
  32. package/dist/integrations/sessions/store.js +15 -0
  33. package/dist/integrations/sessions/store.js.map +1 -1
  34. package/dist/integrations/subagents/runtime.d.ts +2 -0
  35. package/dist/integrations/subagents/runtime.d.ts.map +1 -1
  36. package/dist/integrations/subagents/runtime.js +86 -13
  37. package/dist/integrations/subagents/runtime.js.map +1 -1
  38. package/dist/integrations/updates.d.ts.map +1 -1
  39. package/dist/integrations/updates.js +29 -4
  40. package/dist/integrations/updates.js.map +1 -1
  41. package/dist/tui/App.d.ts +10 -1
  42. package/dist/tui/App.d.ts.map +1 -1
  43. package/dist/tui/App.js +1039 -252
  44. package/dist/tui/App.js.map +1 -1
  45. package/dist/tui/ChoicePicker.d.ts +2 -1
  46. package/dist/tui/ChoicePicker.d.ts.map +1 -1
  47. package/dist/tui/ChoicePicker.js +10 -6
  48. package/dist/tui/ChoicePicker.js.map +1 -1
  49. package/dist/tui/Composer.d.ts +19 -2
  50. package/dist/tui/Composer.d.ts.map +1 -1
  51. package/dist/tui/Composer.js +526 -70
  52. package/dist/tui/Composer.js.map +1 -1
  53. package/dist/tui/CopyPicker.d.ts.map +1 -1
  54. package/dist/tui/CopyPicker.js +3 -4
  55. package/dist/tui/CopyPicker.js.map +1 -1
  56. package/dist/tui/EditPromptPicker.d.ts.map +1 -1
  57. package/dist/tui/EditPromptPicker.js +3 -4
  58. package/dist/tui/EditPromptPicker.js.map +1 -1
  59. package/dist/tui/Markdown.d.ts +3 -1
  60. package/dist/tui/Markdown.d.ts.map +1 -1
  61. package/dist/tui/Markdown.js +11 -9
  62. package/dist/tui/Markdown.js.map +1 -1
  63. package/dist/tui/PermissionOverlay.d.ts +11 -6
  64. package/dist/tui/PermissionOverlay.d.ts.map +1 -1
  65. package/dist/tui/PermissionOverlay.js +13 -20
  66. package/dist/tui/PermissionOverlay.js.map +1 -1
  67. package/dist/tui/Picker.d.ts.map +1 -1
  68. package/dist/tui/Picker.js +47 -11
  69. package/dist/tui/Picker.js.map +1 -1
  70. package/dist/tui/ResumePicker.d.ts.map +1 -1
  71. package/dist/tui/ResumePicker.js +6 -1
  72. package/dist/tui/ResumePicker.js.map +1 -1
  73. package/dist/tui/StatusBar.js +3 -3
  74. package/dist/tui/StatusBar.js.map +1 -1
  75. package/dist/tui/TextPrompt.d.ts +21 -0
  76. package/dist/tui/TextPrompt.d.ts.map +1 -0
  77. package/dist/tui/TextPrompt.js +139 -0
  78. package/dist/tui/TextPrompt.js.map +1 -0
  79. package/dist/tui/Transcript.d.ts +9 -10
  80. package/dist/tui/Transcript.d.ts.map +1 -1
  81. package/dist/tui/Transcript.js +11 -12
  82. package/dist/tui/Transcript.js.map +1 -1
  83. package/dist/tui/agent.d.ts +57 -23
  84. package/dist/tui/agent.d.ts.map +1 -1
  85. package/dist/tui/agent.js +136 -86
  86. package/dist/tui/agent.js.map +1 -1
  87. package/dist/tui/exit-summary.d.ts.map +1 -1
  88. package/dist/tui/exit-summary.js +2 -1
  89. package/dist/tui/exit-summary.js.map +1 -1
  90. package/dist/tui/external-editor.d.ts +25 -0
  91. package/dist/tui/external-editor.d.ts.map +1 -0
  92. package/dist/tui/external-editor.js +169 -0
  93. package/dist/tui/external-editor.js.map +1 -0
  94. package/dist/tui/index.d.ts.map +1 -1
  95. package/dist/tui/index.js +4 -0
  96. package/dist/tui/index.js.map +1 -1
  97. package/dist/tui/live-window.d.ts +17 -11
  98. package/dist/tui/live-window.d.ts.map +1 -1
  99. package/dist/tui/live-window.js +64 -20
  100. package/dist/tui/live-window.js.map +1 -1
  101. package/dist/tui/login-prompt.d.ts +2 -0
  102. package/dist/tui/login-prompt.d.ts.map +1 -1
  103. package/dist/tui/login-prompt.js +8 -1
  104. package/dist/tui/login-prompt.js.map +1 -1
  105. package/dist/tui/mentions.d.ts +19 -0
  106. package/dist/tui/mentions.d.ts.map +1 -1
  107. package/dist/tui/mentions.js +96 -5
  108. package/dist/tui/mentions.js.map +1 -1
  109. package/dist/tui/permission-review.d.ts +59 -0
  110. package/dist/tui/permission-review.d.ts.map +1 -0
  111. package/dist/tui/permission-review.js +219 -0
  112. package/dist/tui/permission-review.js.map +1 -0
  113. package/dist/tui/run-interruption.d.ts +14 -0
  114. package/dist/tui/run-interruption.d.ts.map +1 -0
  115. package/dist/tui/run-interruption.js +67 -0
  116. package/dist/tui/run-interruption.js.map +1 -0
  117. package/dist/tui/selection-window.d.ts +18 -0
  118. package/dist/tui/selection-window.d.ts.map +1 -0
  119. package/dist/tui/selection-window.js +37 -0
  120. package/dist/tui/selection-window.js.map +1 -0
  121. package/dist/tui/slashCommands.d.ts +48 -8
  122. package/dist/tui/slashCommands.d.ts.map +1 -1
  123. package/dist/tui/slashCommands.js +129 -47
  124. package/dist/tui/slashCommands.js.map +1 -1
  125. package/dist/tui/terminal-hyperlinks.d.ts +20 -0
  126. package/dist/tui/terminal-hyperlinks.d.ts.map +1 -0
  127. package/dist/tui/terminal-hyperlinks.js +70 -0
  128. package/dist/tui/terminal-hyperlinks.js.map +1 -0
  129. package/dist/tui/types.d.ts +2 -0
  130. package/dist/tui/types.d.ts.map +1 -1
  131. package/dist/tui/use-selection-index.d.ts +8 -0
  132. package/dist/tui/use-selection-index.d.ts.map +1 -0
  133. package/dist/tui/use-selection-index.js +13 -0
  134. package/dist/tui/use-selection-index.js.map +1 -0
  135. package/dist/tui/workspace-review.d.ts +15 -0
  136. package/dist/tui/workspace-review.d.ts.map +1 -0
  137. package/dist/tui/workspace-review.js +67 -0
  138. package/dist/tui/workspace-review.js.map +1 -0
  139. package/dist/version.d.ts +10 -0
  140. package/dist/version.d.ts.map +1 -0
  141. package/dist/version.js +23 -0
  142. package/dist/version.js.map +1 -0
  143. package/package.json +8 -7
  144. package/dist/commands/stubs.d.ts.map +0 -1
  145. package/dist/commands/stubs.js.map +0 -1
  146. package/dist/tui/bottom-spacer.d.ts +0 -95
  147. package/dist/tui/bottom-spacer.d.ts.map +0 -1
  148. package/dist/tui/bottom-spacer.js +0 -83
  149. package/dist/tui/bottom-spacer.js.map +0 -1
@@ -41,8 +41,9 @@
41
41
  * row that has settled never comes back.
42
42
  */
43
43
  import { renderedDetailLines } from './Transcript.js';
44
- import { SAFETY_ROWS, estimateRenderedLines } from './bottom-spacer.js';
45
44
  import { terminalDisplayText } from './terminal-display.js';
45
+ /** Rows kept clear between the redrawable tail and the terminal boundary. */
46
+ export const LIVE_WINDOW_SAFETY_ROWS = 6;
46
47
  /**
47
48
  * The most rows the window will ever hold, whatever the terminal's height.
48
49
  *
@@ -81,43 +82,86 @@ function messageLines(message, hasPrev, raw) {
81
82
  ];
82
83
  }
83
84
  /**
84
- * Every line the finalized transcript will print, for a height estimate.
85
+ * Every line the finalized transcript will print, for the live-window height
86
+ * estimate and terminal-safety observers.
85
87
  *
86
- * Exported so the wiring is testable rather than only typecheckable. The
87
- * spacer's own docblock states the asymmetry it depends on: over-count the
88
- * content and the composer merely floats, under-count it and the composer is
89
- * pushed off the bottom. The caller used to pass each row's `content` alone,
90
- * which counted a six-line collapsed tool body as nothing at all — the estimate
91
- * ran low by six per tool call, in the direction that costs the usability
92
- * rather than the feature. `/expand` makes it acute: a row whose entire
93
- * substance is a two-hundred-line body would have been handed over as one line.
88
+ * Exported so the wiring is testable rather than only typecheckable. The caller
89
+ * once counted each row's `content` alone, which made a six-line collapsed tool
90
+ * body look like one line. `/expand` makes that acute: a row whose substance is
91
+ * a two-hundred-line body must not be admitted to a small redrawable viewport.
94
92
  *
95
93
  * A pending row is excluded because it is not in the static log yet; the
96
- * spacer's `liveRows` covers the live region.
94
+ * pending content is already part of the live region outside this window.
97
95
  */
98
96
  export function transcriptLines(messages, raw = false) {
99
97
  const finalized = messages.filter((m) => !m.pending);
100
98
  return finalized.flatMap((message, i) => messageLines(message, i > 0, raw));
101
99
  }
100
+ /**
101
+ * Conservative terminal-cell width.
102
+ *
103
+ * ASCII is one cell. Every printable non-ASCII code point is charged two: that
104
+ * deliberately over-counts combining marks and narrow scripts, but never makes
105
+ * a CJK glyph one cell or relies on UTF-16 string length for terminal geometry.
106
+ * Over-counting settles a row earlier; under-counting can trigger Ink's
107
+ * whole-scrollback repaint path.
108
+ */
109
+ function terminalCellWidth(value) {
110
+ let cells = 0;
111
+ for (const char of value) {
112
+ const code = char.codePointAt(0) ?? 0;
113
+ if (char === '\t')
114
+ cells += 4;
115
+ else if (code >= 0x20 && code !== 0x7f)
116
+ cells += code <= 0x7e ? 1 : 2;
117
+ }
118
+ return cells;
119
+ }
120
+ /**
121
+ * A deliberate over-count of how many terminal rows source lines occupy.
122
+ * Unknown width falls back to a narrow terminal; each source line occupies at
123
+ * least one row, and wrapping is measured in terminal cells rather than code
124
+ * units.
125
+ */
126
+ export function estimateRenderedLines(transcript, columns) {
127
+ const width = Math.max(20, columns ?? 80);
128
+ let total = 0;
129
+ for (const entry of transcript) {
130
+ for (const line of entry.split('\n')) {
131
+ total += Math.max(1, Math.ceil(terminalCellWidth(line) / width));
132
+ }
133
+ }
134
+ return total;
135
+ }
102
136
  /**
103
137
  * Rows added to each windowed row's estimate.
104
138
  *
105
- * `estimateRenderedLines` is deliberately an UNDER-count, because for the
106
- * bottom spacer counting low leaves more apparent room and the safety margin
107
- * absorbs the difference. Here the asymmetry runs the other way: under-count a
108
- * live row and the window takes more of the viewport than it was budgeted, and
109
- * the price of exceeding the viewport is the whole-session repaint this window
110
- * exists to stay clear of. Over-count and the window merely holds one row
111
- * fewer.
139
+ * Under-count a live row and the window takes more of the viewport than it was
140
+ * budgeted, and the price of exceeding the viewport is the whole-session
141
+ * repaint this window exists to stay clear of. Over-count and the window merely
142
+ * holds one row fewer.
112
143
  *
113
144
  * Two per row covers what the line estimate cannot see — the rule a code block
114
145
  * draws around itself, the blank line markdown puts between blocks — without
115
146
  * needing to model any of it.
116
147
  */
117
148
  const ROW_HEIGHT_ALLOWANCE = 2;
149
+ /**
150
+ * Markdown can add a margin between adjacent blocks even when source blocks
151
+ * are not separated by a blank line. One potential row per non-empty assistant
152
+ * source line is a conservative ceiling on those vertical margins. Detail and
153
+ * non-assistant rows render as plain Text and need no such allowance.
154
+ */
155
+ function markdownHeightAllowance(message, raw) {
156
+ if (raw || message.role !== 'assistant')
157
+ return 0;
158
+ return message.content.split('\n').filter((line) => line.trim().length > 0).length;
159
+ }
118
160
  /** How tall one row is expected to render, rounded UP. */
119
161
  function messageHeight(message, hasPrev, columns, raw) {
120
- return estimateRenderedLines(messageLines(message, hasPrev, raw), columns) + ROW_HEIGHT_ALLOWANCE;
162
+ return (estimateRenderedLines(messageLines(message, hasPrev, raw), columns) +
163
+ ROW_HEIGHT_ALLOWANCE +
164
+ markdownHeightAllowance(message, raw));
121
165
  }
122
166
  /**
123
167
  * How much of the transcript stays live, given the room there is for it.
@@ -132,7 +176,7 @@ export function liveWindow(input) {
132
176
  const floor = Math.min(Math.max(settled, 0), messages.length);
133
177
  if (rows === undefined || !Number.isFinite(rows))
134
178
  return { settled: messages.length, rows: 0 };
135
- const budget = rows - furnitureRows - SAFETY_ROWS;
179
+ const budget = rows - furnitureRows - LIVE_WINDOW_SAFETY_ROWS;
136
180
  if (budget < 1)
137
181
  return { settled: messages.length, rows: 0 };
138
182
  let height = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"live-window.js","sourceRoot":"","sources":["../../src/tui/live-window.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"live-window.js","sourceRoot":"","sources":["../../src/tui/live-window.ts"],"names":[],"mappings":"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"}
@@ -58,6 +58,8 @@ export declare function describeLoginOutcome(outcome: LoginOutcome, hints?: {
58
58
  }): string;
59
59
  /** What the operator reads after asking namzu to forget the credential. */
60
60
  export declare function describeLogout(path: string, removed: boolean): string;
61
+ /** A provider-specific logout result for a store that may hold two siblings. */
62
+ export declare function describeProviderLogout(path: string, provider: 'anthropic' | 'codex', removed: boolean): string;
61
63
  /**
62
64
  * Whether a `/login` argument is an attempt to finish, rather than to start.
63
65
  *
@@ -1 +1 @@
1
- {"version":3,"file":"login-prompt.d.ts","sourceRoot":"","sources":["../../src/tui/login-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAEtE;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAChC,GAAG,MAAM,CAcT;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IACpD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;CAC/B,GAAG,MAAM,CAaT;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,YAAY,EACrB,KAAK,GAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,MAAM,CAUR;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAIrE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAErE"}
1
+ {"version":3,"file":"login-prompt.d.ts","sourceRoot":"","sources":["../../src/tui/login-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAEtE;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAChC,GAAG,MAAM,CAcT;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IACpD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;CAC/B,GAAG,MAAM,CAaT;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,YAAY,EACrB,KAAK,GAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,MAAM,CAUR;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAIrE;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,WAAW,GAAG,OAAO,EAC/B,OAAO,EAAE,OAAO,GACd,MAAM,CAKR;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAErE"}
@@ -71,9 +71,16 @@ export function describeLoginOutcome(outcome, hints = {}) {
71
71
  /** What the operator reads after asking namzu to forget the credential. */
72
72
  export function describeLogout(path, removed) {
73
73
  return removed
74
- ? `Removed the stored credential at ${path}. This session keeps working until it ends; the next one will ask you to sign in again.\n\nSigning out here does not revoke anything at the provider — do that in your account settings if you need to.`
74
+ ? `Removed the stored credential at ${path}. An active turn is not interrupted; a later turn may ask you to sign in again.\n\nSigning out here does not revoke anything at the provider — do that in your account settings if you need to.`
75
75
  : "There was no stored credential to remove. If namzu is using one, it came from your environment or from another tool on this machine, and neither is namzu's to delete.";
76
76
  }
77
+ /** A provider-specific logout result for a store that may hold two siblings. */
78
+ export function describeProviderLogout(path, provider, removed) {
79
+ const label = provider === 'anthropic' ? 'Claude' : 'Codex';
80
+ return removed
81
+ ? `Removed Namzu's stored ${label} subscription credential at ${path}. Other stored subscriptions were kept. An active turn is not interrupted; a later turn using ${label} may ask you to sign in again.\n\nSigning out here does not revoke anything at the provider, and credentials owned by other tools on this machine were not changed.`
82
+ : `There was no Namzu-owned ${label} subscription credential to remove. Credentials from the environment or another tool on this machine were not changed.`;
83
+ }
77
84
  /**
78
85
  * Whether a `/login` argument is an attempt to finish, rather than to start.
79
86
  *
@@ -1 +1 @@
1
- {"version":3,"file":"login-prompt.js","sourceRoot":"","sources":["../../src/tui/login-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAelC;IACA,MAAM,KAAK,GAAG;QACb,KAAK,CAAC,aAAa;YAClB,CAAC,CAAC,6GAA6G;YAC/G,CAAC,CAAC,0FAA0F;QAC7F,EAAE;QACF,KAAK,CAAC,GAAG;QACT,EAAE;KACF,CAAA;IACD,KAAK,CAAC,IAAI,CACT,mHAAmH,CACnH,CAAA;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,KAAK,CAAC,cAAc,IAAI,wCAAwC,EAAE,CAAC,CAAA;IACvF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACxB,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,KAI7C;IACA,OAAO;QACN,KAAK,CAAC,aAAa;YAClB,CAAC,CAAC,oFAAoF;YACtF,CAAC,CAAC,4CAA4C;QAC/C,EAAE;QACF,KAAK,CAAC,GAAG;QACT,EAAE;QACF,kCAAkC;QAClC,KAAK,KAAK,CAAC,QAAQ,EAAE;QACrB,EAAE;QACF,wFAAwF;KACxF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACb,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CACnC,OAAqB,EACrB,QAA+D,EAAE;IAEjE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,GAAG,OAAO,CAAC,MAAM,WAAW,KAAK,CAAC,KAAK,IAAI,QAAQ,gBAAgB,CAAA;IAC3E,CAAC;IACD,OAAO;QACN,qFAAqF;QACrF,KAAK,OAAO,CAAC,QAAQ,EAAE;QACvB,EAAE;QACF,wFAAwF,KAAK,CAAC,MAAM,IAAI,SAAS,gBAAgB;KACjI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACb,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,OAAgB;IAC5D,OAAO,OAAO;QACb,CAAC,CAAC,oCAAoC,IAAI,yMAAyM;QACnP,CAAC,CAAC,wKAAwK,CAAA;AAC5K,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAuB;IAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAA;AACxC,CAAC"}
1
+ {"version":3,"file":"login-prompt.js","sourceRoot":"","sources":["../../src/tui/login-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAelC;IACA,MAAM,KAAK,GAAG;QACb,KAAK,CAAC,aAAa;YAClB,CAAC,CAAC,6GAA6G;YAC/G,CAAC,CAAC,0FAA0F;QAC7F,EAAE;QACF,KAAK,CAAC,GAAG;QACT,EAAE;KACF,CAAA;IACD,KAAK,CAAC,IAAI,CACT,mHAAmH,CACnH,CAAA;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,KAAK,CAAC,cAAc,IAAI,wCAAwC,EAAE,CAAC,CAAA;IACvF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACxB,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,KAI7C;IACA,OAAO;QACN,KAAK,CAAC,aAAa;YAClB,CAAC,CAAC,oFAAoF;YACtF,CAAC,CAAC,4CAA4C;QAC/C,EAAE;QACF,KAAK,CAAC,GAAG;QACT,EAAE;QACF,kCAAkC;QAClC,KAAK,KAAK,CAAC,QAAQ,EAAE;QACrB,EAAE;QACF,wFAAwF;KACxF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACb,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CACnC,OAAqB,EACrB,QAA+D,EAAE;IAEjE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,GAAG,OAAO,CAAC,MAAM,WAAW,KAAK,CAAC,KAAK,IAAI,QAAQ,gBAAgB,CAAA;IAC3E,CAAC;IACD,OAAO;QACN,qFAAqF;QACrF,KAAK,OAAO,CAAC,QAAQ,EAAE;QACvB,EAAE;QACF,wFAAwF,KAAK,CAAC,MAAM,IAAI,SAAS,gBAAgB;KACjI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACb,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,OAAgB;IAC5D,OAAO,OAAO;QACb,CAAC,CAAC,oCAAoC,IAAI,iMAAiM;QAC3O,CAAC,CAAC,wKAAwK,CAAA;AAC5K,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,sBAAsB,CACrC,IAAY,EACZ,QAA+B,EAC/B,OAAgB;IAEhB,MAAM,KAAK,GAAG,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAA;IAC3D,OAAO,OAAO;QACb,CAAC,CAAC,0BAA0B,KAAK,+BAA+B,IAAI,iGAAiG,KAAK,qKAAqK;QAC/U,CAAC,CAAC,4BAA4B,KAAK,wHAAwH,CAAA;AAC7J,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAuB;IAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAA;AACxC,CAAC"}
@@ -8,6 +8,25 @@
8
8
  * file is capped so a huge file can't blow up the turn. Non-existent or
9
9
  * unreadable tokens are left as literal text.
10
10
  */
11
+ export interface ActiveFileMention {
12
+ /** Inclusive `@` position in the complete composer value. */
13
+ readonly start: number;
14
+ /** Exclusive end of the token, equal to the current cursor. */
15
+ readonly end: number;
16
+ readonly query: string;
17
+ }
18
+ /** The file token currently ending at the cursor, excluding email-like text. */
19
+ export declare function activeFileMention(text: string, cursor: number): ActiveFileMention | null;
20
+ /**
21
+ * List tracked and unignored project files once per hydrated TUI session.
22
+ *
23
+ * The command is argument-vector based (no shell), bounded by time, output and
24
+ * result count, and returns no candidates outside a repository. Exact `@path`
25
+ * input keeps working outside Git; only the convenience picker is absent.
26
+ */
27
+ export declare function listMentionableFiles(cwd: string, signal?: AbortSignal): Promise<readonly string[]>;
28
+ /** Deterministic fuzzy file matches for the active token, bounded for rendering. */
29
+ export declare function matchMentionableFiles(query: string, files: readonly string[], limit?: number): readonly string[];
11
30
  export interface ExpandedMessage {
12
31
  /** Text to send to the model (original + inlined `<file>` blocks). */
13
32
  readonly sendText: string;
@@ -1 +1 @@
1
- {"version":3,"file":"mentions.d.ts","sourceRoot":"","sources":["../../src/tui/mentions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,MAAM,WAAW,eAAe;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAuC,GAC7E,eAAe,CAmBjB"}
1
+ {"version":3,"file":"mentions.d.ts","sourceRoot":"","sources":["../../src/tui/mentions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgBH,MAAM,WAAW,iBAAiB;IACjC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACtB;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CASxF;AAOD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACzC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAsB5B;AAuBD,oFAAoF;AACpF,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,SAAM,GACT,SAAS,MAAM,EAAE,CAcnB;AAED,MAAM,WAAW,eAAe;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAuC,GAC7E,eAAe,CAmBjB"}
@@ -8,11 +8,102 @@
8
8
  * file is capped so a huge file can't blow up the turn. Non-existent or
9
9
  * unreadable tokens are left as literal text.
10
10
  */
11
- import { readFileSync, statSync } from 'node:fs';
11
+ import { execFile } from 'node:child_process';
12
+ import { readFileSync, realpathSync, statSync } from 'node:fs';
12
13
  import { resolve, sep } from 'node:path';
14
+ import { promisify } from 'node:util';
13
15
  /** Path-ish characters only, so `@a.ts,` or `@a.ts)` don't swallow punctuation. */
14
16
  const MENTION = /@([\w./~-]+)/g;
17
+ const MENTION_PATH = /^[\w./~-]+$/u;
15
18
  const MAX_FILE_CHARS = 60_000;
19
+ const MAX_MENTION_FILES = 20_000;
20
+ const FILE_LIST_TIMEOUT_MS = 5_000;
21
+ const run = promisify(execFile);
22
+ /** The file token currently ending at the cursor, excluding email-like text. */
23
+ export function activeFileMention(text, cursor) {
24
+ const prefix = text.slice(0, Math.max(0, Math.min(cursor, text.length)));
25
+ const start = prefix.lastIndexOf('@');
26
+ if (start < 0)
27
+ return null;
28
+ const query = prefix.slice(start + 1);
29
+ if (query.length > 0 && !MENTION_PATH.test(query))
30
+ return null;
31
+ const before = start > 0 ? prefix[start - 1] : undefined;
32
+ if (before !== undefined && /[\w./~-]/u.test(before))
33
+ return null;
34
+ return { start, end: prefix.length, query };
35
+ }
36
+ function isMentionablePath(path) {
37
+ if (path.length === 0 || path.startsWith('/') || !MENTION_PATH.test(path))
38
+ return false;
39
+ return !path.split('/').some((part) => part === '' || part === '.' || part === '..');
40
+ }
41
+ /**
42
+ * List tracked and unignored project files once per hydrated TUI session.
43
+ *
44
+ * The command is argument-vector based (no shell), bounded by time, output and
45
+ * result count, and returns no candidates outside a repository. Exact `@path`
46
+ * input keeps working outside Git; only the convenience picker is absent.
47
+ */
48
+ export async function listMentionableFiles(cwd, signal) {
49
+ try {
50
+ const { stdout } = await run('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z', '--'], {
51
+ cwd,
52
+ signal,
53
+ timeout: FILE_LIST_TIMEOUT_MS,
54
+ maxBuffer: 2 * 1024 * 1024,
55
+ });
56
+ const files = [];
57
+ for (const path of stdout.split('\0')) {
58
+ if (!isMentionablePath(path))
59
+ continue;
60
+ files.push(path);
61
+ if (files.length === MAX_MENTION_FILES)
62
+ break;
63
+ }
64
+ return files;
65
+ }
66
+ catch {
67
+ return [];
68
+ }
69
+ }
70
+ function subsequenceMatch(candidate, query) {
71
+ let index = 0;
72
+ for (const character of candidate) {
73
+ if (character === query[index])
74
+ index += 1;
75
+ if (index === query.length)
76
+ return true;
77
+ }
78
+ return query.length === 0;
79
+ }
80
+ function mentionRank(path, query) {
81
+ if (query.length === 0)
82
+ return [0, path.length, path];
83
+ const normalized = path.toLowerCase();
84
+ const basename = normalized.slice(normalized.lastIndexOf('/') + 1);
85
+ if (basename.startsWith(query))
86
+ return [0, path.length, path];
87
+ if (normalized.startsWith(query))
88
+ return [1, path.length, path];
89
+ if (basename.includes(query))
90
+ return [2, path.length, path];
91
+ if (normalized.includes(query))
92
+ return [3, path.length, path];
93
+ if (subsequenceMatch(normalized, query))
94
+ return [4, path.length, path];
95
+ return null;
96
+ }
97
+ /** Deterministic fuzzy file matches for the active token, bounded for rendering. */
98
+ export function matchMentionableFiles(query, files, limit = 100) {
99
+ const normalizedQuery = query.toLowerCase();
100
+ return files
101
+ .map((path) => ({ path, rank: mentionRank(path, normalizedQuery) }))
102
+ .filter((candidate) => candidate.rank !== null)
103
+ .sort((a, b) => a.rank[0] - b.rank[0] || a.rank[1] - b.rank[1] || a.rank[2].localeCompare(b.rank[2]))
104
+ .slice(0, Math.max(0, limit))
105
+ .map(({ path }) => path);
106
+ }
16
107
  /**
17
108
  * Expand `@path` tokens in `text`. `readFile` is injectable for tests; the
18
109
  * default reads from disk, scoped to `cwd` and size-capped.
@@ -42,11 +133,11 @@ export function expandFileMentions(text, cwd, readFile = (rel) => safeReadInCwd(
42
133
  return { sendText: `${text}\n\n${blocks.join('\n\n')}`, attached };
43
134
  }
44
135
  function safeReadInCwd(cwd, rel) {
45
- const root = resolve(cwd);
46
- const abs = resolve(root, rel);
47
- if (abs !== root && !abs.startsWith(root + sep))
48
- return null;
49
136
  try {
137
+ const root = realpathSync(resolve(cwd));
138
+ const abs = realpathSync(resolve(root, rel));
139
+ if (abs !== root && !abs.startsWith(root + sep))
140
+ return null;
50
141
  if (!statSync(abs).isFile())
51
142
  return null;
52
143
  const raw = readFileSync(abs, 'utf8');
@@ -1 +1 @@
1
- {"version":3,"file":"mentions.js","sourceRoot":"","sources":["../../src/tui/mentions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAExC,mFAAmF;AACnF,MAAM,OAAO,GAAG,eAAe,CAAA;AAE/B,MAAM,cAAc,GAAG,MAAM,CAAA;AAS7B;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CACjC,IAAY,EACZ,GAAW,EACX,WAA+C,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC;IAE/E,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAClC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;IACpD,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC1B,wEAAwE;QACxE,wCAAwC;QACxC,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;QAClD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAQ;QAC/C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACb,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;QAC7B,IAAI,OAAO,KAAK,IAAI;YAAE,SAAQ;QAC9B,MAAM,CAAC,IAAI,CAAC,eAAe,GAAG,OAAO,OAAO,WAAW,CAAC,CAAA;QACxD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACnB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;IAChE,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAA;AACnE,CAAC;AAED,SAAS,aAAa,CAAC,GAAW,EAAE,GAAW;IAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;IACzB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAC9B,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAC5D,IAAI,CAAC;QACJ,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE;YAAE,OAAO,IAAI,CAAA;QACxC,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QACrC,OAAO,GAAG,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAA;IAC5F,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"mentions.js","sourceRoot":"","sources":["../../src/tui/mentions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Maximum exact permission envelope handed to an interactive reviewer.
3
+ *
4
+ * This is a refusal boundary, not a display truncation boundary. A call above
5
+ * it is not offered for approval and is not executed. Keeping the limit on the
6
+ * complete batch also prevents many individually-small calls from turning one
7
+ * consent prompt into an unbounded allocation.
8
+ */
9
+ export declare const MAX_PERMISSION_REVIEW_BYTES = 8000;
10
+ /** Physical rows kept visible in the exact-input pager. */
11
+ export declare const PERMISSION_REVIEW_PAGE_ROWS = 8;
12
+ export interface PermissionReviewCall {
13
+ readonly id: string;
14
+ readonly name: string;
15
+ readonly input: unknown;
16
+ readonly isDestructive: boolean;
17
+ }
18
+ export type PermissionReviewResult = {
19
+ readonly ok: true;
20
+ readonly text: string;
21
+ readonly bytes: number;
22
+ } | {
23
+ readonly ok: false;
24
+ readonly reason: 'too_large' | 'unrepresentable';
25
+ };
26
+ /** Actionable fail-closed feedback for a TUI review that cannot be exact. */
27
+ export declare function permissionReviewRefusal(reason: 'too_large' | 'unrepresentable'): string;
28
+ /** One terminal row of the source-preserving permission projection. */
29
+ export interface PermissionReviewRow {
30
+ /** Stable absolute position in the projected review. */
31
+ readonly index: number;
32
+ readonly text: string;
33
+ /** True when this row continues the logical line above it. */
34
+ readonly continuation: boolean;
35
+ }
36
+ /**
37
+ * Serialize the exact executable batch without invoking getters or `toJSON`.
38
+ *
39
+ * `ToolRegistry.prepareExecution()` already publishes detached JSON values,
40
+ * but this is a host boundary and structural SDK implementations can still
41
+ * supply `unknown`. Ordinary `JSON.stringify` would execute an accessor and
42
+ * could silently turn a Date/Map/custom object into something other than the
43
+ * value being approved. The bounded writer accepts only the JSON graph the
44
+ * permission protocol can preserve and stops building as soon as the complete
45
+ * envelope crosses the limit.
46
+ */
47
+ export declare function buildPermissionReview(calls: readonly PermissionReviewCall[], maxBytes?: number): PermissionReviewResult;
48
+ /**
49
+ * Project and wrap exact review text into PHYSICAL terminal rows.
50
+ *
51
+ * JSON strings can contain thousands of characters on one logical line. Ink
52
+ * would wrap that line after pagination, leaving the suffix below the viewport
53
+ * while the pager still believed there was only one row. Wrapping first makes
54
+ * the pager's unit the same row the operator sees. Non-ASCII code points count
55
+ * conservatively as two cells; overestimating produces shorter rows, never a
56
+ * hidden tail.
57
+ */
58
+ export declare function permissionReviewRows(review: string, terminalColumns: number | undefined): readonly PermissionReviewRow[];
59
+ //# sourceMappingURL=permission-review.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-review.d.ts","sourceRoot":"","sources":["../../src/tui/permission-review.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,OAAQ,CAAA;AAEhD,2DAA2D;AAC3D,eAAO,MAAM,2BAA2B,IAAI,CAAA;AAE5C,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;CAC/B;AAED,MAAM,MAAM,sBAAsB,GAC/B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,iBAAiB,CAAA;CAAE,CAAA;AAE3E,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,WAAW,GAAG,iBAAiB,GAAG,MAAM,CAIvF;AAED,uEAAuE;AACvE,MAAM,WAAW,mBAAmB;IACnC,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;CAC9B;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,SAAS,oBAAoB,EAAE,EACtC,QAAQ,SAA8B,GACpC,sBAAsB,CAqJxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,GAAG,SAAS,GACjC,SAAS,mBAAmB,EAAE,CA6BhC"}
@@ -0,0 +1,219 @@
1
+ import { terminalDisplayText } from './terminal-display.js';
2
+ /**
3
+ * Maximum exact permission envelope handed to an interactive reviewer.
4
+ *
5
+ * This is a refusal boundary, not a display truncation boundary. A call above
6
+ * it is not offered for approval and is not executed. Keeping the limit on the
7
+ * complete batch also prevents many individually-small calls from turning one
8
+ * consent prompt into an unbounded allocation.
9
+ */
10
+ export const MAX_PERMISSION_REVIEW_BYTES = 8_000;
11
+ /** Physical rows kept visible in the exact-input pager. */
12
+ export const PERMISSION_REVIEW_PAGE_ROWS = 8;
13
+ /** Actionable fail-closed feedback for a TUI review that cannot be exact. */
14
+ export function permissionReviewRefusal(reason) {
15
+ return reason === 'too_large'
16
+ ? `Refused: the complete tool batch exceeds the ${MAX_PERMISSION_REVIEW_BYTES}-byte interactive review limit. Split it into smaller calls or add an explicit allow rule after reviewing the operation another way.`
17
+ : 'Refused: this tool batch contains input the interactive permission protocol cannot represent exactly. Use JSON-compatible input or add an explicit allow rule after reviewing the operation another way.';
18
+ }
19
+ class ReviewLimitError extends Error {
20
+ }
21
+ class ReviewShapeError extends Error {
22
+ }
23
+ /**
24
+ * Serialize the exact executable batch without invoking getters or `toJSON`.
25
+ *
26
+ * `ToolRegistry.prepareExecution()` already publishes detached JSON values,
27
+ * but this is a host boundary and structural SDK implementations can still
28
+ * supply `unknown`. Ordinary `JSON.stringify` would execute an accessor and
29
+ * could silently turn a Date/Map/custom object into something other than the
30
+ * value being approved. The bounded writer accepts only the JSON graph the
31
+ * permission protocol can preserve and stops building as soon as the complete
32
+ * envelope crosses the limit.
33
+ */
34
+ export function buildPermissionReview(calls, maxBytes = MAX_PERMISSION_REVIEW_BYTES) {
35
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) {
36
+ return { ok: false, reason: 'unrepresentable' };
37
+ }
38
+ const chunks = [];
39
+ let bytes = 0;
40
+ const active = new WeakSet();
41
+ const append = (chunk) => {
42
+ bytes += Buffer.byteLength(chunk, 'utf8');
43
+ if (bytes > maxBytes)
44
+ throw new ReviewLimitError();
45
+ chunks.push(chunk);
46
+ };
47
+ const appendString = (value) => {
48
+ // JSON escaping only grows a string. Avoid materialising an escaped copy
49
+ // when the source alone has already crossed the complete-envelope cap.
50
+ if (Buffer.byteLength(value, 'utf8') > maxBytes - bytes)
51
+ throw new ReviewLimitError();
52
+ append(JSON.stringify(value));
53
+ };
54
+ const write = (value, indent) => {
55
+ if (value === null) {
56
+ append('null');
57
+ return;
58
+ }
59
+ switch (typeof value) {
60
+ case 'string':
61
+ appendString(value);
62
+ return;
63
+ case 'boolean':
64
+ append(value ? 'true' : 'false');
65
+ return;
66
+ case 'number':
67
+ if (!Number.isFinite(value) || Object.is(value, -0))
68
+ throw new ReviewShapeError();
69
+ append(String(value));
70
+ return;
71
+ case 'object':
72
+ break;
73
+ default:
74
+ throw new ReviewShapeError();
75
+ }
76
+ const object = value;
77
+ if (active.has(object))
78
+ throw new ReviewShapeError();
79
+ active.add(object);
80
+ try {
81
+ if (Array.isArray(value)) {
82
+ const descriptors = Object.getOwnPropertyDescriptors(value);
83
+ if (Object.getOwnPropertySymbols(value).length > 0)
84
+ throw new ReviewShapeError();
85
+ const names = Object.getOwnPropertyNames(value);
86
+ if (names.some((name) => name !== 'length' && (!/^(0|[1-9]\d*)$/.test(name) || Number(name) >= value.length)))
87
+ throw new ReviewShapeError();
88
+ append('[');
89
+ if (value.length > 0)
90
+ append('\n');
91
+ for (let index = 0; index < value.length; index += 1) {
92
+ const descriptor = descriptors[String(index)];
93
+ if (!descriptor || !('value' in descriptor) || !descriptor.enumerable)
94
+ throw new ReviewShapeError();
95
+ append(' '.repeat(indent + 2));
96
+ write(descriptor.value, indent + 2);
97
+ append(index === value.length - 1 ? '\n' : ',\n');
98
+ }
99
+ if (value.length > 0)
100
+ append(' '.repeat(indent));
101
+ append(']');
102
+ return;
103
+ }
104
+ const prototype = Object.getPrototypeOf(value);
105
+ if (prototype !== Object.prototype && prototype !== null)
106
+ throw new ReviewShapeError();
107
+ if (Object.getOwnPropertySymbols(value).length > 0)
108
+ throw new ReviewShapeError();
109
+ const descriptors = Object.getOwnPropertyDescriptors(value);
110
+ const entries = Object.keys(value);
111
+ for (const name of Object.getOwnPropertyNames(value)) {
112
+ const descriptor = descriptors[name];
113
+ if (!descriptor || !descriptor.enumerable || !('value' in descriptor))
114
+ throw new ReviewShapeError();
115
+ }
116
+ append('{');
117
+ if (entries.length > 0)
118
+ append('\n');
119
+ for (let index = 0; index < entries.length; index += 1) {
120
+ const key = entries[index];
121
+ const descriptor = descriptors[key];
122
+ if (!descriptor || !('value' in descriptor))
123
+ throw new ReviewShapeError();
124
+ append(' '.repeat(indent + 2));
125
+ appendString(key);
126
+ append(': ');
127
+ write(descriptor.value, indent + 2);
128
+ append(index === entries.length - 1 ? '\n' : ',\n');
129
+ }
130
+ if (entries.length > 0)
131
+ append(' '.repeat(indent));
132
+ append('}');
133
+ }
134
+ finally {
135
+ active.delete(object);
136
+ }
137
+ };
138
+ try {
139
+ const exactCalls = calls.map((call) => {
140
+ const prototype = Object.getPrototypeOf(call);
141
+ if (prototype !== Object.prototype && prototype !== null)
142
+ throw new ReviewShapeError();
143
+ if (Object.getOwnPropertySymbols(call).length > 0)
144
+ throw new ReviewShapeError();
145
+ const descriptors = Object.getOwnPropertyDescriptors(call);
146
+ const id = descriptors.id;
147
+ const name = descriptors.name;
148
+ const input = descriptors.input;
149
+ const isDestructive = descriptors.isDestructive;
150
+ if (!id ||
151
+ !('value' in id) ||
152
+ typeof id.value !== 'string' ||
153
+ !name ||
154
+ !('value' in name) ||
155
+ typeof name.value !== 'string' ||
156
+ !input ||
157
+ !('value' in input) ||
158
+ !isDestructive ||
159
+ !('value' in isDestructive) ||
160
+ typeof isDestructive.value !== 'boolean')
161
+ throw new ReviewShapeError();
162
+ return {
163
+ id: id.value,
164
+ name: name.value,
165
+ input: input.value,
166
+ isDestructive: isDestructive.value,
167
+ };
168
+ });
169
+ write({
170
+ calls: exactCalls,
171
+ }, 0);
172
+ return { ok: true, text: chunks.join(''), bytes };
173
+ }
174
+ catch (error) {
175
+ return {
176
+ ok: false,
177
+ reason: error instanceof ReviewLimitError ? 'too_large' : 'unrepresentable',
178
+ };
179
+ }
180
+ }
181
+ /**
182
+ * Project and wrap exact review text into PHYSICAL terminal rows.
183
+ *
184
+ * JSON strings can contain thousands of characters on one logical line. Ink
185
+ * would wrap that line after pagination, leaving the suffix below the viewport
186
+ * while the pager still believed there was only one row. Wrapping first makes
187
+ * the pager's unit the same row the operator sees. Non-ASCII code points count
188
+ * conservatively as two cells; overestimating produces shorter rows, never a
189
+ * hidden tail.
190
+ */
191
+ export function permissionReviewRows(review, terminalColumns) {
192
+ // App padding (2), border (2), horizontal padding (2), inner indentation
193
+ // (2) and the row marker (2) are outside the source text. Keep wrapping at
194
+ // one cell even in an unusually narrow terminal; truncating would make the
195
+ // exact approval envelope incomplete precisely where it matters most.
196
+ const width = Math.max(1, (terminalColumns ?? 80) - 10);
197
+ const visible = terminalDisplayText(review);
198
+ const rows = [];
199
+ for (const logical of visible.split('\n')) {
200
+ let text = '';
201
+ let cells = 0;
202
+ let continuation = false;
203
+ for (const point of logical) {
204
+ const codePoint = point.codePointAt(0);
205
+ const pointCells = codePoint !== undefined && codePoint <= 0x7e ? 1 : 2;
206
+ if (text.length > 0 && cells + pointCells > width) {
207
+ rows.push({ index: rows.length, text, continuation });
208
+ text = '';
209
+ cells = 0;
210
+ continuation = true;
211
+ }
212
+ text += point;
213
+ cells += pointCells;
214
+ }
215
+ rows.push({ index: rows.length, text, continuation });
216
+ }
217
+ return rows;
218
+ }
219
+ //# sourceMappingURL=permission-review.js.map