@vybestack/llxprt-code-tools 0.10.0 → 0.11.0-nightly.260728.ffaa8d5d7

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 (56) hide show
  1. package/dist/src/index.d.ts +3 -1
  2. package/dist/src/index.js +2 -0
  3. package/dist/src/index.js.map +1 -1
  4. package/dist/src/interfaces/IShellToolHost.d.ts +2 -1
  5. package/dist/src/tools/ast-edit/ast-edit-invocation.d.ts +3 -2
  6. package/dist/src/tools/ast-edit/ast-edit-invocation.js +38 -11
  7. package/dist/src/tools/ast-edit/ast-edit-invocation.js.map +1 -1
  8. package/dist/src/tools/ast-edit/ast-read-file-invocation.d.ts +2 -2
  9. package/dist/src/tools/ast-edit/ast-read-file-invocation.js.map +1 -1
  10. package/dist/src/tools/ast-edit/edit-calculator.d.ts +6 -3
  11. package/dist/src/tools/ast-edit/edit-calculator.js +24 -6
  12. package/dist/src/tools/ast-edit/edit-calculator.js.map +1 -1
  13. package/dist/src/tools/ast-edit/validation-categorizer.d.ts +58 -0
  14. package/dist/src/tools/ast-edit/validation-categorizer.js +207 -0
  15. package/dist/src/tools/ast-edit/validation-categorizer.js.map +1 -0
  16. package/dist/src/tools/codesearch.js.map +1 -1
  17. package/dist/src/tools/direct-web-fetch.js.map +1 -1
  18. package/dist/src/tools/exa-web-search.js.map +1 -1
  19. package/dist/src/tools/shell.d.ts +2 -2
  20. package/dist/src/tools/shell.js +17 -3
  21. package/dist/src/tools/shell.js.map +1 -1
  22. package/dist/src/tools/todo-pause.d.ts +2 -2
  23. package/dist/src/tools/todo-pause.js +1 -1
  24. package/dist/src/tools/todo-pause.js.map +1 -1
  25. package/dist/src/tools/todo-read.d.ts +2 -2
  26. package/dist/src/tools/todo-read.js +1 -1
  27. package/dist/src/tools/todo-read.js.map +1 -1
  28. package/dist/src/tools/todo-write.d.ts +2 -2
  29. package/dist/src/tools/todo-write.js +1 -1
  30. package/dist/src/tools/todo-write.js.map +1 -1
  31. package/dist/src/tools/tool-registry.d.ts +3 -3
  32. package/dist/src/tools/tool-registry.js +6 -43
  33. package/dist/src/tools/tool-registry.js.map +1 -1
  34. package/dist/src/tools/tools.d.ts +6 -5
  35. package/dist/src/tools/tools.js.map +1 -1
  36. package/dist/src/utils/fileUtils.js +8 -1
  37. package/dist/src/utils/fileUtils.js.map +1 -1
  38. package/dist/src/utils/terminalSerializer.d.ts +12 -0
  39. package/package.json +151 -1
  40. package/src/index.ts +11 -0
  41. package/src/interfaces/IShellToolHost.ts +3 -1
  42. package/src/tools/ast-edit/ast-edit-invocation.ts +82 -12
  43. package/src/tools/ast-edit/ast-read-file-invocation.ts +2 -2
  44. package/src/tools/ast-edit/edit-calculator.ts +26 -6
  45. package/src/tools/ast-edit/validation-categorizer.ts +263 -0
  46. package/src/tools/codesearch.ts +2 -1
  47. package/src/tools/direct-web-fetch.ts +2 -1
  48. package/src/tools/exa-web-search.ts +2 -1
  49. package/src/tools/shell.ts +19 -6
  50. package/src/tools/todo-pause.ts +7 -2
  51. package/src/tools/todo-read.ts +7 -2
  52. package/src/tools/todo-write.ts +7 -2
  53. package/src/tools/tool-registry.ts +16 -82
  54. package/src/tools/tools.ts +10 -6
  55. package/src/utils/fileUtils.ts +11 -1
  56. package/src/utils/terminalSerializer.ts +9 -0
@@ -23,6 +23,7 @@ import {
23
23
  type ToolCallConfirmationDetails,
24
24
  type ToolExecuteConfirmationDetails,
25
25
  type PolicyUpdateOptions,
26
+ type LiveOutputUpdate,
26
27
  } from './tools.js';
