@chances-ai/ui-core 17.0.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.
@@ -0,0 +1,172 @@
1
+ /**
2
+ * (5.9 / v14) Pure view-side tool-call label. Renders `⏺ Name(argSummary)` —
3
+ * see `tool-message.tsx`. This is a MINIMAL, NON-AUTHORITATIVE label (codex R1
4
+ * SHOULD-1): it only echoes the call's own args (available on the `tool:call`
5
+ * bus event, which carries no `ToolContext`). It deliberately does NOT claim
6
+ * create-vs-overwrite, byte counts, or diffs — that fidelity comes only from
7
+ * the richer permission summary (`tool:permission`), which the view stashes
8
+ * and renders under the `⎿` branch (see `view-model.ts`).
9
+ *
10
+ * Pure `(name, args) => string`, so it is fully unit-testable without Ink.
11
+ *
12
+ * (v17 ui-core) NO `node:*`: `home` is always injected by the caller (the TUI
13
+ * passes `homedir()`; the web/server passes the engine's reported home or
14
+ * nothing). When `home` is absent, paths are shown verbatim (no `~` fold) —
15
+ * the browser-safe degradation.
16
+ */
17
+ /** Read a string property from an unknown JSON args bag, or "" if absent. */
18
+ function s(args, key) {
19
+ if (args && typeof args === "object" && key in args) {
20
+ const v = args[key];
21
+ if (typeof v === "string")
22
+ return v;
23
+ if (typeof v === "number" || typeof v === "boolean")
24
+ return String(v);
25
+ }
26
+ return "";
27
+ }
28
+ /** Collapse a leading home-dir to `~`. `home` is injected by the caller; when
29
+ * absent (browser/no-home) the path is returned verbatim. Handles both POSIX
30
+ * (`/`) and Windows (`\`) separators after the home prefix (codex R2 NICE-8). */
31
+ export function homeRelative(path, home) {
32
+ if (!path || !home)
33
+ return path;
34
+ if (path === home)
35
+ return "~";
36
+ if (path.startsWith(`${home}/`) || path.startsWith(`${home}\\`)) {
37
+ return `~${path.slice(home.length)}`;
38
+ }
39
+ return path;
40
+ }
41
+ /** Truncate a single-line value for the inline label. */
42
+ function clip(text, max = 60) {
43
+ const oneLine = text.replace(/\s+/g, " ").trim();
44
+ return oneLine.length <= max ? oneLine : `${oneLine.slice(0, max - 1)}…`;
45
+ }
46
+ /** Display name for a tool: builtin names verbatim; `mcp__<server>__<tool>`
47
+ * shown as `<server›>`. Edit/write get a claude-code-style verb. */
48
+ export function toolDisplayName(name) {
49
+ const mcp = parseMcpName(name);
50
+ if (mcp)
51
+ return mcp.server;
52
+ switch (name) {
53
+ case "edit":
54
+ return "Edit";
55
+ case "write":
56
+ return "Write";
57
+ case "read":
58
+ return "Read";
59
+ case "bash":
60
+ return "Bash";
61
+ case "grep":
62
+ return "Grep";
63
+ case "glob":
64
+ return "Glob";
65
+ case "diff":
66
+ return "Diff";
67
+ case "web_fetch":
68
+ return "Fetch";
69
+ case "task":
70
+ return "Task";
71
+ case "lsp":
72
+ return "Lsp";
73
+ case "pty":
74
+ return "Pty";
75
+ case "memory_save":
76
+ return "Memory";
77
+ case "memory_delete":
78
+ return "Memory";
79
+ case "memory_list":
80
+ return "Memory";
81
+ default:
82
+ return name;
83
+ }
84
+ }
85
+ /** `mcp__<server>__<tool>` → `{server, tool}`; null for non-MCP or malformed
86
+ * names (empty server — codex R2 NICE-9). A `mcp__<server>` with no `__tool`
87
+ * is allowed (server-level), but an empty server component is rejected. */
88
+ export function parseMcpName(name) {
89
+ if (!name.startsWith("mcp__"))
90
+ return null;
91
+ const rest = name.slice("mcp__".length);
92
+ const sep = rest.indexOf("__");
93
+ const server = sep < 0 ? rest : rest.slice(0, sep);
94
+ if (server.length === 0)
95
+ return null;
96
+ return { server, tool: sep < 0 ? "" : rest.slice(sep + 2) };
97
+ }
98
+ /**
99
+ * The parenthesized argument summary for the header. Empty string ⇒ the header
100
+ * renders just `⏺ Name` with no parens.
101
+ */
102
+ export function formatToolCall(name, args, opts) {
103
+ const home = opts?.home;
104
+ const rel = (p) => homeRelative(p, home);
105
+ const mcp = parseMcpName(name);
106
+ if (mcp)
107
+ return mcp.tool;
108
+ switch (name) {
109
+ case "read":
110
+ case "edit":
111
+ case "write":
112
+ case "diff":
113
+ return clip(rel(s(args, "path")));
114
+ case "bash": {
115
+ const desc = s(args, "description");
116
+ return clip(desc || s(args, "command"));
117
+ }
118
+ case "grep": {
119
+ const pattern = s(args, "pattern");
120
+ const path = s(args, "path");
121
+ return clip(path ? `${pattern} in ${rel(path)}` : pattern);
122
+ }
123
+ case "glob": {
124
+ const pattern = s(args, "pattern");
125
+ const path = s(args, "path");
126
+ return clip(path ? `${pattern} in ${rel(path)}` : pattern);
127
+ }
128
+ case "web_fetch":
129
+ return clip(s(args, "url"));
130
+ case "task": {
131
+ const sub = s(args, "subagent_type");
132
+ const desc = s(args, "description");
133
+ return clip([sub, desc].filter(Boolean).join(": ") || desc || sub);
134
+ }
135
+ case "lsp": {
136
+ const op = s(args, "op");
137
+ const path = s(args, "path");
138
+ const query = s(args, "query");
139
+ const detail = path ? rel(path) : query;
140
+ return clip([op, detail].filter(Boolean).join(" "));
141
+ }
142
+ case "pty": {
143
+ const op = s(args, "op");
144
+ const cmd = s(args, "command");
145
+ return clip([op, cmd].filter(Boolean).join(" "));
146
+ }
147
+ case "memory_save":
148
+ case "memory_delete":
149
+ return clip(s(args, "name"));
150
+ case "memory_list":
151
+ return clip(s(args, "scope"));
152
+ default:
153
+ return compactArgs(args);
154
+ }
155
+ }
156
+ /** Fallback for unknown tools: a compact `key=value` join of scalar args. */
157
+ function compactArgs(args) {
158
+ if (!args || typeof args !== "object")
159
+ return "";
160
+ const parts = [];
161
+ for (const [k, v] of Object.entries(args)) {
162
+ if (v === null || v === undefined)
163
+ continue;
164
+ if (typeof v === "object")
165
+ continue; // skip nested for a one-liner
166
+ parts.push(`${k}=${String(v)}`);
167
+ if (parts.length >= 3)
168
+ break;
169
+ }
170
+ return clip(parts.join(" "));
171
+ }
172
+ //# sourceMappingURL=tool-line.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-line.js","sourceRoot":"","sources":["../src/tool-line.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,233 @@
1
+ import type { ApprovalMode, ApprovalState } from "@chances-ai/runtime";
2
+ import type { AppEventBus } from "./event-bus.js";
3
+ import type { AuthorizationDecision, PermissionDecision, PermissionRequest, QuestionDecision } from "@chances-ai/tools";
4
+ import { type FrameScheduler } from "./frame-scheduler.js";
5
+ /** (5.9) Extract the `linesDiff` block (from the first `@@ -d` hunk header to
6
+ * the end) out of a write/edit permission summary, or null when the summary
7
+ * carries no diff (single-line edits, "diff preview skipped", non-write tools). */
8
+ export declare function extractDiff(summary: string): string | null;
9
+ export type LineKind = "user" | "assistant" | "tool" | "error" | "info";
10
+ export interface Line {
11
+ kind: LineKind;
12
+ /** user/assistant/info/error body — OR, for a `tool` line, the pure arg
13
+ * summary from `formatToolCall`. */
14
+ text: string;
15
+ /** `tool`: undefined while running, then the result's ok/err. */
16
+ ok?: boolean;
17
+ /** `tool`: raw tool name (drives the ⏺ header + display-name mapping). */
18
+ toolName?: string;
19
+ /** `tool`: correlates the call / permission / result bus events. */
20
+ callId?: string;
21
+ /** `tool`: result preview shown under the ⎿ branch. */
22
+ result?: string;
23
+ /** `tool`: raw `linesDiff` text for write/edit, rendered under ⎿ (5.9). */
24
+ diff?: string;
25
+ /** `tool`: whether `diff` is whole-file-anchored (write) vs snippet (edit). */
26
+ anchored?: boolean;
27
+ }
28
+ interface Pending {
29
+ req: PermissionRequest;
30
+ resolve: (decision: PermissionDecision) => void;
31
+ }
32
+ export interface ChatViewModelOptions {
33
+ /** (5.8) Injectable frame coalescer. Production uses the default ~16 ms
34
+ * `setTimeout` batcher; tests inject a manual queue for determinism. */
35
+ scheduler?: FrameScheduler;
36
+ /** (5.8) See `config.tui.toolResultPreviewLines`. Default 12. */
37
+ toolResultPreviewLines?: number;
38
+ /** (v17) Home dir for `~`-folding tool-call paths. Injected — the TUI passes
39
+ * `homedir()`, a browser client passes the engine-reported home or nothing
40
+ * (absent ⇒ paths shown verbatim). Keeps `view-model` free of `node:os`. */
41
+ home?: string;
42
+ }
43
+ /**
44
+ * Observable view-model. The Ink tree subscribes via useSyncExternalStore and
45
+ * never touches domain objects directly — the only inputs are bus events and the
46
+ * permission resolver. This is the seam that keeps the UI decoupled from core.
47
+ *
48
+ * (5.8) Rendering is split into a committed prefix (frozen scrollback fed to
49
+ * Ink `<Static>`, rendered once) and a small live tail (re-rendered each
50
+ * frame). Streamed `assistant:delta` re-renders are coalesced to one per frame
51
+ * via {@link FrameScheduler}; every structural event force-flushes immediately.
52
+ */
53
+ export declare class ChatViewModel {
54
+ private readonly approval?;
55
+ lines: Line[];
56
+ busy: boolean;
57
+ pending: Pending | null;
58
+ /** (5.3) True while the Shift+Tab-into-yolo confirmation overlay is up.
59
+ * app.tsx renders the red confirm box; `resolveYoloConfirm` clears it. */
60
+ yoloConfirmPending: boolean;
61
+ /**
62
+ * (5.8) Count of leading `lines` that are committed (frozen). Lines
63
+ * `[0, committedCount)` are append-only — an index's content never changes
64
+ * once committed — and feed Ink `<Static>`. Lines `[committedCount, end)`
65
+ * are the live tail. Advanced ONLY forward (monotonic) so `<Static>` never
66
+ * has to un-render anything. The only line that ever mutates in place is an
67
+ * open assistant line (text grows per delta) and it is always live.
68
+ */
69
+ committedCount: number;
70
+ /**
71
+ * (5.8) Bumped by `clearLines()` and used as the Ink `<Static key>`. Ink
72
+ * `<Static>` can't be emptied by replacing its items, so `/clear` remounts
73
+ * it under a fresh key (needs `ink >= 7.0.3`, which fixes the
74
+ * remount-with-different-key drop-new-items bug).
75
+ */
76
+ clearGeneration: number;
77
+ private version;
78
+ private readonly listeners;
79
+ private assistantOpen;
80
+ private busUnsubscribers;
81
+ private readonly scheduler;
82
+ private readonly toolResultPreviewLines;
83
+ /** (v17) Injected home dir for `~`-folding; undefined ⇒ no fold. */
84
+ private readonly home?;
85
+ /** Non-null while a coalesced delta frame is queued (see `scheduleBump`). */
86
+ private pendingFrame;
87
+ /**
88
+ * (5.9) Diff blocks parsed from `tool:permission` summaries, keyed by callId,
89
+ * awaiting their `tool:result` so they can be attached to the tool line and
90
+ * rendered under the `⎿` branch. Emitted before `gate.evaluate` for every
91
+ * write/edit call (engine.ts:763) — so this populates even under
92
+ * auto-edit/yolo, giving transcript diffs in all approval modes with zero
93
+ * engine/contract change. Cleared on detach/clear so it can't leak.
94
+ */
95
+ private readonly diffStash;
96
+ /**
97
+ * (5.3) Optional session approval-mode holder. When wired (interactive
98
+ * `chat`), the footer reflects `approvalMode` and Shift+Tab / `/approval`
99
+ * mutate it. Left undefined in tests that don't exercise modes — every
100
+ * approval method then no-ops and `approvalMode` reads `"default"`.
101
+ *
102
+ * (5.8) `options` injects the frame scheduler + tool-result preview length;
103
+ * both default so existing callers (`new ChatViewModel()`,
104
+ * `new ChatViewModel(approval)`) keep working unchanged.
105
+ */
106
+ constructor(approval?: ApprovalState | undefined, options?: ChatViewModelOptions);
107
+ subscribe: (fn: () => void) => (() => void);
108
+ getSnapshot: () => number;
109
+ /**
110
+ * (5.8) The frozen scrollback prefix — fed to Ink `<Static>` and rendered
111
+ * once. Guaranteed append-only: the element at index `i` never changes once
112
+ * it appears here. A fresh array is returned each call, but the prefix
113
+ * content is stable, which is all `<Static>` relies on.
114
+ */
115
+ committedLines(): Line[];
116
+ /** (5.8) The live tail re-rendered each frame: the open assistant line while
117
+ * streaming, or an in-flight tool / tool-result line awaiting its terminal
118
+ * event. Usually 0–1 entries. */
119
+ liveLines(): Line[];
120
+ /** (5.9) The still-live `tool` line for a callId (normally the last line).
121
+ * Returns a mutable ref so `tool:result` can fill it in place — safe because
122
+ * it is in the live region (index ≥ committedCount), not yet committed. */
123
+ private findLiveTool;
124
+ /**
125
+ * (5.9 codex R2 MUST-1) Advance `committedCount` toward `target` but NEVER
126
+ * past the earliest still-running tool line (`kind:"tool"` with `ok ===
127
+ * undefined`). A sync/background subagent emits its own `tool:call` /
128
+ * `assistant:delta` frames on the SAME bus BETWEEN a parent `task` tool's
129
+ * call and its result; without this clamp those frames would commit the
130
+ * parent's tool line, after which `findLiveTool` could no longer fill it
131
+ * (mutating a committed line breaks the `<Static>` append-only invariant) and
132
+ * the parent result would spawn a duplicate empty block. Keeping every
133
+ * unresolved tool line live until its result arrives makes the fill always
134
+ * append-only-safe. `turn:end` force-commits unconditionally (the turn is
135
+ * over). Monotonic: `target ≥ committedCount`, and the result is in
136
+ * `[committedCount, target]`.
137
+ */
138
+ private commitThrough;
139
+ private bump;
140
+ /**
141
+ * (5.8) Coalesce a streamed-token re-render onto the next frame. The first
142
+ * delta in a window arms the scheduler; later deltas in the same window are
143
+ * no-ops (the text is already accumulated on the line), so N tokens cause
144
+ * at most one render per frame instead of N.
145
+ */
146
+ private scheduleBump;
147
+ /** (5.8) Drop a queued delta frame if one is pending. */
148
+ private cancelPendingBump;
149
+ /**
150
+ * (5.8) Immediate notify that first drops any pending coalesced delta frame.
151
+ * Used by every structural mutation (finished message, tool call/result,
152
+ * turn boundary, user action) so the UI never renders a structural change
153
+ * behind a stale token-stream frame, and a cancelled frame can't fire into a
154
+ * later epoch.
155
+ */
156
+ private forceFlush;
157
+ /**
158
+ * Push a standalone, immutable line and commit everything (it and any prior
159
+ * live tail are terminal-on-creation).
160
+ *
161
+ * Closing `assistantOpen` here means a standalone line pushed while a stream
162
+ * is open ends that stream: the next `assistant:delta` opens a FRESH line
163
+ * rather than appending to the committed one — so the committed line is
164
+ * genuinely final (append-only holds; see the interleaving regression test).
165
+ * That a mid-stream user submit / mode-cycle visually splits the assistant
166
+ * message is a pre-existing interaction behavior (NOT a v13 regression — the
167
+ * pre-v13 `pushUser` closed the stream the same way); richer mid-turn input
168
+ * handling (queueing) is a v15 interaction-model concern, out of v13 scope.
169
+ */
170
+ private pushCommitted;
171
+ pushUser(text: string): void;
172
+ pushInfo(text: string): void;
173
+ pushError(text: string): void;
174
+ /** Empties the rendered scrollback — used by `/clear` so the user sees a
175
+ * fresh view alongside the session.clearTurns() that drops the conversation
176
+ * history. The view-model and the underlying session are intentionally
177
+ * separate operations; the slash command sequences both.
178
+ *
179
+ * (5.8) Because Ink `<Static>` keeps everything ever handed to it, emptying
180
+ * `lines` is not enough — `clearGeneration` is bumped so app.tsx can remount
181
+ * `<Static>` under a fresh key. Any pending delta frame is dropped so it
182
+ * can't fire into the cleared epoch. */
183
+ clearLines(): void;
184
+ attach(bus: AppEventBus): void;
185
+ /**
186
+ * (3.4 — codex Round-2 MUST-FIX #2) Once a VM is detached (engine
187
+ * respawn via `/resume`, `/clear` shouldn't detach but if a future
188
+ * caller does), any in-flight `requestPermission` is rejected with
189
+ * a synthetic "denied" decision so the closing-over-stale-resolver
190
+ * call sites (notably the background-task child engine that holds
191
+ * `deps.gate` from before the respawn) don't hang waiting for a
192
+ * prompt that will never reach a user. Any *future* call to
193
+ * `requestPermission` on this detached VM short-circuits the same
194
+ * way — same reasoning.
195
+ *
196
+ * (5.8) Also drops any pending coalesced frame so a scheduled bump
197
+ * can't fire into a remounted/stale epoch.
198
+ */
199
+ detach(): void;
200
+ private detached;
201
+ /** Returned to the PermissionGate as its resolver. After `detach()`,
202
+ * any call resolves denied synchronously — see `detach()` docstring. */
203
+ requestPermission: (req: PermissionRequest) => Promise<PermissionDecision>;
204
+ /**
205
+ * (5.3) Resolve the open permission prompt with a full decision:
206
+ * - `{allow:true}` — approve once (not remembered).
207
+ * - `{allow:true, remember:true}` — approve & don't ask again (the gate
208
+ * caches it even for un-scoped tools like file-write).
209
+ * - `{allow:false}` — plain deny.
210
+ * - `{allow:false, feedback}` — deny + relay the user's note to the model.
211
+ */
212
+ resolvePermission(decision: AuthorizationDecision): void;
213
+ /**
214
+ * (5.10b) Resolve an open QUESTION prompt with the user's answers (or a
215
+ * decline). Mirrors {@link resolvePermission} for the `question` pending
216
+ * variant — the `ask_user_question` tool turns the decision into the
217
+ * model-facing tool result.
218
+ */
219
+ resolveQuestion(decision: QuestionDecision): void;
220
+ /** The current session approval mode (`"default"` when unwired). */
221
+ get approvalMode(): ApprovalMode;
222
+ /**
223
+ * Shift+Tab handler. Advances the cycle; cycling INTO `yolo` without a prior
224
+ * confirmation opens the confirm overlay instead of switching (the typed
225
+ * `/approval yolo` path counts as consent and skips this — see
226
+ * `setApprovalMode`).
227
+ */
228
+ cycleApprovalMode(): void;
229
+ /** Resolve the yolo confirm overlay. `confirm` latches yolo for the session. */
230
+ resolveYoloConfirm(confirm: boolean): void;
231
+ }
232
+ export {};
233
+ //# sourceMappingURL=view-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-model.d.ts","sourceRoot":"","sources":["../src/view-model.ts"],"names":[],"mappings":"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"}