@alisio/alisio-code 0.1.0-alpha.14 → 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. */
@@ -124,6 +124,44 @@ export declare function parseCommand(input: string): {
124
124
  name: string;
125
125
  args: string;
126
126
  } | undefined;
127
+ /** Reasoning-effort capability advertised by a model's catalog entry (`ModelInfo.effort`). */
128
+ export interface EffortCapability {
129
+ supportedLevels: string[];
130
+ defaultLevel?: string;
131
+ }
132
+ /**
133
+ * The effective reasoning effort to send for a model: the persisted level when the model
134
+ * supports it, otherwise the model's default level (silent fallback), and nothing when the
135
+ * model advertises no effort levels at all.
136
+ */
137
+ export declare function effectiveEffort(persisted: string | undefined, capability: EffortCapability | undefined): string | undefined;
138
+ /** Validates a `/effort <level>` argument against the active model; returns an error or nothing. */
139
+ export declare function validateEffortLevel(level: string, capability: EffortCapability | undefined): string | undefined;
140
+ export declare function effortPickerItems(capability: EffortCapability, active: string | undefined): Array<{
141
+ value: string;
142
+ label: string;
143
+ description?: string;
144
+ }>;
145
+ /** Role of a status-line identity part; the renderer maps each role to a distinct color. */
146
+ export type IdentityRole = "agent" | "model" | "provider" | "effort";
147
+ export interface IdentityPart extends Segment {
148
+ role: IdentityRole;
149
+ }
150
+ export interface IdentityInput {
151
+ agent: string;
152
+ model: string;
153
+ provider?: string;
154
+ effort?: string;
155
+ }
156
+ /**
157
+ * The ordered identity parts for the status line below the editor:
158
+ * `agent: <name> · <model> · <provider> · <effort>`, with the effort part omitted when the
159
+ * active model advertises no supported levels (`effort === undefined`). Colors are applied by
160
+ * the renderer per role; this module stays terminal-free and testable.
161
+ */
162
+ export declare function identityParts(input: IdentityInput): IdentityPart[];
163
+ /** Drops the lowest-priority identity parts until the line fits `width`; truncates the survivor. */
164
+ export declare function fitIdentityParts(input: IdentityInput, width: number): IdentityPart[];
127
165
  /** Minimal structural view of a skill for slash autocompletion (subset of the core catalog entry). */
128
166
  export interface SkillCompletionEntry {
129
167
  id: string;
@@ -208,6 +246,8 @@ export interface EditSummary {
208
246
  }
209
247
  export declare function editSummary(name: string, args: string): EditSummary | undefined;
210
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";
211
251
  export type TranscriptItem = {
212
252
  kind: "user";
213
253
  text: string;
@@ -216,6 +256,10 @@ export type TranscriptItem = {
216
256
  text: string;
217
257
  reasoning: string;
218
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;
219
263
  } | {
220
264
  kind: "tool";
221
265
  id: string;
@@ -225,6 +269,17 @@ export type TranscriptItem = {
225
269
  status: ToolStatus;
226
270
  durationMs?: number;
227
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;
228
283
  } | {
229
284
  kind: "notice";
230
285
  text: string;
@@ -264,7 +319,155 @@ export interface ViewState {
264
319
  }
265
320
  export declare function initialViewState(model: string, now?: number): ViewState;
266
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;
267
327
  export declare function reduceEvent(state: ViewState, event: RunEvent): ViewState;
268
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;
269
472
  /** Rebuilds transcript items from persisted history (used by /resume). */
270
473
  export declare function itemsFromHistory(messages: Message[]): TranscriptItem[];