27
28
  import { ToolErrorType } from '../types/tool-error.js';
28
29
  import { stringOrDefault } from '../utils/stringCoalescing.js';
@@ -184,7 +185,7 @@ export class ShellToolInvocation extends BaseToolInvocation<
184
185
 
185
186
  async execute(
186
187
  signal: AbortSignal,
187
- updateOutput?: (output: string) => void,
188
+ updateOutput?: (update: LiveOutputUpdate) => void,
188
189
  ): Promise<ToolResult> {
189
190
  this.validateFilterParams();
190
191
 
@@ -243,7 +244,7 @@ export class ShellToolInvocation extends BaseToolInvocation<
243
244
  timeoutSeconds: number | undefined,
244
245
  defaultTimeoutSeconds: number,
245
246
  _timeoutId: ReturnType<typeof setTimeout> | null,
246
- updateOutput?: (output: string) => void,
247
+ updateOutput?: (update: LiveOutputUpdate) => void,
247
248
  ): Promise<ToolResult> {
248
249
  const combinedSignal = timeoutController.signal;
249
250
  const cwd = this.resolveCwd();
@@ -340,7 +341,7 @@ export class ShellToolInvocation extends BaseToolInvocation<
340
341
  }
341
342
 
342
343
  private createOutputEventHandler(
343
- updateOutput: ((output: string) => void) | undefined,
344
+ updateOutput: ((update: LiveOutputUpdate) => void) | undefined,
344
345
  ): (event: ShellOutputEvent) => void {
345
346
  let cumulativeOutput = '';
346
347
  let lastUpdateTime = 0;
@@ -353,8 +354,20 @@ export class ShellToolInvocation extends BaseToolInvocation<
353
354
  switch (event.type) {
354
355
  case 'data': {
355
356
  if (isBinaryStream) break;
356
- cumulativeOutput = event.chunk ?? '';
357
- shouldUpdate = true;
357
+ const chunk = event.chunk;
358
+ if (typeof chunk === 'string') {
359
+ cumulativeOutput = chunk;
360
+ shouldUpdate = true;
361
+ } else if (chunk !== undefined) {
362
+ // PTY terminal-buffer snapshot — replace semantics.
363
+ updateOutput({ mode: 'replace', data: chunk });
364
+ lastUpdateTime = Date.now();
365
+ } else {
366
+ // Preserve pre-migration behavior: an undefined chunk on a data
367
+ // event forwards the (empty) cumulative output as an append.
368
+ cumulativeOutput = '';
369
+ shouldUpdate = true;
370
+ }
358
371
  break;
359
372
  }
360
373
  case 'binary_detected':
@@ -375,7 +388,7 @@ export class ShellToolInvocation extends BaseToolInvocation<
375
388
  }
376
389
 
377
390
  if (shouldUpdate) {
378
- updateOutput(cumulativeOutput);
391
+ updateOutput({ mode: 'append', data: cumulativeOutput });
379
392
  lastUpdateTime = Date.now();
380
393
  }
381
394
  };
@@ -5,7 +5,12 @@
5
5
  */
6
6
 
7
7
  import { Type } from '../types/schema-type.js';
8
- import { BaseTool, type ToolResult, Kind } from './tools.js';
8
+ import {
9
+ BaseTool,
10
+ type ToolResult,
11
+ Kind,
12
+ type LiveOutputUpdate,
13
+ } from './tools.js';
9
14
  import type { ITodoService } from '../interfaces/ITodoService.js';
10
15
  import type { IToolHost } from '../interfaces/IToolHost.js';
11
16
  import { EmojiFilter, isEmojiFilterMode } from '../utils/EmojiFilter.js';
