@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.
- package/README.md +117 -17
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +26 -26
- package/dist/cli.js.map +1 -1
- package/dist/commands/acp.d.ts.map +1 -1
- package/dist/commands/acp.js +3 -2
- package/dist/commands/acp.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +2 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +45 -7
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +36 -12
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/{stubs.d.ts → serve.d.ts} +1 -10
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/{stubs.js → serve.js} +1 -24
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/skills.d.ts +19 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +107 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/integrations/mcp/servers.d.ts +7 -0
- package/dist/integrations/mcp/servers.d.ts.map +1 -1
- package/dist/integrations/mcp/servers.js +33 -8
- package/dist/integrations/mcp/servers.js.map +1 -1
- package/dist/integrations/sessions/store.d.ts +8 -0
- package/dist/integrations/sessions/store.d.ts.map +1 -1
- package/dist/integrations/sessions/store.js +15 -0
- package/dist/integrations/sessions/store.js.map +1 -1
- package/dist/integrations/subagents/runtime.d.ts +2 -0
- package/dist/integrations/subagents/runtime.d.ts.map +1 -1
- package/dist/integrations/subagents/runtime.js +86 -13
- package/dist/integrations/subagents/runtime.js.map +1 -1
- package/dist/integrations/updates.d.ts.map +1 -1
- package/dist/integrations/updates.js +29 -4
- package/dist/integrations/updates.js.map +1 -1
- package/dist/tui/App.d.ts +10 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +1039 -252
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/ChoicePicker.d.ts +2 -1
- package/dist/tui/ChoicePicker.d.ts.map +1 -1
- package/dist/tui/ChoicePicker.js +10 -6
- package/dist/tui/ChoicePicker.js.map +1 -1
- package/dist/tui/Composer.d.ts +19 -2
- package/dist/tui/Composer.d.ts.map +1 -1
- package/dist/tui/Composer.js +526 -70
- package/dist/tui/Composer.js.map +1 -1
- package/dist/tui/CopyPicker.d.ts.map +1 -1
- package/dist/tui/CopyPicker.js +3 -4
- package/dist/tui/CopyPicker.js.map +1 -1
- package/dist/tui/EditPromptPicker.d.ts.map +1 -1
- package/dist/tui/EditPromptPicker.js +3 -4
- package/dist/tui/EditPromptPicker.js.map +1 -1
- package/dist/tui/Markdown.d.ts +3 -1
- package/dist/tui/Markdown.d.ts.map +1 -1
- package/dist/tui/Markdown.js +11 -9
- package/dist/tui/Markdown.js.map +1 -1
- package/dist/tui/PermissionOverlay.d.ts +11 -6
- package/dist/tui/PermissionOverlay.d.ts.map +1 -1
- package/dist/tui/PermissionOverlay.js +13 -20
- package/dist/tui/PermissionOverlay.js.map +1 -1
- package/dist/tui/Picker.d.ts.map +1 -1
- package/dist/tui/Picker.js +47 -11
- package/dist/tui/Picker.js.map +1 -1
- package/dist/tui/ResumePicker.d.ts.map +1 -1
- package/dist/tui/ResumePicker.js +6 -1
- package/dist/tui/ResumePicker.js.map +1 -1
- package/dist/tui/StatusBar.js +3 -3
- package/dist/tui/StatusBar.js.map +1 -1
- package/dist/tui/TextPrompt.d.ts +21 -0
- package/dist/tui/TextPrompt.d.ts.map +1 -0
- package/dist/tui/TextPrompt.js +139 -0
- package/dist/tui/TextPrompt.js.map +1 -0
- package/dist/tui/Transcript.d.ts +9 -10
- package/dist/tui/Transcript.d.ts.map +1 -1
- package/dist/tui/Transcript.js +11 -12
- package/dist/tui/Transcript.js.map +1 -1
- package/dist/tui/agent.d.ts +57 -23
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +136 -86
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/exit-summary.d.ts.map +1 -1
- package/dist/tui/exit-summary.js +2 -1
- package/dist/tui/exit-summary.js.map +1 -1
- package/dist/tui/external-editor.d.ts +25 -0
- package/dist/tui/external-editor.d.ts.map +1 -0
- package/dist/tui/external-editor.js +169 -0
- package/dist/tui/external-editor.js.map +1 -0
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +4 -0
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/live-window.d.ts +17 -11
- package/dist/tui/live-window.d.ts.map +1 -1
- package/dist/tui/live-window.js +64 -20
- package/dist/tui/live-window.js.map +1 -1
- package/dist/tui/login-prompt.d.ts +2 -0
- package/dist/tui/login-prompt.d.ts.map +1 -1
- package/dist/tui/login-prompt.js +8 -1
- package/dist/tui/login-prompt.js.map +1 -1
- package/dist/tui/mentions.d.ts +19 -0
- package/dist/tui/mentions.d.ts.map +1 -1
- package/dist/tui/mentions.js +96 -5
- package/dist/tui/mentions.js.map +1 -1
- package/dist/tui/permission-review.d.ts +59 -0
- package/dist/tui/permission-review.d.ts.map +1 -0
- package/dist/tui/permission-review.js +219 -0
- package/dist/tui/permission-review.js.map +1 -0
- package/dist/tui/run-interruption.d.ts +14 -0
- package/dist/tui/run-interruption.d.ts.map +1 -0
- package/dist/tui/run-interruption.js +67 -0
- package/dist/tui/run-interruption.js.map +1 -0
- package/dist/tui/selection-window.d.ts +18 -0
- package/dist/tui/selection-window.d.ts.map +1 -0
- package/dist/tui/selection-window.js +37 -0
- package/dist/tui/selection-window.js.map +1 -0
- package/dist/tui/slashCommands.d.ts +48 -8
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +129 -47
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/terminal-hyperlinks.d.ts +20 -0
- package/dist/tui/terminal-hyperlinks.d.ts.map +1 -0
- package/dist/tui/terminal-hyperlinks.js +70 -0
- package/dist/tui/terminal-hyperlinks.js.map +1 -0
- package/dist/tui/types.d.ts +2 -0
- package/dist/tui/types.d.ts.map +1 -1
- package/dist/tui/use-selection-index.d.ts +8 -0
- package/dist/tui/use-selection-index.d.ts.map +1 -0
- package/dist/tui/use-selection-index.js +13 -0
- package/dist/tui/use-selection-index.js.map +1 -0
- package/dist/tui/workspace-review.d.ts +15 -0
- package/dist/tui/workspace-review.d.ts.map +1 -0
- package/dist/tui/workspace-review.js +67 -0
- package/dist/tui/workspace-review.js.map +1 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +23 -0
- package/dist/version.js.map +1 -0
- package/package.json +8 -7
- package/dist/commands/stubs.d.ts.map +0 -1
- package/dist/commands/stubs.js.map +0 -1
- package/dist/tui/bottom-spacer.d.ts +0 -95
- package/dist/tui/bottom-spacer.d.ts.map +0 -1
- package/dist/tui/bottom-spacer.js +0 -83
- package/dist/tui/bottom-spacer.js.map +0 -1
package/dist/tui/live-window.js
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
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) +
|
|
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 -
|
|
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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,
|
|
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"}
|
package/dist/tui/login-prompt.js
CHANGED
|
@@ -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}.
|
|
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,
|
|
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"}
|
package/dist/tui/mentions.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/tui/mentions.js
CHANGED
|
@@ -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 {
|
|
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');
|
package/dist/tui/mentions.js.map
CHANGED
|
@@ -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;
|
|
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
|