indusagi 0.12.33 → 0.13.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/CHANGELOG.md +11 -0
- package/dist/agent.js +1247 -184
- package/dist/ai.js +72 -4
- package/dist/capabilities.js +69 -2
- package/dist/cli.js +1353 -29
- package/dist/connectors-saas.js +66 -0
- package/dist/index.js +1353 -29
- package/dist/interop.js +66 -0
- package/dist/mcp.js +270 -363
- package/dist/react-ink.js +1391 -41
- package/dist/shell-app.js +1353 -29
- package/dist/smithy.js +69 -2
- package/dist/swarm.js +69 -2
- package/dist/types/capabilities/backends/node-backends.d.ts +3 -1
- package/dist/types/capabilities/files/read-state-gate.d.ts +69 -0
- package/dist/types/capabilities/files/read-state-gate.test.d.ts +14 -0
- package/dist/types/capabilities/kernel/context.d.ts +4 -0
- package/dist/types/capabilities/kernel/index.d.ts +2 -2
- package/dist/types/capabilities/kernel/spec.d.ts +55 -0
- package/dist/types/facade/bot/actions/bash.d.ts +15 -0
- package/dist/types/facade/bot/actions/bash.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/checkpoint.d.ts +49 -0
- package/dist/types/facade/bot/actions/checkpoint.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/edit-utils.d.ts +86 -0
- package/dist/types/facade/bot/actions/edit.d.ts +18 -0
- package/dist/types/facade/bot/actions/edit.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/find.d.ts +2 -0
- package/dist/types/facade/bot/actions/find.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/grep.d.ts +10 -0
- package/dist/types/facade/bot/actions/grep.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/index.d.ts +16 -0
- package/dist/types/facade/bot/actions/read-state.d.ts +83 -0
- package/dist/types/facade/bot/actions/read-state.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/read.d.ts +7 -0
- package/dist/types/facade/bot/actions/read.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/sandbox-backend.d.ts +99 -0
- package/dist/types/facade/bot/actions/sandbox-backend.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/websearch.d.ts +5 -2
- package/dist/types/facade/bot/actions/websearch.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/write.d.ts +15 -0
- package/dist/types/facade/bot/agent-loop.d.ts +10 -0
- package/dist/types/facade/bot/agent-loop.test.d.ts +1 -0
- package/dist/types/facade/bot/agent.d.ts +9 -1
- package/dist/types/facade/bot/permission-gate.test.d.ts +1 -0
- package/dist/types/facade/bot/types.d.ts +60 -0
- package/dist/types/facade/mcp-core/client.d.ts +71 -15
- package/dist/types/facade/mcp-core/client.test.d.ts +18 -0
- package/dist/types/facade/mcp-core/types.d.ts +10 -0
- package/dist/types/facade/ml/adapters/anthropic-retry.test.d.ts +1 -0
- package/dist/types/facade/ml/adapters/anthropic.d.ts +17 -0
- package/dist/types/facade/ml/adapters/simple-options.d.ts +13 -0
- package/dist/types/facade/ml/adapters/simple-options.test.d.ts +1 -0
- package/dist/types/react-ink/components/StatusLine.d.ts +10 -1
- package/dist/types/react-ink/components/ToolEventBlock.d.ts +9 -1
- package/dist/types/react-ink/components/ToolEventBlock.test.d.ts +1 -0
- package/dist/types/react-ink/components/dialogs/SelectableDialog.d.ts +7 -1
- package/dist/types/react-ink/components/dialogs/ThemeDialog.d.ts +21 -2
- package/dist/types/react-ink/diff/Diff.d.ts +22 -0
- package/dist/types/react-ink/diff/diff.test.d.ts +1 -0
- package/dist/types/react-ink/diff/structured.d.ts +41 -0
- package/dist/types/react-ink/diff/word-diff.d.ts +27 -0
- package/dist/types/react-ink/index.d.ts +8 -0
- package/dist/types/react-ink/markdown/Markdown.d.ts +23 -0
- package/dist/types/react-ink/markdown/MarkdownTable.d.ts +19 -0
- package/dist/types/react-ink/markdown/StreamingMarkdown.d.ts +34 -0
- package/dist/types/react-ink/markdown/format-token.d.ts +39 -0
- package/dist/types/react-ink/markdown/highlight.d.ts +31 -0
- package/dist/types/react-ink/theme-adapter.d.ts +58 -1
- package/dist/types/react-ink/utils/tool-display.d.ts +17 -1
- package/package.json +5 -1
|
@@ -4,7 +4,18 @@
|
|
|
4
4
|
* Manages connection to one MCP server and provides
|
|
5
5
|
* methods to list/call tools, read resources, etc.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Transport is provided by the official `@modelcontextprotocol/sdk`:
|
|
8
|
+
* - stdio → subprocess JSON-RPC over stdin/stdout
|
|
9
|
+
* - http → Streamable HTTP (POST for client→server, plus a server→client
|
|
10
|
+
* push channel — the SDK opens the SSE stream and a standalone GET
|
|
11
|
+
* SSE for server-initiated messages, and handles reconnect/
|
|
12
|
+
* session-expiry by re-using the session id)
|
|
13
|
+
* - sse → legacy HTTP+SSE transport (when `transport: "sse"` is requested)
|
|
14
|
+
*
|
|
15
|
+
* The SDK gives us a real persistent connection, a server→client push channel,
|
|
16
|
+
* dispatch of server NOTIFICATIONS, answering of server REQUESTS (elicitation,
|
|
17
|
+
* roots/list, sampling, ping), and bounded reconnect on a dropped stream /
|
|
18
|
+
* expired session — none of which the hand-rolled POST-only transport did.
|
|
8
19
|
*/
|
|
9
20
|
import type { MCPServerConfig, MCPToolDefinition, MCPResource, MCPPrompt, MCPToolCallResult, MCPLogHandler, MCPProgressHandler, MCPElicitationHandler, MCPRoot } from "./types.js";
|
|
10
21
|
/**
|
|
@@ -29,7 +40,7 @@ export interface MCPClientOptions {
|
|
|
29
40
|
/**
|
|
30
41
|
* MCP Client - manages connection to a single MCP server.
|
|
31
42
|
*
|
|
32
|
-
* Supports stdio
|
|
43
|
+
* Supports stdio (subprocess) and HTTP (Streamable HTTP / legacy SSE) transports.
|
|
33
44
|
* Provides methods to list tools, call tools, read resources, etc.
|
|
34
45
|
*
|
|
35
46
|
* @example
|
|
@@ -50,10 +61,8 @@ export interface MCPClientOptions {
|
|
|
50
61
|
*/
|
|
51
62
|
export declare class MCPClient {
|
|
52
63
|
private options;
|
|
53
|
-
private
|
|
54
|
-
private
|
|
55
|
-
private messageId;
|
|
56
|
-
private pendingRequests;
|
|
64
|
+
private client?;
|
|
65
|
+
private transport?;
|
|
57
66
|
private isConnected;
|
|
58
67
|
private connectionPromise;
|
|
59
68
|
private serverCapabilities?;
|
|
@@ -61,6 +70,13 @@ export declare class MCPClient {
|
|
|
61
70
|
private enableServerLogs;
|
|
62
71
|
private enableProgressTracking;
|
|
63
72
|
private _roots;
|
|
73
|
+
private elicitationHandler?;
|
|
74
|
+
private samplingHandler?;
|
|
75
|
+
private resourceUpdatedHandler?;
|
|
76
|
+
private resourceListChangedHandler?;
|
|
77
|
+
private toolListChangedHandler?;
|
|
78
|
+
private promptListChangedHandler?;
|
|
79
|
+
private progressHandler?;
|
|
64
80
|
/** Server name */
|
|
65
81
|
readonly serverName: string;
|
|
66
82
|
/** Server config */
|
|
@@ -74,10 +90,37 @@ export declare class MCPClient {
|
|
|
74
90
|
*/
|
|
75
91
|
connect(): Promise<void>;
|
|
76
92
|
private doConnect;
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Build the SDK transport for the configured server.
|
|
95
|
+
*
|
|
96
|
+
* - stdio: `StdioClientTransport` (inherits the parent env, merges config.env).
|
|
97
|
+
* - http: `StreamableHTTPClientTransport` (POST + server-push SSE channel,
|
|
98
|
+
* reconnect + session-expiry handled by the SDK), unless the config
|
|
99
|
+
* asks for the legacy `SSEClientTransport`.
|
|
100
|
+
*/
|
|
101
|
+
private createTransport;
|
|
102
|
+
/**
|
|
103
|
+
* Register handlers for server-initiated REQUESTS and NOTIFICATIONS.
|
|
104
|
+
*
|
|
105
|
+
* REQUESTS (have both `.method` AND `.id`) must be ANSWERED with a JSON-RPC
|
|
106
|
+
* RESPONSE carrying the matching id — the SDK does this automatically for the
|
|
107
|
+
* value a request handler returns (or rejects). The previous hand-rolled
|
|
108
|
+
* transport routed these into the notification path, so they were never
|
|
109
|
+
* answered (bug #13).
|
|
110
|
+
*
|
|
111
|
+
* Defaults:
|
|
112
|
+
* - ping → {}
|
|
113
|
+
* - roots/list → the configured roots
|
|
114
|
+
* - elicitation/create→ decline ({action:"cancel"}) unless a host handler is set
|
|
115
|
+
* - sampling → reject "Method not found" unless a host handler is set
|
|
116
|
+
*/
|
|
117
|
+
private registerServerHandlers;
|
|
79
118
|
/**
|
|
80
119
|
* Disconnect from the MCP server.
|
|
120
|
+
*
|
|
121
|
+
* The SDK's `client.close()` closes the transport and rejects any pending
|
|
122
|
+
* requests — a clean teardown for both stdio (graceful subprocess shutdown)
|
|
123
|
+
* and HTTP (close the push channel / end the session).
|
|
81
124
|
*/
|
|
82
125
|
disconnect(): Promise<void>;
|
|
83
126
|
/**
|
|
@@ -134,24 +177,37 @@ export declare class MCPClient {
|
|
|
134
177
|
* Set a handler for resource list changed notifications.
|
|
135
178
|
*/
|
|
136
179
|
setResourceListChangedHandler(handler: () => void): void;
|
|
180
|
+
/**
|
|
181
|
+
* Set a handler for tool list changed notifications.
|
|
182
|
+
*/
|
|
183
|
+
setToolListChangedHandler(handler: () => void): void;
|
|
137
184
|
/**
|
|
138
185
|
* Set a handler for prompt list changed notifications.
|
|
139
186
|
*/
|
|
140
187
|
setPromptListChangedHandler(handler: () => void): void;
|
|
141
188
|
/**
|
|
142
|
-
* Set a handler for elicitation requests.
|
|
189
|
+
* Set a handler for elicitation requests. When set, the server's
|
|
190
|
+
* `elicitation/create` REQUEST is answered with the host's decision; when
|
|
191
|
+
* unset the request is declined ({action:"cancel"}).
|
|
143
192
|
*/
|
|
144
193
|
setElicitationHandler(handler: MCPElicitationHandler): void;
|
|
194
|
+
/**
|
|
195
|
+
* Set a handler for sampling (`sampling/createMessage`) requests. When set,
|
|
196
|
+
* the server request is answered with the host's result; when unset the
|
|
197
|
+
* request is rejected with "Method not found".
|
|
198
|
+
*/
|
|
199
|
+
setSamplingHandler(handler: (params: unknown) => Promise<unknown>): void;
|
|
145
200
|
/**
|
|
146
201
|
* Set a handler for progress notifications.
|
|
147
202
|
*/
|
|
148
203
|
setProgressHandler(handler: MCPProgressHandler): void;
|
|
149
204
|
private ensureConnected;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Normalize an SDK/transport error into an MCPError. Session-expiry style
|
|
207
|
+
* failures (404 session / -32001) are surfaced as SESSION_ERROR so callers
|
|
208
|
+
* (the pool) can decide to reconnect; the SDK's StreamableHTTP transport
|
|
209
|
+
* already attempts a bounded reconnect re-using the session id before this.
|
|
210
|
+
*/
|
|
211
|
+
private wrapError;
|
|
156
212
|
private log;
|
|
157
213
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP client transport tests (#23 / bug #13).
|
|
3
|
+
*
|
|
4
|
+
* These verify the server→client directions that the old hand-rolled,
|
|
5
|
+
* POST-only transport never handled:
|
|
6
|
+
* 1. A server-initiated REQUEST (`elicitation/create`) is ANSWERED with a
|
|
7
|
+
* JSON-RPC RESPONSE carrying the matching id (not silently dropped into a
|
|
8
|
+
* notification path).
|
|
9
|
+
* 2. A server-initiated NOTIFICATION (`notifications/tools/list_changed`)
|
|
10
|
+
* fires the host-registered handler.
|
|
11
|
+
* 3. The HTTP path builds a real Streamable-HTTP transport with a server-push
|
|
12
|
+
* channel, and an SSE-framed server message off that channel is parsed and
|
|
13
|
+
* dispatched to the right handler.
|
|
14
|
+
*
|
|
15
|
+
* A `MockTransport` (implementing the SDK `Transport` interface) drives the
|
|
16
|
+
* client without a live MCP server.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
@@ -27,6 +27,12 @@ export interface HttpServerConfig {
|
|
|
27
27
|
url: URL;
|
|
28
28
|
/** Optional headers for HTTP requests */
|
|
29
29
|
headers?: Record<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* Which HTTP transport to use.
|
|
32
|
+
* - "http" (default): Streamable HTTP (single endpoint, server-push channel)
|
|
33
|
+
* - "sse": legacy HTTP+SSE transport
|
|
34
|
+
*/
|
|
35
|
+
transport?: "http" | "sse";
|
|
30
36
|
/** Custom fetch implementation */
|
|
31
37
|
fetch?: typeof fetch;
|
|
32
38
|
}
|
|
@@ -166,6 +172,8 @@ export interface MCPServerConfigEntry {
|
|
|
166
172
|
url?: string;
|
|
167
173
|
/** Headers for HTTP transport */
|
|
168
174
|
headers?: Record<string, string>;
|
|
175
|
+
/** HTTP transport flavor ("http" = Streamable HTTP default, "sse" = legacy) */
|
|
176
|
+
transport?: "http" | "sse";
|
|
169
177
|
/** Timeout in milliseconds */
|
|
170
178
|
timeout?: number;
|
|
171
179
|
/** Whether this server is enabled (default: true) */
|
|
@@ -185,6 +193,8 @@ export interface MCPServerConfigValue {
|
|
|
185
193
|
url?: string;
|
|
186
194
|
/** Headers for HTTP transport */
|
|
187
195
|
headers?: Record<string, string>;
|
|
196
|
+
/** HTTP transport flavor ("http" = Streamable HTTP default, "sse" = legacy) */
|
|
197
|
+
transport?: "http" | "sse";
|
|
188
198
|
/** Timeout in milliseconds */
|
|
189
199
|
timeout?: number;
|
|
190
200
|
/** Whether this server is enabled (default: true) */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -57,5 +57,22 @@ export declare class AnthropicStreamHandler extends BaseStreamHandler {
|
|
|
57
57
|
*/
|
|
58
58
|
process(anthropicStream: AsyncIterable<unknown>, onEvent: (event: any) => void): Promise<void>;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Reads a `Retry-After` header off an Anthropic APIError and converts it to a
|
|
62
|
+
* delay in milliseconds. The header is the integer-seconds form per the
|
|
63
|
+
* Anthropic API; a missing/non-numeric value yields `null` so callers fall
|
|
64
|
+
* back to exponential backoff.
|
|
65
|
+
*/
|
|
66
|
+
export declare function parseAnthropicRetryAfterMs(error: unknown): number | null;
|
|
67
|
+
/**
|
|
68
|
+
* Classifies an Anthropic failure as worth retrying. Mirrors
|
|
69
|
+
* `kit/provider-errors.ts:isRetryableError`: transient HTTP statuses (408/409/
|
|
70
|
+
* 429/529/5xx) and an `overloaded_error` body are retryable; auth/validation
|
|
71
|
+
* (401/403/4xx) are not. Generic errors fall back to a message sniff covering
|
|
72
|
+
* network/timeout/overload conditions. The `_attempt` is accepted to satisfy
|
|
73
|
+
* the RetryPolicy.shouldRetry contract but unused here (the loop already caps
|
|
74
|
+
* attempts).
|
|
75
|
+
*/
|
|
76
|
+
export declare function shouldRetryAnthropic(error: unknown, _attempt: number): boolean;
|
|
60
77
|
export declare const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOptions>;
|
|
61
78
|
export declare const streamSimpleAnthropic: StreamFunction<"anthropic-messages", SimpleStreamOptions>;
|
|
@@ -8,7 +8,20 @@ export interface RetryPolicy {
|
|
|
8
8
|
maxAttempts: number;
|
|
9
9
|
baseDelayMs: number;
|
|
10
10
|
maxDelayMs?: number;
|
|
11
|
+
/**
|
|
12
|
+
* When present, abort is checked between attempts and before each backoff
|
|
13
|
+
* sleep. A live request is short-circuited the moment its signal fires —
|
|
14
|
+
* but the presence of a signal no longer suppresses transient retries.
|
|
15
|
+
*/
|
|
16
|
+
signal?: AbortSignal;
|
|
11
17
|
shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the server-requested cooldown (e.g. a parsed `Retry-After`
|
|
20
|
+
* header) in milliseconds, or `null` when the error carries no such hint.
|
|
21
|
+
* When provided, it takes priority over exponential backoff (still clamped
|
|
22
|
+
* by `maxDelayMs`).
|
|
23
|
+
*/
|
|
24
|
+
getRetryAfterMs?: (error: unknown) => number | null;
|
|
12
25
|
}
|
|
13
26
|
export declare class SimpleOptionsProviderError extends Error {
|
|
14
27
|
readonly code: "rate_limit" | "timeout" | "network" | "validation" | "auth" | "unknown";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,6 +4,15 @@ interface StatusLineProps {
|
|
|
4
4
|
theme: InkThemeAdapter;
|
|
5
5
|
status?: StatusMessage;
|
|
6
6
|
snapshot: SessionSnapshot;
|
|
7
|
+
/**
|
|
8
|
+
* Whether this line draws its own "Agent working..." / "Running bash..." /
|
|
9
|
+
* "Compacting..." busy fallbacks. Defaults to `true` (legacy behaviour). A host
|
|
10
|
+
* that renders its own live progress affordance (e.g. an animated working
|
|
11
|
+
* indicator on a separate row) passes `false` so the busy state is shown in ONE
|
|
12
|
+
* place only — this line then carries just the explicit transient toast (and a
|
|
13
|
+
* hard error), and never competes with the host's indicator.
|
|
14
|
+
*/
|
|
15
|
+
showBusyText?: boolean;
|
|
7
16
|
}
|
|
8
|
-
export declare function StatusLine({ snapshot, status, theme }: StatusLineProps): JSX.Element | null;
|
|
17
|
+
export declare function StatusLine({ snapshot, status, theme, showBusyText }: StatusLineProps): JSX.Element | null;
|
|
9
18
|
export {};
|
|
@@ -12,6 +12,14 @@ interface ToolEventBlockProps {
|
|
|
12
12
|
showTitle?: boolean;
|
|
13
13
|
showSummaryInline?: boolean;
|
|
14
14
|
maxContentLines?: number;
|
|
15
|
+
/**
|
|
16
|
+
* When true the detail body already carries syntax-highlight ANSI; render it
|
|
17
|
+
* verbatim instead of stripping the escapes. stripAnsi is still used for the
|
|
18
|
+
* visible-width / line-count measurements so wrapping and clamping stay
|
|
19
|
+
* correct against the escape-free text.
|
|
20
|
+
*/
|
|
21
|
+
preformatted?: boolean;
|
|
15
22
|
}
|
|
16
|
-
export declare function
|
|
23
|
+
export declare function statusColorKey(status: ToolEventStatus): string;
|
|
24
|
+
export declare function ToolEventBlock({ detail, emptyText, indent, marginBottom, maxContentLines, preformatted, showSummaryInline, showTitle, status, summary, theme, title, }: ToolEventBlockProps): JSX.Element;
|
|
17
25
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -9,8 +9,14 @@ interface SelectableDialogProps<T> {
|
|
|
9
9
|
noSearchResultsText?: string;
|
|
10
10
|
onClose: () => void;
|
|
11
11
|
onSelect: (item: T) => void | Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Fired whenever the highlighted item changes (including on first mount), so a
|
|
14
|
+
* caller can live-preview the currently-focused choice before it is committed
|
|
15
|
+
* with Enter. Distinct from {@link onSelect}, which fires only on Enter.
|
|
16
|
+
*/
|
|
17
|
+
onHighlight?: (item: T, index: number) => void;
|
|
12
18
|
getSearchText?: (item: T) => string;
|
|
13
19
|
renderItem: (item: T, selected: boolean, index: number) => ReactNode;
|
|
14
20
|
}
|
|
15
|
-
export declare function SelectableDialog<T>({ title, subtitle, items, isActive, searchEnabled, emptyText, noSearchResultsText, onClose, onSelect, getSearchText, renderItem, }: SelectableDialogProps<T>): JSX.Element;
|
|
21
|
+
export declare function SelectableDialog<T>({ title, subtitle, items, isActive, searchEnabled, emptyText, noSearchResultsText, onClose, onSelect, onHighlight, getSearchText, renderItem, }: SelectableDialogProps<T>): JSX.Element;
|
|
16
22
|
export {};
|
|
@@ -1,7 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single selectable scheme row. The `id` is the scheme token committed/
|
|
3
|
+
* previewed; `label` and `description` are the human-readable strings shown.
|
|
4
|
+
*/
|
|
5
|
+
export interface ThemeDialogItem {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
}
|
|
1
10
|
interface ThemeDialogProps {
|
|
2
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The schemes to offer. Either bare scheme tokens (label == token) or rich
|
|
13
|
+
* items carrying a friendly label + description.
|
|
14
|
+
*/
|
|
15
|
+
themes: Array<string | ThemeDialogItem>;
|
|
3
16
|
onClose: () => void;
|
|
17
|
+
/** Fired on Enter: the chosen scheme token. */
|
|
4
18
|
onSelect: (themeName: string) => void | Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Fired on every highlight move (and on mount): the focused scheme token, so
|
|
21
|
+
* the caller can live-preview a scheme before it is committed.
|
|
22
|
+
*/
|
|
23
|
+
onHighlight?: (themeName: string) => void;
|
|
5
24
|
}
|
|
6
|
-
export declare function ThemeDialog({ themes, onClose, onSelect }: ThemeDialogProps): JSX.Element;
|
|
25
|
+
export declare function ThemeDialog({ themes, onClose, onSelect, onHighlight }: ThemeDialogProps): JSX.Element;
|
|
7
26
|
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from "indusagi/react-host";
|
|
2
|
+
import type { InkThemeAdapter } from "../theme-adapter.js";
|
|
3
|
+
import type { StructuredDiff } from "./structured.js";
|
|
4
|
+
interface DiffProps {
|
|
5
|
+
diff: StructuredDiff;
|
|
6
|
+
theme: InkThemeAdapter;
|
|
7
|
+
/** Left margin (columns) applied to the whole block. */
|
|
8
|
+
indent?: number;
|
|
9
|
+
marginBottom?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A minimal, pure-Ink colored diff block.
|
|
13
|
+
*
|
|
14
|
+
* Renders each hunk's classified lines with a right-aligned line-number gutter,
|
|
15
|
+
* themed add/remove backgrounds, and word-level intra-line emphasis (only the
|
|
16
|
+
* changed sub-ranges are bolded; suppressed above the change threshold upstream
|
|
17
|
+
* in {@link buildStructuredDiff}). Multiple hunks are separated by a dim `...`
|
|
18
|
+
* marker. No state, no effects — colors survive to the terminal because nothing
|
|
19
|
+
* strips the ANSI on this path.
|
|
20
|
+
*/
|
|
21
|
+
export declare function Diff({ diff, theme, indent, marginBottom }: DiffProps): ReactNode;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type WordSpan } from "./word-diff.js";
|
|
2
|
+
/** Lines of unchanged context kept around each change, matching git's default. */
|
|
3
|
+
export declare const CONTEXT_LINES = 3;
|
|
4
|
+
/** A single rendered diff line, already classified and numbered. */
|
|
5
|
+
export interface DiffLine {
|
|
6
|
+
kind: "context" | "added" | "removed";
|
|
7
|
+
/** The old-file line number, or undefined for added lines. */
|
|
8
|
+
oldLine?: number;
|
|
9
|
+
/** The new-file line number, or undefined for removed lines. */
|
|
10
|
+
newLine?: number;
|
|
11
|
+
/** The line text without its `+`/`-`/space diff prefix. */
|
|
12
|
+
text: string;
|
|
13
|
+
/**
|
|
14
|
+
* Word-level segments for intra-line highlighting. Present only on added /
|
|
15
|
+
* removed lines that were paired with their counterpart; context lines and
|
|
16
|
+
* unpaired changes leave this undefined (render the whole line one colour).
|
|
17
|
+
*/
|
|
18
|
+
spans?: WordSpan[];
|
|
19
|
+
}
|
|
20
|
+
/** One contiguous change region: classified lines plus a hunk header marker. */
|
|
21
|
+
export interface DiffHunk {
|
|
22
|
+
oldStart: number;
|
|
23
|
+
newStart: number;
|
|
24
|
+
lines: DiffLine[];
|
|
25
|
+
}
|
|
26
|
+
export interface StructuredDiff {
|
|
27
|
+
hunks: DiffHunk[];
|
|
28
|
+
addedCount: number;
|
|
29
|
+
removedCount: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build a structured diff between `oldStr` and `newStr` using the `diff`
|
|
33
|
+
* package's {@link structuredPatch} with {@link CONTEXT_LINES} of context.
|
|
34
|
+
*
|
|
35
|
+
* The raw hunk `lines` (prefixed `+`/`-`/space) are walked into typed
|
|
36
|
+
* {@link DiffLine}s carrying gutter line numbers. Adjacent removed→added runs
|
|
37
|
+
* are paired index-for-index and handed to {@link wordDiffLine} so the renderer
|
|
38
|
+
* can emphasise only the changed sub-range of each line. Returns `null` when
|
|
39
|
+
* there is nothing to show (identical input or an empty patch).
|
|
40
|
+
*/
|
|
41
|
+
export declare function buildStructuredDiff(oldStr: string, newStr: string, filePath?: string): StructuredDiff | null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Above this fraction of a line changing, intra-line word highlighting reads as
|
|
3
|
+
* noise — almost every span is "changed", so a flat whole-line colour is
|
|
4
|
+
* clearer. Below it we paint only the genuinely altered sub-ranges brighter.
|
|
5
|
+
*/
|
|
6
|
+
export declare const CHANGE_THRESHOLD = 0.4;
|
|
7
|
+
/** One contiguous run of a removed/added line, flagged whether it changed. */
|
|
8
|
+
export interface WordSpan {
|
|
9
|
+
text: string;
|
|
10
|
+
/** True when this run differs between the old and new line. */
|
|
11
|
+
changed: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Pair a removed line with the added line that replaced it and return the
|
|
15
|
+
* per-segment word diff for ONE side.
|
|
16
|
+
*
|
|
17
|
+
* `side` selects which line's segments we want: a removed-line render wants the
|
|
18
|
+
* segments present in the old text (unchanged + removed), an added-line render
|
|
19
|
+
* wants the segments present in the new text (unchanged + added). The other
|
|
20
|
+
* side's segments are dropped so the caller paints exactly the line it is on.
|
|
21
|
+
*
|
|
22
|
+
* Word highlighting is suppressed — the whole line is returned as a single
|
|
23
|
+
* `changed: true` span — when the changed character fraction exceeds
|
|
24
|
+
* {@link CHANGE_THRESHOLD}, where scattered word spans read worse than a flat
|
|
25
|
+
* line colour. The returned spans always concatenate back to the original line.
|
|
26
|
+
*/
|
|
27
|
+
export declare function wordDiffLine(oldLine: string, newLine: string, side: "removed" | "added"): WordSpan[];
|
|
@@ -29,6 +29,14 @@ export * from "./components/messages/SkillInvocationMessage.js";
|
|
|
29
29
|
export * from "./components/messages/ToolCallMessage.js";
|
|
30
30
|
export * from "./components/messages/ToolResultBlock.js";
|
|
31
31
|
export * from "./components/messages/UserMessage.js";
|
|
32
|
+
export * from "./diff/Diff.js";
|
|
33
|
+
export * from "./diff/structured.js";
|
|
34
|
+
export * from "./diff/word-diff.js";
|
|
35
|
+
export * from "./markdown/Markdown.js";
|
|
36
|
+
export * from "./markdown/MarkdownTable.js";
|
|
37
|
+
export * from "./markdown/StreamingMarkdown.js";
|
|
38
|
+
export * from "./markdown/format-token.js";
|
|
39
|
+
export * from "./markdown/highlight.js";
|
|
32
40
|
export * from "./utils/message-groups.js";
|
|
33
41
|
export * from "./utils/selection-dialog.js";
|
|
34
42
|
export * from "./utils/tool-display.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReactNode } from "indusagi/react-host";
|
|
2
|
+
import type { InkThemeAdapter } from "../theme-adapter.js";
|
|
3
|
+
interface MarkdownProps {
|
|
4
|
+
/** The raw markdown source to render. */
|
|
5
|
+
children: string;
|
|
6
|
+
/** The theme adapter colours resolve against. */
|
|
7
|
+
theme: InkThemeAdapter;
|
|
8
|
+
/** When false, fenced code is rendered plain (no syntax highlighting). */
|
|
9
|
+
highlightCode?: boolean;
|
|
10
|
+
/** Render every text run dim (used for de-emphasised content). */
|
|
11
|
+
dim?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Render markdown as styled terminal output.
|
|
15
|
+
*
|
|
16
|
+
* Tables become real flexbox columns ({@link MarkdownTable}); every other token
|
|
17
|
+
* is formatted to a chalk-ANSI string via {@link formatToken} and accumulated
|
|
18
|
+
* into a single `<Text>` run. Ink passes embedded ANSI straight to the terminal,
|
|
19
|
+
* so the chalk escapes render as colour. The `theme` is passed explicitly — the
|
|
20
|
+
* rebuild has no `useTheme()` hook.
|
|
21
|
+
*/
|
|
22
|
+
export declare function Markdown({ children, theme, highlightCode, dim }: MarkdownProps): ReactNode;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Tokens } from "marked";
|
|
2
|
+
import type { InkThemeAdapter } from "../theme-adapter.js";
|
|
3
|
+
import type { CliHighlight } from "./highlight.js";
|
|
4
|
+
interface MarkdownTableProps {
|
|
5
|
+
token: Tokens.Table;
|
|
6
|
+
theme: InkThemeAdapter;
|
|
7
|
+
highlight?: CliHighlight | null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Render a GFM table token as aligned flexbox columns.
|
|
11
|
+
*
|
|
12
|
+
* Each column's width is measured from the stripAnsi'd display text (so embedded
|
|
13
|
+
* ANSI styling does not inflate the column), the header row is bold, a dash rule
|
|
14
|
+
* separates header from body, and every cell is padded per its column alignment.
|
|
15
|
+
* Cells render as `<Box>` columns inside `<Box flexDirection="row">` rows, so Ink
|
|
16
|
+
* lays them out as real columns rather than a raw `| a | b |` pipe string.
|
|
17
|
+
*/
|
|
18
|
+
export declare function MarkdownTable({ token, theme, highlight }: MarkdownTableProps): JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReactNode } from "indusagi/react-host";
|
|
2
|
+
import type { InkThemeAdapter } from "../theme-adapter.js";
|
|
3
|
+
interface StreamingMarkdownProps {
|
|
4
|
+
/** The accumulated answer text so far (grows monotonically per delta). */
|
|
5
|
+
children: string;
|
|
6
|
+
/** The theme adapter colours resolve against. */
|
|
7
|
+
theme: InkThemeAdapter;
|
|
8
|
+
/** When false, fenced code is rendered plain (no syntax highlighting). */
|
|
9
|
+
highlightCode?: boolean;
|
|
10
|
+
/** Render every text run dim. */
|
|
11
|
+
dim?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Streaming-aware markdown renderer with a monotonic stable-prefix boundary.
|
|
15
|
+
*
|
|
16
|
+
* On every delta the accumulated answer grows. Re-lexing the whole answer each
|
|
17
|
+
* time would re-parse (and reflow/flicker) content that is already settled. To
|
|
18
|
+
* avoid that, we split the answer at the last completed block boundary:
|
|
19
|
+
*
|
|
20
|
+
* - **stable prefix** — every block above the boundary. Memoised on its own
|
|
21
|
+
* content, so once a block settles its `<Markdown>` is parsed once and never
|
|
22
|
+
* re-renders as later deltas arrive.
|
|
23
|
+
* - **unstable suffix** — the still-growing last block. Re-lexed every delta
|
|
24
|
+
* (cheap: it is one block, and `Markdown`'s own LRU cache + fast-path absorb
|
|
25
|
+
* the cost).
|
|
26
|
+
*
|
|
27
|
+
* The boundary only ever moves forward (it is recomputed from `content`, which
|
|
28
|
+
* grows monotonically, and `lastStableBoundary` is monotonic in its input), so
|
|
29
|
+
* completed blocks never re-flicker. When the turn ends and the finalized
|
|
30
|
+
* assistant message takes over rendering, the same `<Markdown>` pipeline draws
|
|
31
|
+
* identical output, so the hand-off is seamless.
|
|
32
|
+
*/
|
|
33
|
+
export declare function StreamingMarkdown({ children, theme, highlightCode, dim, }: StreamingMarkdownProps): ReactNode;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type Token } from "marked";
|
|
2
|
+
import type { InkThemeAdapter } from "../theme-adapter.js";
|
|
3
|
+
import type { CliHighlight } from "./highlight.js";
|
|
4
|
+
/**
|
|
5
|
+
* Configure the shared `marked` instance exactly once. The only customisation is
|
|
6
|
+
* disabling strikethrough: models frequently write `~` for "approximate"
|
|
7
|
+
* (e.g. `~100`) and almost never intend an actual strike, so we keep `~` literal.
|
|
8
|
+
*/
|
|
9
|
+
export declare function configureMarked(): void;
|
|
10
|
+
/**
|
|
11
|
+
* Fast structural check for whether `text` contains any markdown syntax. Samples
|
|
12
|
+
* the first 500 chars — markdown markers (headings, fences, list bullets) cluster
|
|
13
|
+
* at the start, while long plain tails (tool output) stay markdown-free.
|
|
14
|
+
*/
|
|
15
|
+
export declare function hasMarkdownSyntax(text: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Lex `content` into marked tokens, hitting the LRU cache and the plain-text
|
|
18
|
+
* fast-path. Plain text (no markdown syntax) returns a single synthetic
|
|
19
|
+
* paragraph token and is intentionally not cached — reconstruction is one
|
|
20
|
+
* allocation and caching would retain the content for no benefit.
|
|
21
|
+
*/
|
|
22
|
+
export declare function cachedLexer(content: string): Token[];
|
|
23
|
+
/**
|
|
24
|
+
* Render a single marked token to a chalk-ANSI string, painting via the
|
|
25
|
+
* framework {@link InkThemeAdapter}. Tables are handled out-of-band by the
|
|
26
|
+
* `Markdown` component (rendered as flexbox), so this returns an empty string
|
|
27
|
+
* for `table` tokens — the caller never funnels a table through here.
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatToken(token: Token, theme: InkThemeAdapter, highlight?: CliHighlight | null, listDepth?: number, orderedListNumber?: number | null, parent?: Token | null): string;
|
|
30
|
+
/** Pick the list-marker style for a depth: 1./2. → a./b. → i./ii. → numeric. */
|
|
31
|
+
export declare function getListNumber(listDepth: number, orderedListNumber: number): string;
|
|
32
|
+
/**
|
|
33
|
+
* Pad `content` to `targetWidth` per alignment. `displayWidth` is the visible
|
|
34
|
+
* width of `content` (caller computes it on stripAnsi'd text so embedded ANSI
|
|
35
|
+
* codes do not skew the padding).
|
|
36
|
+
*/
|
|
37
|
+
export declare function padAligned(content: string, displayWidth: number, targetWidth: number, align: "left" | "center" | "right" | null | undefined): string;
|
|
38
|
+
/** Measure a string's visible terminal width (ANSI-aware), for table layout. */
|
|
39
|
+
export declare function stringWidth(text: string): number;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { InkThemeAdapter } from "../theme-adapter.js";
|
|
2
|
+
/**
|
|
3
|
+
* The shape {@link formatToken} (and the tool-body highlighter) consume. It is a
|
|
4
|
+
* deliberately small surface: ask whether a language is known, then hand back a
|
|
5
|
+
* chalk-ANSI rendering of the code. Keeping it behind an interface means the
|
|
6
|
+
* markdown formatter never imports highlight.js directly and a caller can pass
|
|
7
|
+
* `null` to disable highlighting entirely.
|
|
8
|
+
*/
|
|
9
|
+
export interface CliHighlight {
|
|
10
|
+
/** Whether highlight.js has a grammar registered for `language`. */
|
|
11
|
+
supportsLanguage: (language: string) => boolean;
|
|
12
|
+
/** Render `code` as a chalk-ANSI string, colouring scopes via the theme. */
|
|
13
|
+
highlight: (code: string, options: {
|
|
14
|
+
language: string;
|
|
15
|
+
}) => string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build a {@link CliHighlight} bound to a theme. The returned object paints each
|
|
19
|
+
* highlight.js scope via the theme's syntax roles ({@link ThemeRole}); unknown
|
|
20
|
+
* languages and any internal failure fall back to the raw code, never throwing.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createHighlighter(theme: InkThemeAdapter): CliHighlight;
|
|
23
|
+
/**
|
|
24
|
+
* Highlight `code` keyed on a file path's extension. Used by the read/write/edit
|
|
25
|
+
* tool bodies, which know the file but not a fenced ```lang tag. Resolves the
|
|
26
|
+
* extension to a highlight.js language, falling back to the plain code when the
|
|
27
|
+
* extension is unknown or unsupported.
|
|
28
|
+
*/
|
|
29
|
+
export declare function highlightByPath(code: string, filePath: string, theme: InkThemeAdapter): string;
|
|
30
|
+
/** Resolve a file path to a highlight.js language id, or null when unknown. */
|
|
31
|
+
export declare function languageFromPath(filePath: string): string | null;
|