@@ -118,7 +123,7 @@ export class TodoPause extends BaseTool<TodoPauseParams, ToolResult> {
118
123
  async execute(
119
124
  params: TodoPauseParams,
120
125
  _signal: AbortSignal,
121
- _updateOutput?: (output: string) => void,
126
+ _updateOutput?: (update: LiveOutputUpdate) => void,
122
127
  ): Promise<ToolResult> {
123
128
  const reasonResult = this.filterReason(params.reason);
124
129
  if (reasonResult.blocked) {
@@ -4,7 +4,12 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { BaseTool, type ToolResult, Kind } from './tools.js';
7
+ import {
8
+ BaseTool,
9
+ type ToolResult,
10
+ Kind,
11
+ type LiveOutputUpdate,
12
+ } from './tools.js';
8
13
  import { type Todo } from '../types/todo-schemas.js';
9
14
  import { Type } from '../types/schema-type.js';
10
15
  import { TodoReminderService } from '../utils/todoReminderService.js';
@@ -39,7 +44,7 @@ export class TodoRead extends BaseTool<TodoReadParams, ToolResult> {
39
44
  async execute(
40
45
  _params: TodoReadParams,
41
46
  _signal: AbortSignal,
42
- _updateOutput?: (output: string) => void,
47
+ _updateOutput?: (update: LiveOutputUpdate) => void,
43
48
  ): Promise<ToolResult> {
44
49
  // Get session and agent IDs from context
45
50
  const sessionId = this.context?.sessionId ?? 'default';
@@ -5,7 +5,12 @@
5
5
  */
6
6
 
7
7
  import { Type } from '../types/schema-type.js';
8
- import { BaseTool, type ToolResult, Kind } from './tools.js';
8
+ import {
9
+ BaseTool,
10
+ type ToolResult,
11
+ Kind,
12
+ type LiveOutputUpdate,
13
+ } from './tools.js';
9
14
  import { type Todo, TodoArraySchema } from '../types/todo-schemas.js';
10
15
  import { DEFAULT_AGENT_ID } from './todo-store.js';
11
16
  import { TodoReminderService } from '../utils/todoReminderService.js';
@@ -147,7 +152,7 @@ export class TodoWrite extends BaseTool<TodoWriteParams, ToolResult> {
147
152
  async execute(
148
153
  params: TodoWriteParams,
149
154
  _signal: AbortSignal,
150
- _updateOutput?: (output: string) => void,
155
+ _updateOutput?: (update: LiveOutputUpdate) => void,
151
156
  ): Promise<ToolResult> {
152
157
  const normalizedTodos = this.normalizeTodos(params.todos);
153
158
  const validation = this.validateAndFilterTodos(normalizedTodos);
@@ -11,6 +11,7 @@ import {
11
11
  type ToolResult,
12
12
  BaseTool,
13
13
  BaseToolInvocation,
14
+ type LiveOutputUpdate,
14
15
  } from './tools.js';
15
16
  import { type ToolContext, isContextAwareTool } from '../types/tool-context.js';
16
17
  import type { IToolRegistryHost } from '../interfaces/IToolRegistryHost.js';
@@ -22,6 +23,11 @@ import { ToolErrorType } from '../types/tool-error.js';
22
23
  import { safeJsonStringify } from '../utils/safeJsonStringify.js';
23
24
  import { debugLogger } from '../utils/debugLogger.js';
24
25
  import { normalizeToolName } from '../formatters/toolNameUtils.js';
26
+ import {
27
+ buildToolGovernance,
28
+ isToolBlocked,
29
+ type ToolGovernance,
30
+ } from '../formatters/toolGovernanceUtils.js';
25
31
 
26
32
  export const DISCOVERED_TOOL_PREFIX = 'discovered_tool_';
27
33
 
@@ -115,7 +121,7 @@ Signal: Signal number or \`(none)\` if no signal was received.
115
121
  async execute(
116
122
  params: ToolParams,
117
123
  signal: AbortSignal,
118
- _updateOutput?: (output: string) => void,
124
+ _updateOutput?: (update: LiveOutputUpdate) => void,
119
125
  ): Promise<ToolResult> {
120
126
  const callCommand = this.config.getToolCallCommand?.() ?? '';
121
127
  const child = spawn(callCommand, [this.name]);
@@ -262,7 +268,7 @@ class DiscoveredToolInvocation extends BaseToolInvocation<
262
268
 
263
269
  async execute(
264
270
  signal: AbortSignal,
265
- updateOutput?: (output: string) => void,
271
+ updateOutput?: (update: LiveOutputUpdate) => void,
266
272
  ): Promise<ToolResult> {
267
273
  return this.tool.execute(this.params, signal, updateOutput);
268
274
  }
@@ -287,70 +293,16 @@ export class ToolRegistry {
287
293
  this.messageBus = messageBus;
288
294
  }
289
295
 
290
- private getToolGovernance(): {
291
- allowed: Set<string>;
292
- disabled: Set<string>;
293
- excluded: Set<string>;
294
- } {
295
- const rawEphemerals =
296
- typeof this.config.getEphemeralSettings === 'function'
297
- ? (this.config.getEphemeralSettings() as
298
- | Record<string, unknown>
299
- | null
300
- | undefined
301
- | false
302
- | 0
303
- | '')
304
- : undefined;
305
-
306
- const ephemerals: Record<string, unknown> =
307
- resolveEphemerals(rawEphemerals);
308
-
309
- const allowedRaw = Array.isArray(ephemerals['tools.allowed'])
310
- ? (ephemerals['tools.allowed'] as string[])
311
- : [];
312
-
313
- function resolveDisabled(ephemeralsMap: Record<string, unknown>): string[] {
314
- if (Array.isArray(ephemeralsMap['tools.disabled'])) {
315
- return ephemeralsMap['tools.disabled'] as string[];
316
- }
317
- if (Array.isArray(ephemeralsMap['disabled-tools'])) {
318
- return ephemeralsMap['disabled-tools'] as string[];
319
- }
320
- return [];
321
- }
322
-
323
- const disabledRaw = resolveDisabled(ephemerals);
324
- const excludedRaw = this.config.getExcludeTools?.() ?? [];
325
-
326
- return {
327
- allowed: new Set(
328
- allowedRaw.map((name) => normalizeToolName(name) ?? name),
329
- ),
330
- disabled: new Set(
331
- disabledRaw.map((name) => normalizeToolName(name) ?? name),
332
- ),
333
- excluded: new Set(
334
- excludedRaw.map((name) => normalizeToolName(name) ?? name),
335
- ),
336
- };
296
+ private getToolGovernance(): ToolGovernance {
297
+ return buildToolGovernance({
298
+ getEphemeralSettings: () =>
299
+ this.config.getEphemeralSettings?.() ?? undefined,
300
+ getExcludeTools: () => this.config.getExcludeTools?.(),
301
+ });
337
302
  }
338
303
 
339
- private isToolActive(
340
- toolName: string,
341
- governance: ReturnType<ToolRegistry['getToolGovernance']>,
342
- ): boolean {
343
- const canonical = normalizeToolName(toolName) ?? toolName;
344
- if (governance.excluded.has(canonical)) {
345
- return false;
346
- }
347
- if (governance.disabled.has(canonical)) {
348
- return false;
349
- }
350
- if (governance.allowed.size > 0 && !governance.allowed.has(canonical)) {
351
- return false;
352
- }
353
- return true;
304
+ private isToolActive(toolName: string, governance: ToolGovernance): boolean {
305
+ return !isToolBlocked(toolName, governance);
354
306
  }
355
307
 
356
308
  /**
@@ -818,21 +770,3 @@ export class ToolRegistry {
818
770
  }
819
771
  }
820
772
  }
821
-
822
- type EphemeralValue =
823
- | Record<string, unknown>
824
- | null
825
- | undefined
826
- | false
827
- | 0
828
- | '';
829
-
830
- function resolveEphemerals(raw: EphemeralValue): Record<string, unknown> {
831
- if (raw === null || raw === undefined || raw === false || raw === 0) {
832
- return {};
833
- }
834
- if (raw === '') {
835
- return {};
836
- }
837
- return raw;
838
- }
@@ -18,7 +18,10 @@ import {
18
18
  } from '../interfaces/IToolMessageBus.js';
19
19
  import type { DiffUpdateResult } from '../interfaces/IIdeService.js';
20
20
  import { SchemaValidator } from '../utils/schemaValidator.js';
21
- import type { AnsiOutput } from '../utils/terminalSerializer.js';
21
+ import type {
22
+ AnsiOutput,
23
+ LiveOutputUpdate,
24
+ } from '../utils/terminalSerializer.js';
22
25
  import type { ContextAwareTool, ToolContext } from '../types/tool-context.js';
23
26
  import {
24
27
  ToolConfirmationOutcome,
@@ -28,6 +31,7 @@ import { ToolErrorType } from '../types/tool-error.js';
28
31
 
29
32
  export { ToolConfirmationOutcome } from '../types/tool-confirmation-types.js';
30
33
  export type { ToolConfirmationPayload } from '../types/tool-confirmation-types.js';
34
+ export type { LiveOutputUpdate } from '../utils/terminalSerializer.js';
31
35
 
32
36
  /**
33
37
  * Represents a validated and ready-to-execute tool call.
@@ -74,7 +78,7 @@ export interface ToolInvocation<
74
78
  */
75
79
  execute(
76
80
  signal: AbortSignal,
77
- updateOutput?: (output: string | AnsiOutput) => void,
81
+ updateOutput?: (update: LiveOutputUpdate) => void,
78
82
  terminalColumns?: number,
79
83
  terminalRows?: number,
80
84
  setPidCallback?: (pid: number) => void,
@@ -419,7 +423,7 @@ export abstract class BaseToolInvocation<
419
423
 
420
424
  abstract execute(
421
425
  signal: AbortSignal,
422
- updateOutput?: (output: string | AnsiOutput) => void,
426
+ updateOutput?: (update: LiveOutputUpdate) => void,
423
427
  terminalColumns?: number,
424
428
  terminalRows?: number,
425
429
  setPidCallback?: (pid: number) => void,
@@ -577,7 +581,7 @@ export abstract class DeclarativeTool<
577
581
  async buildAndExecute(
578
582
  params: TParams,
579
583
  signal: AbortSignal,
580
- updateOutput?: (output: string | AnsiOutput) => void,
584
+ updateOutput?: (update: LiveOutputUpdate) => void,
581
585
  ): Promise<TResult> {
582
586
  const invocation = this.build(params);
583
587
  return invocation.execute(signal, updateOutput);
@@ -1002,7 +1006,7 @@ export abstract class BaseTool<
1002
1006
  abstract execute(
1003
1007
  params: TParams,
1004
1008
  signal: AbortSignal,
1005
- updateOutput?: (output: string) => void,
1009
+ updateOutput?: (update: LiveOutputUpdate) => void,
1006
1010
  ): Promise<TResult>;
1007
1011
 
1008
1012
  /**
@@ -1043,7 +1047,7 @@ class BaseToolLegacyInvocation<
1043
1047
 
1044
1048
  async execute(
1045
1049
  signal: AbortSignal,
1046
- updateOutput?: (output: string) => void,
1050
+ updateOutput?: (update: LiveOutputUpdate) => void,
1047
1051
  _terminalColumns?: number,
1048
1052
  _terminalRows?: number,
1049
1053
  _setPidCallback?: (pid: number) => void,
@@ -222,8 +222,18 @@ export async function detectFileType(
222
222
  return 'svg';
223
223
  }
224
224
 
225
+ // mime-db maps the FreeHand family (.fh, .fh4, .fh5, .fh7, .fhc) to
226
+ // image/x-freehand, but these extensions are commonly shader/source text.
227
+ // Exclude them from the image short-circuit below so they fall through to
228
+ // content-based detection (text or binary).
229
+ const freehandExtensions = ['.fh', '.fh4', '.fh5', '.fh7', '.fhc'];
230
+
225
231
  const lookedUpMimeType = mime.lookup(filePath);
226
- if (typeof lookedUpMimeType === 'string' && lookedUpMimeType !== '') {
232
+ if (
233
+ typeof lookedUpMimeType === 'string' &&
234
+ lookedUpMimeType !== '' &&
235
+ !freehandExtensions.includes(ext)
236
+ ) {
227
237
  if (lookedUpMimeType.startsWith('image/')) return 'image';
228
238
  if (lookedUpMimeType.startsWith('audio/')) return 'audio';
229
239
  if (lookedUpMimeType.startsWith('video/')) return 'video';
@@ -35,3 +35,12 @@ export interface AnsiToken {
35
35
 
36
36
  export type AnsiLine = AnsiToken[];
37
37
  export type AnsiOutput = AnsiLine[];
38
+
39
+ /**
40
+ * Explicit tagged update protocol for live-output streaming. Discriminated
41
+ * by `mode`: text stream producers emit `append` (incremental deltas);
42
+ * terminal-buffer producers emit `replace` (full snapshots).
43
+ */
44
+ export type LiveOutputUpdate =
45
+ | { mode: 'append'; data: string }
46
+ | { mode: 'replace'; data: AnsiOutput };