llm-cli-gateway 1.0.1 → 1.4.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/dist/optimizer.js CHANGED
@@ -5,18 +5,18 @@ const COURTESY_PATTERNS = [
5
5
  /\bI would like\b\s*/gi,
6
6
  /\bI need you to\b\s*/gi,
7
7
  /\bI just implemented\b\s*/gi,
8
- /\bPlease do the following:?\s*/gi
8
+ /\bPlease do the following:?\s*/gi,
9
9
  ];
10
10
  const ADJECTIVE_PATTERNS = [
11
11
  /\bcomprehensive\b/gi,
12
12
  /\bdetailed\b/gi,
13
13
  /\bthorough\b/gi,
14
14
  /\boverall\b/gi,
15
- /\bcritical\b/gi
15
+ /\bcritical\b/gi,
16
16
  ];
17
17
  const TASK_PREFIXES = [
18
18
  /^(First|Then|After that|Finally),?\s*/i,
19
- /^(First|Then|After that|Finally),?\s*you should\s*/i
19
+ /^(First|Then|After that|Finally),?\s*you should\s*/i,
20
20
  ];
21
21
  export function estimateTokens(text) {
22
22
  const trimmed = text.trim();
@@ -49,15 +49,15 @@ function optimizeText(text, mode) {
49
49
  function optimizeSegment(segment, mode) {
50
50
  const inlineParts = segment.split(/(`[^`]*`)/g);
51
51
  return inlineParts
52
- .map((part) => (part.startsWith("`") ? part : optimizePlain(part, mode)))
52
+ .map(part => (part.startsWith("`") ? part : optimizePlain(part, mode)))
53
53
  .join("");
54
54
  }
55
55
  function optimizePlain(text, mode) {
56
56
  let output = text;
57
- COURTESY_PATTERNS.forEach((pattern) => {
57
+ COURTESY_PATTERNS.forEach(pattern => {
58
58
  output = output.replace(pattern, "");
59
59
  });
60
- ADJECTIVE_PATTERNS.forEach((pattern) => {
60
+ ADJECTIVE_PATTERNS.forEach(pattern => {
61
61
  output = output.replace(pattern, "");
62
62
  });
63
63
  output = output.replace(/\bfound in the [^:.\n]+/gi, "");
@@ -147,7 +147,7 @@ function compressTaskLists(text) {
147
147
  }
148
148
  function cleanTaskItem(item) {
149
149
  let cleaned = item.trim();
150
- TASK_PREFIXES.forEach((pattern) => {
150
+ TASK_PREFIXES.forEach(pattern => {
151
151
  cleaned = cleaned.replace(pattern, "");
152
152
  });
153
153
  cleaned = cleaned.replace(/^you should\s*/i, "");
@@ -157,7 +157,7 @@ function cleanTaskItem(item) {
157
157
  }
158
158
  function applyArrowNotation(text) {
159
159
  const lines = text.split("\n");
160
- const output = lines.map((line) => {
160
+ const output = lines.map(line => {
161
161
  let updated = line;
162
162
  updated = updated.replace(/\bChange\s+(?:the\s+)?([A-Za-z][\w-]*)\s+to\s+(?:a\s+|an\s+)?([A-Za-z][\w-]*)([.!?]|$)/gi, (_m, from, to, end) => {
163
163
  return `${from.trim()} → ${to.trim()}${end || ""}`;
@@ -172,7 +172,7 @@ function applyArrowNotation(text) {
172
172
  }
173
173
  function applySlashNotation(text) {
174
174
  const lines = text.split("\n");
175
- const output = lines.map((line) => {
175
+ const output = lines.map(line => {
176
176
  const trimmed = line.trim();
177
177
  if (trimmed.length < 50 && /^[A-Za-z0-9][A-Za-z0-9\s/&-]+$/.test(trimmed)) {
178
178
  return line.replace(/\s+and\s+/, "/");
@@ -68,7 +68,14 @@ export class ProcessMonitor {
68
68
  }
69
69
  catch (err) {
70
70
  if (err.code === "ESRCH") {
71
- return { pid, alive: false, state: null, cpuPercent: null, memoryRssKb: null, sampledAt: now };
71
+ return {
72
+ pid,
73
+ alive: false,
74
+ state: null,
75
+ cpuPercent: null,
76
+ memoryRssKb: null,
77
+ sampledAt: now,
78
+ };
72
79
  }
73
80
  // EPERM = process exists but we can't signal it
74
81
  if (err.code === "EPERM") {
@@ -87,7 +94,7 @@ export class ProcessMonitor {
87
94
  const totalJiffies = getTotalCpuJiffies();
88
95
  const prev = this.prevSamples.get(pid);
89
96
  if (prev && totalJiffies !== null) {
90
- const processJiffiesDelta = (parsed.utime + parsed.stime) - (prev.utime + prev.stime);
97
+ const processJiffiesDelta = parsed.utime + parsed.stime - (prev.utime + prev.stime);
91
98
  const totalJiffiesDelta = totalJiffies - prev.totalJiffies;
92
99
  if (totalJiffiesDelta > 0) {
93
100
  cpuPercent = (processJiffiesDelta / totalJiffiesDelta) * 100;
@@ -96,8 +103,10 @@ export class ProcessMonitor {
96
103
  // Store for next delta
97
104
  if (totalJiffies !== null) {
98
105
  this.prevSamples.set(pid, {
99
- utime: parsed.utime, stime: parsed.stime,
100
- totalJiffies, timestamp: Date.now()
106
+ utime: parsed.utime,
107
+ stime: parsed.stime,
108
+ totalJiffies,
109
+ timestamp: Date.now(),
101
110
  });
102
111
  }
103
112
  }
@@ -121,17 +130,24 @@ export class ProcessMonitor {
121
130
  const runningForMs = Date.now() - new Date(job.startedAt).getTime();
122
131
  if (!job.pid) {
123
132
  return {
124
- jobId: job.jobId, cli: job.cli, status: job.status,
125
- processHealth: null, isDead: false, isZombie: false, runningForMs
133
+ jobId: job.jobId,
134
+ cli: job.cli,
135
+ status: job.status,
136
+ processHealth: null,
137
+ isDead: false,
138
+ isZombie: false,
139
+ runningForMs,
126
140
  };
127
141
  }
128
142
  const health = this.sampleProcess(job.pid);
129
143
  return {
130
- jobId: job.jobId, cli: job.cli, status: job.status,
144
+ jobId: job.jobId,
145
+ cli: job.cli,
146
+ status: job.status,
131
147
  processHealth: health,
132
148
  isDead: job.status === "running" && !health.alive,
133
149
  isZombie: job.status === "running" && health.state === "Z",
134
- runningForMs
150
+ runningForMs,
135
151
  };
136
152
  });
137
153
  }
@@ -18,8 +18,56 @@ export declare function validateSessionId(sessionId: string): void;
18
18
  * Pure function: determine --resume args and session provenance from request flags.
19
19
  * Does NOT perform any session I/O — callers handle create/update separately.
20
20
  */
21
+ /**
22
+ * Reject CLI arg values that start with "-" to prevent argument injection.
23
+ * spawn() doesn't invoke a shell so there's no shell injection, but a value
24
+ * like "--dangerously-skip-permissions" passed as a tool name would be
25
+ * interpreted as a flag by the child CLI.
26
+ */
27
+ export declare function sanitizeCliArgValues(values: string[], fieldName: string): string[];
21
28
  export declare function resolveSessionResumeArgs(opts: {
22
29
  sessionId?: string;
23
30
  resumeLatest?: boolean;
24
31
  createNewSession?: boolean;
25
32
  }): SessionResumeResult;
33
+ /**
34
+ * Codex-specific resume planning.
35
+ *
36
+ * Codex CLI ≥ 0.30 exposes session resume as a subcommand (`codex exec resume`),
37
+ * not a flag pair like Claude/Gemini/Grok. So we can't return a simple list of
38
+ * args — we describe the *mode* and let the caller branch when building argv:
39
+ *
40
+ * - "new" → `codex exec [...flags] PROMPT`
41
+ * - "resume-by-id" → `codex exec resume [...resume-safe flags] <SESSION_ID> PROMPT`
42
+ * - "resume-latest" → `codex exec resume --last [...resume-safe flags] PROMPT`
43
+ *
44
+ * `codex exec resume` rejects `--full-auto`; the original session's approval
45
+ * policy is inherited. Callers MUST filter `--full-auto` out of the flag set
46
+ * when mode is one of the resume forms (see `prepareCodexRequest`).
47
+ *
48
+ * `sessionId` MUST be a real Codex session UUID (as recorded under
49
+ * `~/.codex/sessions/`). Gateway-generated `gw-*` IDs are rejected, since
50
+ * they are bookkeeping handles and would 404 against `codex resume`.
51
+ */
52
+ export type CodexSessionMode = "new" | "resume-by-id" | "resume-latest";
53
+ export interface CodexSessionPlan {
54
+ mode: CodexSessionMode;
55
+ /** Real Codex session UUID. Present only when mode === "resume-by-id". */
56
+ sessionId?: string;
57
+ }
58
+ export declare function resolveCodexSessionArgs(opts: {
59
+ sessionId?: string;
60
+ resumeLatest?: boolean;
61
+ createNewSession?: boolean;
62
+ }): CodexSessionPlan;
63
+ /**
64
+ * Grok-specific resume args. Grok accepts `--resume <id>` to resume a named session,
65
+ * and `--continue` to resume the most recent session for the current working directory.
66
+ * Unlike `resolveSessionResumeArgs`, "resume latest" maps to `--continue` (not `--resume latest`)
67
+ * because Grok would interpret a literal "latest" as a session ID.
68
+ */
69
+ export declare function resolveGrokSessionArgs(opts: {
70
+ sessionId?: string;
71
+ resumeLatest?: boolean;
72
+ createNewSession?: boolean;
73
+ }): SessionResumeResult;
@@ -17,16 +17,78 @@ export function validateSessionId(sessionId) {
17
17
  * Pure function: determine --resume args and session provenance from request flags.
18
18
  * Does NOT perform any session I/O — callers handle create/update separately.
19
19
  */
20
+ /**
21
+ * Reject CLI arg values that start with "-" to prevent argument injection.
22
+ * spawn() doesn't invoke a shell so there's no shell injection, but a value
23
+ * like "--dangerously-skip-permissions" passed as a tool name would be
24
+ * interpreted as a flag by the child CLI.
25
+ */
26
+ export function sanitizeCliArgValues(values, fieldName) {
27
+ for (const v of values) {
28
+ if (v.startsWith("-")) {
29
+ throw new Error(`Invalid ${fieldName} value "${v}": values must not start with "-" (argument injection prevention)`);
30
+ }
31
+ }
32
+ return values;
33
+ }
20
34
  export function resolveSessionResumeArgs(opts) {
21
35
  if (opts.createNewSession) {
22
36
  return { resumeArgs: [], effectiveSessionId: undefined, userProvidedSession: false };
23
37
  }
24
38
  if (opts.resumeLatest && !opts.sessionId) {
25
- return { resumeArgs: ["--resume", "latest"], effectiveSessionId: undefined, userProvidedSession: false };
39
+ return {
40
+ resumeArgs: ["--resume", "latest"],
41
+ effectiveSessionId: undefined,
42
+ userProvidedSession: false,
43
+ };
44
+ }
45
+ if (opts.sessionId) {
46
+ validateSessionId(opts.sessionId);
47
+ return {
48
+ resumeArgs: ["--resume", opts.sessionId],
49
+ effectiveSessionId: opts.sessionId,
50
+ userProvidedSession: true,
51
+ };
52
+ }
53
+ return { resumeArgs: [], effectiveSessionId: undefined, userProvidedSession: false };
54
+ }
55
+ export function resolveCodexSessionArgs(opts) {
56
+ if (opts.createNewSession) {
57
+ return { mode: "new" };
58
+ }
59
+ if (opts.sessionId) {
60
+ validateSessionId(opts.sessionId);
61
+ return { mode: "resume-by-id", sessionId: opts.sessionId };
62
+ }
63
+ if (opts.resumeLatest) {
64
+ return { mode: "resume-latest" };
65
+ }
66
+ return { mode: "new" };
67
+ }
68
+ /**
69
+ * Grok-specific resume args. Grok accepts `--resume <id>` to resume a named session,
70
+ * and `--continue` to resume the most recent session for the current working directory.
71
+ * Unlike `resolveSessionResumeArgs`, "resume latest" maps to `--continue` (not `--resume latest`)
72
+ * because Grok would interpret a literal "latest" as a session ID.
73
+ */
74
+ export function resolveGrokSessionArgs(opts) {
75
+ if (opts.createNewSession) {
76
+ return { resumeArgs: [], effectiveSessionId: undefined, userProvidedSession: false };
77
+ }
78
+ if (opts.resumeLatest && !opts.sessionId) {
79
+ return {
80
+ resumeArgs: ["--continue"],
81
+ effectiveSessionId: undefined,
82
+ userProvidedSession: false,
83
+ };
26
84
  }
27
85
  if (opts.sessionId) {
28
86
  validateSessionId(opts.sessionId);
29
- return { resumeArgs: ["--resume", opts.sessionId], effectiveSessionId: opts.sessionId, userProvidedSession: true };
87
+ return {
88
+ resumeArgs: ["--resume", opts.sessionId],
89
+ effectiveSessionId: opts.sessionId,
90
+ userProvidedSession: true,
91
+ };
30
92
  }
31
93
  return { resumeArgs: [], effectiveSessionId: undefined, userProvidedSession: false };
32
94
  }
package/dist/resources.js CHANGED
@@ -18,8 +18,8 @@ export class ResourceProvider {
18
18
  annotations: {
19
19
  audience: ["user", "assistant"],
20
20
  priority: 0.7,
21
- lastModified: new Date().toISOString()
22
- }
21
+ lastModified: new Date().toISOString(),
22
+ },
23
23
  },
24
24
  {
25
25
  uri: "sessions://claude",
@@ -29,8 +29,8 @@ export class ResourceProvider {
29
29
  mimeType: "application/json",
30
30
  annotations: {
31
31
  audience: ["user", "assistant"],
32
- priority: 0.6
33
- }
32
+ priority: 0.6,
33
+ },
34
34
  },
35
35
  {
36
36
  uri: "sessions://codex",
@@ -40,8 +40,8 @@ export class ResourceProvider {
40
40
  mimeType: "application/json",
41
41
  annotations: {
42
42
  audience: ["user", "assistant"],
43
- priority: 0.6
44
- }
43
+ priority: 0.6,
44
+ },
45
45
  },
46
46
  {
47
47
  uri: "sessions://gemini",
@@ -51,8 +51,19 @@ export class ResourceProvider {
51
51
  mimeType: "application/json",
52
52
  annotations: {
53
53
  audience: ["user", "assistant"],
54
- priority: 0.6
55
- }
54
+ priority: 0.6,
55
+ },
56
+ },
57
+ {
58
+ uri: "sessions://grok",
59
+ name: "Grok Sessions",
60
+ title: "⚡ Grok Sessions",
61
+ description: "List of Grok conversation sessions",
62
+ mimeType: "application/json",
63
+ annotations: {
64
+ audience: ["user", "assistant"],
65
+ priority: 0.6,
66
+ },
56
67
  },
57
68
  {
58
69
  uri: "models://claude",
@@ -62,8 +73,8 @@ export class ResourceProvider {
62
73
  mimeType: "application/json",
63
74
  annotations: {
64
75
  audience: ["user", "assistant"],
65
- priority: 0.8
66
- }
76
+ priority: 0.8,
77
+ },
67
78
  },
68
79
  {
69
80
  uri: "models://codex",
@@ -73,8 +84,8 @@ export class ResourceProvider {
73
84
  mimeType: "application/json",
74
85
  annotations: {
75
86
  audience: ["user", "assistant"],
76
- priority: 0.8
77
- }
87
+ priority: 0.8,
88
+ },
78
89
  },
79
90
  {
80
91
  uri: "models://gemini",
@@ -84,8 +95,19 @@ export class ResourceProvider {
84
95
  mimeType: "application/json",
85
96
  annotations: {
86
97
  audience: ["user", "assistant"],
87
- priority: 0.8
88
- }
98
+ priority: 0.8,
99
+ },
100
+ },
101
+ {
102
+ uri: "models://grok",
103
+ name: "Grok Models",
104
+ title: "⚡ Grok Models & Capabilities",
105
+ description: "Available Grok models and their capabilities",
106
+ mimeType: "application/json",
107
+ annotations: {
108
+ audience: ["user", "assistant"],
109
+ priority: 0.8,
110
+ },
89
111
  },
90
112
  {
91
113
  uri: "metrics://performance",
@@ -95,9 +117,9 @@ export class ResourceProvider {
95
117
  mimeType: "application/json",
96
118
  annotations: {
97
119
  audience: ["user", "assistant"],
98
- priority: 0.9
99
- }
100
- }
120
+ priority: 0.9,
121
+ },
122
+ },
101
123
  ];
102
124
  }
103
125
  // Read a specific resource by URI
@@ -110,19 +132,20 @@ export class ResourceProvider {
110
132
  mimeType: "application/json",
111
133
  text: JSON.stringify({
112
134
  total: sessions.length,
113
- sessions: sessions.map((s) => ({
135
+ sessions: sessions.map(s => ({
114
136
  id: s.id,
115
137
  cli: s.cli,
116
138
  description: s.description,
117
139
  createdAt: s.createdAt,
118
- lastUsedAt: s.lastUsedAt
140
+ lastUsedAt: s.lastUsedAt,
119
141
  })),
120
142
  activeSessions: {
121
143
  claude: (await this.sessionManager.getActiveSession("claude"))?.id || null,
122
144
  codex: (await this.sessionManager.getActiveSession("codex"))?.id || null,
123
- gemini: (await this.sessionManager.getActiveSession("gemini"))?.id || null
124
- }
125
- }, null, 2)
145
+ gemini: (await this.sessionManager.getActiveSession("gemini"))?.id || null,
146
+ grok: (await this.sessionManager.getActiveSession("grok"))?.id || null,
147
+ },
148
+ }, null, 2),
126
149
  };
127
150
  }
128
151
  if (uri === "sessions://claude") {
@@ -134,8 +157,8 @@ export class ResourceProvider {
134
157
  cli: "claude",
135
158
  total: sessions.length,
136
159
  sessions,
137
- activeSession: (await this.sessionManager.getActiveSession("claude"))?.id || null
138
- }, null, 2)
160
+ activeSession: (await this.sessionManager.getActiveSession("claude"))?.id || null,
161
+ }, null, 2),
139
162
  };
140
163
  }
141
164
  if (uri === "sessions://codex") {
@@ -147,8 +170,8 @@ export class ResourceProvider {
147
170
  cli: "codex",
148
171
  total: sessions.length,
149
172
  sessions,
150
- activeSession: (await this.sessionManager.getActiveSession("codex"))?.id || null
151
- }, null, 2)
173
+ activeSession: (await this.sessionManager.getActiveSession("codex"))?.id || null,
174
+ }, null, 2),
152
175
  };
153
176
  }
154
177
  if (uri === "sessions://gemini") {
@@ -160,8 +183,21 @@ export class ResourceProvider {
160
183
  cli: "gemini",
161
184
  total: sessions.length,
162
185
  sessions,
163
- activeSession: (await this.sessionManager.getActiveSession("gemini"))?.id || null
164
- }, null, 2)
186
+ activeSession: (await this.sessionManager.getActiveSession("gemini"))?.id || null,
187
+ }, null, 2),
188
+ };
189
+ }
190
+ if (uri === "sessions://grok") {
191
+ const sessions = await this.sessionManager.listSessions("grok");
192
+ return {
193
+ uri,
194
+ mimeType: "application/json",
195
+ text: JSON.stringify({
196
+ cli: "grok",
197
+ total: sessions.length,
198
+ sessions,
199
+ activeSession: (await this.sessionManager.getActiveSession("grok"))?.id || null,
200
+ }, null, 2),
165
201
  };
166
202
  }
167
203
  // Model capability resources
@@ -170,7 +206,7 @@ export class ResourceProvider {
170
206
  return {
171
207
  uri,
172
208
  mimeType: "application/json",
173
- text: JSON.stringify(cliInfo.claude, null, 2)
209
+ text: JSON.stringify(cliInfo.claude, null, 2),
174
210
  };
175
211
  }
176
212
  if (uri === "models://codex") {
@@ -178,7 +214,7 @@ export class ResourceProvider {
178
214
  return {
179
215
  uri,
180
216
  mimeType: "application/json",
181
- text: JSON.stringify(cliInfo.codex, null, 2)
217
+ text: JSON.stringify(cliInfo.codex, null, 2),
182
218
  };
183
219
  }
184
220
  if (uri === "models://gemini") {
@@ -186,14 +222,22 @@ export class ResourceProvider {
186
222
  return {
187
223
  uri,
188
224
  mimeType: "application/json",
189
- text: JSON.stringify(cliInfo.gemini, null, 2)
225
+ text: JSON.stringify(cliInfo.gemini, null, 2),
226
+ };
227
+ }
228
+ if (uri === "models://grok") {
229
+ const cliInfo = getCliInfo();
230
+ return {
231
+ uri,
232
+ mimeType: "application/json",
233
+ text: JSON.stringify(cliInfo.grok, null, 2),
190
234
  };
191
235
  }
192
236
  if (uri === "metrics://performance") {
193
237
  return {
194
238
  uri,
195
239
  mimeType: "application/json",
196
- text: JSON.stringify(this.performanceMetrics.snapshot(), null, 2)
240
+ text: JSON.stringify(this.performanceMetrics.snapshot(), null, 2),
197
241
  };
198
242
  }
199
243
  return null;
package/dist/retry.js CHANGED
@@ -27,15 +27,17 @@ const isDefaultTransient = (error) => {
27
27
  return false;
28
28
  }
29
29
  // Shell command-related errors
30
- if (error.code === 124) { // wall-clock timeout (explicit, caller-set) — transient
30
+ if (error.code === 124) {
31
+ // wall-clock timeout (explicit, caller-set) — transient
31
32
  return true;
32
33
  }
33
34
  // Note: exit code 125 = idle timeout (stuck process) — intentionally non-transient
34
- if (error.code === 'ENOENT') { // command not found
35
+ if (error.code === "ENOENT") {
36
+ // command not found
35
37
  return false;
36
38
  }
37
39
  // Node.js network errors
38
- const transientErrorCodes = ['ECONNRESET', 'ETIMEDOUT', 'ECONNREFUSED', 'EPIPE'];
40
+ const transientErrorCodes = ["ECONNRESET", "ETIMEDOUT", "ECONNREFUSED", "EPIPE"];
39
41
  if (transientErrorCodes.includes(error.code)) {
40
42
  return true;
41
43
  }
@@ -142,5 +144,5 @@ export async function withRetry(operation, circuitBreaker, retryOptions, logger)
142
144
  await new Promise(resolve => setTimeout(resolve, delay));
143
145
  }
144
146
  }
145
- throw new Error('[Retry] Operation failed after all retry attempts.');
147
+ throw new Error("[Retry] Operation failed after all retry attempts.");
146
148
  }
@@ -1,17 +1,6 @@
1
- /**
2
- * Review Integrity Bypass Detection
3
- *
4
- * Detects when orchestrating agents neuter the multi-LLM review process by:
5
- * - Embedding tool-suppression language in review prompts
6
- * - Inlining full code instead of letting reviewers read files directly
7
- * - Setting allowedTools:[] to strip tool access from reviewers
8
- *
9
- * Two-gate design: violations only emitted when BOTH review context AND
10
- * a restriction are detected. This avoids false positives on non-review
11
- * prompts that happen to contain similar language.
12
- */
1
+ export type ReviewIntegrityViolationType = "empty_allowed_tools" | "critical_tools_disallowed" | "tool_suppression";
13
2
  export interface ReviewIntegrityViolation {
14
- type: "tool_suppression" | "inlined_code" | "empty_allowed_tools" | "critical_tools_disallowed";
3
+ type: ReviewIntegrityViolationType;
15
4
  score: number;
16
5
  detail: string;
17
6
  }
@@ -20,31 +9,10 @@ export interface ReviewIntegrityResult {
20
9
  violations: ReviewIntegrityViolation[];
21
10
  totalScore: number;
22
11
  }
23
- /**
24
- * Detect whether the prompt is a review/audit context.
25
- * Uses two-part detection: unambiguous phrases match alone,
26
- * ambiguous verbs (review, analyze, etc.) require a code anchor.
27
- * Normalizes Unicode before matching to prevent confusable bypasses.
28
- */
29
- export declare function isReviewContext(prompt: string): boolean;
30
- /**
31
- * Detect tool-suppression language in a prompt.
32
- * Returns the matched patterns for diagnostics.
33
- */
34
- export declare function detectToolSuppression(prompt: string): string[];
35
- /**
36
- * Detect inlined code blocks that look like full file dumps.
37
- * Two detection strategies:
38
- * 1. Any single code block with 200+ chars is flagged.
39
- * 2. Fallback: if total chars across ALL code blocks (even small ones)
40
- * exceeds 1000, flag to catch split-block bypass attempts.
41
- */
42
- export declare function detectInlinedCode(prompt: string): {
43
- count: number;
44
- totalChars: number;
45
- };
46
- export declare function checkReviewIntegrity(params: {
12
+ export interface ReviewIntegrityInput {
47
13
  prompt: string;
48
14
  allowedTools?: string[];
49
15
  disallowedTools?: string[];
50
- }): ReviewIntegrityResult;
16
+ }
17
+ export declare function isReviewContext(prompt: string): boolean;
18
+ export declare function checkReviewIntegrity(input: ReviewIntegrityInput): ReviewIntegrityResult;