@alisio/alisio-code 0.1.0-alpha.15 → 0.1.0-alpha.16

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.
@@ -2,7 +2,7 @@
2
2
  * Pure presentation logic for the TUI: formatting, command parsing and the reduction of
3
3
  * versioned runner events into a view model. No terminal or pi-tui imports here.
4
4
  */
5
- import type { Message, ModelInfo, RunEvent } from "@alisio/sdk";
5
+ import type { Message, ModelInfo, RunEvent, ToolResult, TreeNode, UiBlock } from "@alisio/sdk";
6
6
  export type Level = "ok" | "warn" | "danger";
7
7
  export declare function formatTokens(n: number): string;
8
8
  /** Prefixes every model with its owning provider so identical model ids are never ambiguous. */
@@ -246,6 +246,8 @@ export interface EditSummary {
246
246
  }
247
247
  export declare function editSummary(name: string, args: string): EditSummary | undefined;
248
248
  export type ToolStatus = "running" | "approval" | "ok" | "error";
249
+ /** Capability kind of a tool call; drives grouping labels and batch display. */
250
+ export type ToolKind = "read" | "write" | "process" | "mcp" | "internal" | "other";
249
251
  export type TranscriptItem = {
250
252
  kind: "user";
251
253
  text: string;
@@ -254,6 +256,10 @@ export type TranscriptItem = {
254
256
  text: string;
255
257
  reasoning: string;
256
258
  done: boolean;
259
+ /** Epoch ms of the first reasoning delta; with `reasoningDoneAt` it approximates the thinking interval. */
260
+ reasoningStartedAt?: number;
261
+ /** Epoch ms of the last reasoning delta (the end of the visible thought section). */
262
+ reasoningDoneAt?: number;
257
263
  } | {
258
264
  kind: "tool";
259
265
  id: string;
@@ -263,6 +269,17 @@ export type TranscriptItem = {
263
269
  status: ToolStatus;
264
270
  durationMs?: number;
265
271
  preview?: string;
272
+ /** Structured block rendered natively by the TUI (from a `{type:"ui"}` result part). */
273
+ ui?: UiBlock;
274
+ /** Base64 image rendered inline by the TUI when the terminal supports it (from a `{type:"image"}` part). */
275
+ image?: {
276
+ mimeType: string;
277
+ data: string;
278
+ };
279
+ /** Effect-derived kind (grouping label); name-heuristic fallback for replayed history. */
280
+ toolKind?: ToolKind;
281
+ /** Exit code derived safely from the JSON preview when the tool reports one (run_process/shell/search_text). */
282
+ exitCode?: number;
266
283
  } | {
267
284
  kind: "notice";
268
285
  text: string;
@@ -302,7 +319,155 @@ export interface ViewState {
302
319
  }
303
320
  export declare function initialViewState(model: string, now?: number): ViewState;
304
321
  export declare function addItem(state: ViewState, item: TranscriptItem): ViewState;
322
+ /** Approximated thinking interval of a completed reasoning section, when the events carried timestamps. */
323
+ export declare function reasoningDurationMs(item: {
324
+ reasoningStartedAt?: number;
325
+ reasoningDoneAt?: number;
326
+ }): number | undefined;
305
327
  export declare function reduceEvent(state: ViewState, event: RunEvent): ViewState;
306
328
  export declare function lastAssistantText(items: TranscriptItem[]): string | undefined;
329
+ /**
330
+ * True when a printable key should copy the last assistant response instead of typing: bound to
331
+ * `c`/`y` (and uppercase) on an EMPTY editor line, while no autocomplete is showing and no turn
332
+ * is running — the same "empty input" convention Enter and Ctrl+D already use. A non-empty input
333
+ * never triggers, so typing `c` or `y` mid-message is unaffected.
334
+ */
335
+ export declare function editorCopyKey(data: string, input: {
336
+ text: string;
337
+ autocomplete: boolean;
338
+ busy: boolean;
339
+ }): boolean;
340
+ /** True when an unknown event value is a `Node` of a tree UI block. */
341
+ export declare function isTreeNode(value: unknown): value is TreeNode;
342
+ /** True when an unknown event value is an Alisio `UiBlock` (validated defensively). */
343
+ export declare function isUiBlock(value: unknown): value is UiBlock;
344
+ /** True when an unknown event value is a `{type:"image"}`-style part (mime + base64 data). */
345
+ export declare function isImagePart(value: unknown): value is {
346
+ mimeType: string;
347
+ data: string;
348
+ };
349
+ /** Rich (ui/image) parts of a persisted tool result, if any. */
350
+ export declare function richPartsOf(result: ToolResult | undefined): {
351
+ ui?: UiBlock;
352
+ image?: {
353
+ mimeType: string;
354
+ data: string;
355
+ };
356
+ };
357
+ /**
358
+ * `read_file` → `Read File`, `search_text` → `Search Text`, `mcp_devforge_time_diff` →
359
+ * `MCP · Devforge Time Diff`. Splits on `_`/`-`/`.`/spaces, keeps known acronyms (MCP, HTTP…)
360
+ * and bare numeric suffixes (`read_file_2` → `Read File 2`). The machine name stays available
361
+ * dimmed in grouped detail rows and in the `/tools` and `/stats` reports.
362
+ */
363
+ export declare function humanizeToolName(name: string): string;
364
+ /** Verb and count noun used by grouped batch rows per tool kind. */
365
+ export declare const TOOL_KIND_VERB: Record<ToolKind, string>;
366
+ export declare const TOOL_KIND_WORD: Record<ToolKind, string>;
367
+ /**
368
+ * Capability kind of a tool call. Live events carry the registry `effect` (exact); replayed
369
+ * history falls back to conservative name heuristics. `mcp_*` is always the mcp kind.
370
+ */
371
+ export declare function toolKindOf(name: string, effect?: string): ToolKind;
372
+ /** Terminal-preview caps for collapsed rows (same values the renderer always used). */
373
+ export declare const OUTPUT_PREVIEW_LINES_OK = 3;
374
+ export declare const OUTPUT_PREVIEW_LINES_ERROR = 6;
375
+ export declare const previewLinesFor: (status: ToolStatus) => number;
376
+ /** Preview rows as the renderer shows them: blank lines dropped, tabs kept for the caller to expand. */
377
+ export declare const previewRows: (preview: string) => string[];
378
+ /** True when the preview exceeds the collapsed cap (the row becomes a collapsible "output"). */
379
+ export declare function previewTruncated(preview: string | undefined, status: ToolStatus): boolean;
380
+ /**
381
+ * Exit code derived safely from a text preview. `run_process`/`shell`/`search_text` return
382
+ * `{stdout, stderr, exitCode, truncated}` JSON; read-effect tools may carry an appended
383
+ * `\n<instructions>…` block, retried on the JSON prefix. Any other shape yields nothing.
384
+ */
385
+ export declare function exitCodeOf(preview: string | undefined): number | undefined;
386
+ /** Renderable view of one tool call (singleton row, or a member of a grouped batch). */
387
+ export interface ToolItemView {
388
+ id: string;
389
+ /** Machine name (kept for dimmed detail display). */
390
+ name: string;
391
+ /** Humanized display name. */
392
+ humanName: string;
393
+ kind: ToolKind;
394
+ summary: string;
395
+ status: ToolStatus;
396
+ durationMs?: number;
397
+ preview?: string;
398
+ ui?: UiBlock;
399
+ image?: {
400
+ mimeType: string;
401
+ data: string;
402
+ };
403
+ exitCode?: number;
404
+ }
405
+ /** A batch of consecutive same-kind, finished tool calls rendered as ONE collapsible row. */
406
+ export interface GroupedTool {
407
+ /** Id of the first member call (stable fold key while the batch grows). */
408
+ id: string;
409
+ members: ToolItemView[];
410
+ kind: ToolKind;
411
+ /** Aggregate: `error` when any member failed, else `ok` (members are all terminal). */
412
+ status: ToolStatus;
413
+ /** Sum of member durations. */
414
+ totalMs: number;
415
+ /** Header name: the shared humanized tool name, or the kind verb for mixed batches. */
416
+ label: string;
417
+ /** Count noun matching `label` (`reads`, `files`, `commands`…). */
418
+ word: string;
419
+ }
420
+ /** Everything the transcript renders: a plain item, or one grouped batch row. */
421
+ export type TranscriptEntry = TranscriptItem | {
422
+ kind: "group";
423
+ group: GroupedTool;
424
+ };
425
+ export declare function toolItemView(item: Extract<TranscriptItem, {
426
+ kind: "tool";
427
+ }>): ToolItemView;
428
+ /**
429
+ * Consecutive tool calls of the same kind whose statuses are ALL terminal (ok/error) merge into
430
+ * one `group` entry; running/approval calls always stay singleton so their spinner and live state
431
+ * stay visible. A kind change splits the run (`read` never merges with `shell`). Order is
432
+ * preserved; every entry carries its index within `items` (fold keys for assistant items need
433
+ * it). One linear pass — called on state changes only, never per frame.
434
+ */
435
+ export declare function groupToolEntries(items: TranscriptItem[]): Array<{
436
+ entry: TranscriptEntry;
437
+ at: number;
438
+ }>;
439
+ /** Content identity of a group (fold/diff purposes): member ids, statuses and durations. */
440
+ export declare function groupIdentity(group: GroupedTool): string;
441
+ export type FoldableKind = "reasoning" | "group" | "output";
442
+ /** A collapsible region of the transcript and how to address it (fold key, toggle target). */
443
+ export interface FoldCandidate {
444
+ /** Stable key for the fold state map (`a:` assistant index, `g:`/`t:` tool ids). */
445
+ key: string;
446
+ kind: FoldableKind;
447
+ /** Running/live content defaults expanded; finished reasoning, batches and long output collapse. */
448
+ defaultExpanded: boolean;
449
+ }
450
+ /** Cap for the reasoning text when expanded; longer sections show a truncation note. */
451
+ export declare const REASONING_EXPAND_MAX_LINES = 40;
452
+ /**
453
+ * The fold contract of one transcript entry: nothing (not collapsible), or a candidate with its
454
+ * default state. `at` is the entry's index within the ORIGINAL items array (assistant fold keys
455
+ * are stable because transcript items are append-only).
456
+ */
457
+ export declare function foldCandidateOf(entry: TranscriptEntry, at: number): FoldCandidate | undefined;
458
+ /** Stable key of a transcript entry (used by the component sync to reuse row instances). */
459
+ export declare function entryKeyOf(entry: TranscriptEntry, at: number): string;
460
+ /** Every fold candidate of the transcript in display order; the keybinding targets the last one. */
461
+ export declare function foldCandidates(items: TranscriptItem[]): FoldCandidate[];
462
+ /**
463
+ * True when a printable key should toggle the nearest collapsible transcript row instead of
464
+ * typing: bound to `x`/`X` on an EMPTY editor line (no autocomplete), the same "empty input"
465
+ * convention Enter, Ctrl+D and `c`/`y` already use. Unlike copy, it also works while a turn runs
466
+ * (the nearest candidate may still exist mid-stream). A non-empty input never triggers.
467
+ */
468
+ export declare function foldToggleKey(data: string, input: {
469
+ text: string;
470
+ autocomplete: boolean;
471
+ }): boolean;
307
472
  /** Rebuilds transcript items from persisted history (used by /resume). */
308
473
  export declare function itemsFromHistory(messages: Message[]): TranscriptItem[];