experimental-ash 0.54.0 → 0.55.1

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 (80) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/docs/public/advanced/cli-build-and-debugging.md +1 -1
  3. package/dist/docs/public/advanced/dev-tui.md +202 -0
  4. package/dist/docs/public/advanced/meta.json +1 -0
  5. package/dist/docs/public/advanced/vercel-deployment.md +8 -4
  6. package/dist/docs/public/getting-started.mdx +1 -1
  7. package/dist/docs/public/human-in-the-loop.mdx +4 -4
  8. package/dist/docs/public/onboarding.md +2 -2
  9. package/dist/docs/public/sandbox.md +12 -6
  10. package/dist/src/cli/dev/{repl.d.ts → repl/repl.d.ts} +1 -1
  11. package/dist/src/cli/dev/{repl.js → repl/repl.js} +1 -1
  12. package/dist/src/cli/dev/tui/layout.d.ts +24 -0
  13. package/dist/src/cli/dev/tui/layout.js +3 -0
  14. package/dist/src/cli/dev/tui/markdown.d.ts +14 -0
  15. package/dist/src/cli/dev/tui/markdown.js +3 -0
  16. package/dist/src/cli/dev/tui/runner.d.ts +205 -0
  17. package/dist/src/cli/dev/tui/runner.js +1 -0
  18. package/dist/src/cli/dev/tui/terminal-frame-buffer.d.ts +21 -0
  19. package/dist/src/cli/dev/tui/terminal-frame-buffer.js +2 -0
  20. package/dist/src/cli/dev/tui/terminal-renderer.d.ts +111 -0
  21. package/dist/src/cli/dev/tui/terminal-renderer.js +12 -0
  22. package/dist/src/cli/dev/tui/terminal-text.d.ts +6 -0
  23. package/dist/src/cli/dev/tui/terminal-text.js +1 -0
  24. package/dist/src/cli/dev/tui/test/index.d.ts +10 -0
  25. package/dist/src/cli/dev/tui/test/index.js +1 -0
  26. package/dist/src/cli/dev/tui/test/mock-terminal.d.ts +28 -0
  27. package/dist/src/cli/dev/tui/test/mock-terminal.js +3 -0
  28. package/dist/src/cli/dev/tui/tui.d.ts +32 -0
  29. package/dist/src/cli/dev/tui/tui.js +1 -0
  30. package/dist/src/cli/dev/tui/types.d.ts +68 -0
  31. package/dist/src/cli/dev/tui/types.js +1 -0
  32. package/dist/src/cli/run.d.ts +66 -0
  33. package/dist/src/cli/run.js +2 -2
  34. package/dist/src/compiler/channel-instrumentation-types.js +1 -1
  35. package/dist/src/compiler/manifest.d.ts +3 -0
  36. package/dist/src/compiler/manifest.js +1 -1
  37. package/dist/src/compiler/workspace-resources.js +1 -1
  38. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  39. package/dist/src/evals/scorers/autoevals.js +1 -1
  40. package/dist/src/execution/node-step.js +1 -1
  41. package/dist/src/execution/sandbox/bindings/local.js +1 -1
  42. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  43. package/dist/src/execution/sandbox/ensure.js +1 -1
  44. package/dist/src/execution/sandbox/lazy-backend.js +1 -1
  45. package/dist/src/execution/sandbox/prewarm.d.ts +2 -2
  46. package/dist/src/execution/sandbox/prewarm.js +1 -1
  47. package/dist/src/harness/code-mode.js +1 -1
  48. package/dist/src/harness/tool-loop.js +1 -1
  49. package/dist/src/harness/workflow-stream-error.d.ts +29 -0
  50. package/dist/src/harness/workflow-stream-error.js +1 -0
  51. package/dist/src/internal/application/package.js +1 -1
  52. package/dist/src/internal/instrumentation.d.ts +1 -1
  53. package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
  54. package/dist/src/public/channels/ash.js +1 -1
  55. package/dist/src/public/channels/discord/discordChannel.d.ts +2 -1
  56. package/dist/src/public/channels/discord/discordChannel.js +1 -1
  57. package/dist/src/public/channels/discord/index.d.ts +4 -0
  58. package/dist/src/public/channels/index.d.ts +63 -1
  59. package/dist/src/public/channels/index.js +1 -1
  60. package/dist/src/public/channels/teams/index.d.ts +5 -0
  61. package/dist/src/public/channels/teams/teamsChannel.d.ts +2 -1
  62. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  63. package/dist/src/public/channels/telegram/index.d.ts +5 -0
  64. package/dist/src/public/channels/telegram/telegramChannel.d.ts +2 -1
  65. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  66. package/dist/src/public/definitions/sandbox-backend.d.ts +1 -1
  67. package/dist/src/public/instrumentation/index.d.ts +2 -61
  68. package/dist/src/public/instrumentation/index.js +1 -1
  69. package/dist/src/runtime/sandbox/keys.d.ts +7 -3
  70. package/dist/src/runtime/sandbox/keys.js +1 -1
  71. package/dist/src/runtime/sandbox/template-plan.d.ts +21 -0
  72. package/dist/src/runtime/sandbox/template-plan.js +1 -0
  73. package/dist/src/shared/sandbox-backend.d.ts +25 -2
  74. package/package.json +6 -1
  75. /package/dist/src/cli/dev/{input-requests.d.ts → repl/input-requests.d.ts} +0 -0
  76. /package/dist/src/cli/dev/{input-requests.js → repl/input-requests.js} +0 -0
  77. /package/dist/src/cli/dev/{input.d.ts → repl/input.d.ts} +0 -0
  78. /package/dist/src/cli/dev/{input.js → repl/input.js} +0 -0
  79. /package/dist/src/cli/dev/{terminal.d.ts → repl/terminal.d.ts} +0 -0
  80. /package/dist/src/cli/dev/{terminal.js → repl/terminal.js} +0 -0
@@ -0,0 +1,205 @@
1
+ import { type ConnectionAuthorizationOutcome, Client, ClientSession } from "#client/index.js";
2
+ import { type UIMessage, type UIMessageChunk } from "ai";
3
+ import type { AssistantResponseStatsMode, TerminalPartDisplayMode, TuiDisplayOptions } from "./types.js";
4
+ import { type TerminalInput, type TerminalOutput } from "./terminal-renderer.js";
5
+ export type AgentTUIStreamResult = {
6
+ uiMessageStream: AsyncIterable<UIMessageChunk> | ReadableStream<UIMessageChunk>;
7
+ message?: UIMessage;
8
+ abort?: () => void;
9
+ };
10
+ export type AgentTUIStreamOptions = {
11
+ messages: UIMessage[];
12
+ };
13
+ export type AgentTUISessionOptions = {
14
+ title?: string;
15
+ initialPrompt?: string;
16
+ submittedPrompt?: string;
17
+ waitForExit?: boolean;
18
+ continueSession?: boolean;
19
+ tools?: TerminalPartDisplayMode;
20
+ reasoning?: TerminalPartDisplayMode;
21
+ subagents?: TerminalPartDisplayMode;
22
+ connectionAuth?: TerminalPartDisplayMode;
23
+ assistantResponseStats?: AssistantResponseStatsMode;
24
+ contextSize?: number;
25
+ };
26
+ export type AgentTUIToolApprovalRequest = {
27
+ approvalId: string;
28
+ toolCallId: string;
29
+ toolName: string;
30
+ title?: string;
31
+ input: unknown;
32
+ providerExecuted?: boolean;
33
+ messageId: string;
34
+ partIndex: number;
35
+ };
36
+ export type AgentTUIToolApprovalResponse = {
37
+ approved: boolean;
38
+ reason?: string;
39
+ };
40
+ export type AgentTUIInputOption = {
41
+ id: string;
42
+ label: string;
43
+ description?: string;
44
+ style?: "primary" | "danger" | "default";
45
+ };
46
+ export type AgentTUIInputQuestion = {
47
+ requestId: string;
48
+ prompt: string;
49
+ display: "select" | "text";
50
+ options?: ReadonlyArray<AgentTUIInputOption>;
51
+ allowFreeform?: boolean;
52
+ };
53
+ export type AgentTUIInputQuestionResponse = {
54
+ optionId?: string;
55
+ text?: string;
56
+ };
57
+ export type AgentTUIRenderer = {
58
+ readPrompt?(options?: AgentTUISessionOptions): Promise<string | undefined>;
59
+ readToolApproval?(request: AgentTUIToolApprovalRequest, options?: AgentTUISessionOptions): Promise<AgentTUIToolApprovalResponse>;
60
+ readInputQuestion?(question: AgentTUIInputQuestion, options?: AgentTUISessionOptions): Promise<AgentTUIInputQuestionResponse | undefined>;
61
+ renderStream(result: AgentTUIStreamResult, options?: AgentTUISessionOptions): Promise<UIMessage | undefined>;
62
+ /**
63
+ * Out-of-band update for one child step (reasoning + message text) of a
64
+ * subagent dispatch. Called by the runner as child-session stream events
65
+ * arrive. The renderer renders this as a body section colored by the
66
+ * subagent palette.
67
+ */
68
+ upsertSubagentStep?(update: SubagentStepUpdate): void;
69
+ /**
70
+ * Out-of-band update for one child tool call of a subagent dispatch.
71
+ */
72
+ upsertSubagentTool?(update: SubagentToolUpdate): void;
73
+ /**
74
+ * Registers a tool call id as originating from a subagent's child
75
+ * session. The renderer must skip rendering parent UIMessage tool parts
76
+ * for these ids — they are surfaced via {@link upsertSubagentTool}
77
+ * instead.
78
+ */
79
+ markChildToolCallId?(callId: string): void;
80
+ /**
81
+ * Out-of-band update for one MCP connection authorization lifecycle.
82
+ * Called by the runner as `authorization.*` events arrive.
83
+ * The renderer renders this as a persistent body section per
84
+ * connection that transitions through `required` → `pending` →
85
+ * one of the terminal `ConnectionAuthorizationOutcome` states.
86
+ */
87
+ upsertConnectionAuth?(update: ConnectionAuthUpdate): void;
88
+ /**
89
+ * Sets the number of connections currently awaiting an OAuth
90
+ * callback. The renderer overrides its bottom status bar with a
91
+ * "waiting for connection authorization" hint while this is > 0,
92
+ * so the user understands the agent is parked, not hung.
93
+ */
94
+ setConnectionAuthPendingCount?(count: number): void;
95
+ /**
96
+ * Clears the rendered transcript and resets per-conversation display
97
+ * state, leaving the UI interactive on a fresh screen. Used by the
98
+ * `/new` command to start a new session with a clean slate.
99
+ */
100
+ reset?(): void;
101
+ /**
102
+ * Tears down interactive mode and restores the terminal, as if the user
103
+ * pressed Ctrl+C. Used by the `/exit` command.
104
+ */
105
+ shutdown?(): void;
106
+ };
107
+ export type AshTUIRunnerOptions = TuiDisplayOptions & {
108
+ session: ClientSession;
109
+ /**
110
+ * Optional client used to attach to child sessions for live subagent
111
+ * stream observation. When omitted, the TUI still shows the subagent
112
+ * section but cannot surface the subagent's reasoning / response /
113
+ * intermediate events — only the parent-stream `called` and
114
+ * `completed` transitions.
115
+ */
116
+ client?: Client;
117
+ renderer?: AgentTUIRenderer;
118
+ screen?: TerminalOutput;
119
+ userInput?: TerminalInput;
120
+ /**
121
+ * Formats an error thrown while dispatching a turn (the initial
122
+ * `session.send()` POST — e.g. a transport failure or a Vercel
123
+ * Deployment Protection challenge) into the text rendered in the
124
+ * inline error region. Defaults to the error's message. Callers that
125
+ * know about transport-specific challenges (the `ash dev` glue) inject
126
+ * a richer formatter here.
127
+ */
128
+ formatTransportError?: (error: unknown) => string;
129
+ };
130
+ export declare class AshTUIRunner {
131
+ #private;
132
+ constructor(options: AshTUIRunnerOptions);
133
+ run(): Promise<void>;
134
+ }
135
+ type ResponseMetadata = {
136
+ usage?: {
137
+ totalTokens?: number;
138
+ outputTokens?: number;
139
+ };
140
+ };
141
+ type SubagentChildStep = {
142
+ reasoning: string;
143
+ message: string;
144
+ finalized: boolean;
145
+ };
146
+ type SubagentToolState = {
147
+ toolName: string;
148
+ input: unknown;
149
+ status: "approval-requested" | "executing" | "done" | "failed";
150
+ output?: unknown;
151
+ errorText?: string;
152
+ };
153
+ export type SubagentRun = {
154
+ name: string;
155
+ /**
156
+ * One entry per logical "child message" — independent of the child's
157
+ * `stepIndex` field, which the harness can reuse across multiple
158
+ * assistant messages within a turn (e.g. a message before a tool call
159
+ * and another message after the tool result both arrive under
160
+ * `stepIndex: 0`). The key is a monotonic counter so each
161
+ * `message.completed` opens a new box on the next inbound delta.
162
+ */
163
+ steps: Map<number, SubagentChildStep>;
164
+ /**
165
+ * Section currently accepting reasoning/message deltas. `null` means
166
+ * the next delta opens a new section.
167
+ */
168
+ currentSectionKey: number | null;
169
+ /** Monotonic counter for new section keys. */
170
+ nextSectionKey: number;
171
+ tools: Map<string, SubagentToolState>;
172
+ };
173
+ export type SubagentStepUpdate = {
174
+ callId: string;
175
+ subagentName: string;
176
+ sectionKey: number;
177
+ reasoning: string;
178
+ message: string;
179
+ finalized: boolean;
180
+ };
181
+ export type SubagentToolUpdate = {
182
+ callId: string;
183
+ subagentName: string;
184
+ childCallId: string;
185
+ toolName: string;
186
+ input: unknown;
187
+ status: "approval-requested" | "executing" | "done" | "failed";
188
+ output?: unknown;
189
+ errorText?: string;
190
+ };
191
+ export type ConnectionAuthChallenge = {
192
+ url?: string;
193
+ userCode?: string;
194
+ expiresAt?: string;
195
+ instructions?: string;
196
+ };
197
+ export type ConnectionAuthState = "required" | "pending" | ConnectionAuthorizationOutcome;
198
+ export type ConnectionAuthUpdate = {
199
+ name: string;
200
+ description: string;
201
+ state: ConnectionAuthState;
202
+ challenge?: ConnectionAuthChallenge;
203
+ reason?: string;
204
+ };
205
+ export type { ResponseMetadata as _ResponseMetadata };
@@ -0,0 +1 @@
1
+ import{TerminalRenderer}from"./terminal-renderer.js";import{isCurrentTurnBoundaryEvent}from"#client/index.js";import{getToolName,isToolUIPart}from"ai";var AshTUIRunner=class{#e;#t;#n;#r;#i;#a;#o;#s;#c;#l;#u;#d=new Map;#f=new Map;#p=new Map;#m=new Map;#h=new Set;constructor(e){this.#e=e.session,e.client!==void 0&&(this.#t=e.client),this.#n=createRenderer(e)??createDefaultRenderer(e),this.#r=e.name??`Ash`,this.#i=e.tools??`full`,this.#a=e.reasoning??`full`,this.#o=e.subagents??`full`,this.#s=e.connectionAuth??`full`,this.#c=e.assistantResponseStats??`tokensPerSecond`,this.#l=e.contextSize,this.#u=e.formatTransportError??formatErrorMessage}async run(){let e=this.#r,t=[],n=0,generateMessageId=()=>`message-${++n}`,r,i,a=!1,o=!1;for(;;){if(!o){if(r==null){if(!this.#n.readPrompt){if(a)return;throw Error(`No prompt was provided and the renderer does not support prompt input.`)}try{r=await this.#n.readPrompt({title:e})}catch(e){if(isInterruptedError(e))return;throw e}if(r==null)return}let s=parsePromptCommand(r);if(s===`exit`){this.#n.shutdown?.();return}if(s===`new`){this.#g(),t.length=0,n=0,i=void 0,o=!1,r=void 0,this.#n.reset?.();continue}t.push(createUserMessage(generateMessageId(),r)),a=!0}let s=await this.#_({prompt:o?void 0:r,inputResponses:i,messages:t,generateMessageId});try{let n=await this.#n.renderStream(s,{title:e,submittedPrompt:r,continueSession:!!this.#n.readPrompt,tools:this.#i,reasoning:this.#a,subagents:this.#o,connectionAuth:this.#s,assistantResponseStats:this.#c,contextSize:this.#l,waitForExit:!1});if(n&&n.parts.length>0){let a=findPendingToolApprovalRequests(n),s=findPendingQuestionRequests(this.#d);if(a.length>0||s.length>0){let c=[];if(a.length>0){if(!this.#n.readToolApproval)throw Error(`Tool approval was requested, but the renderer does not support tool approval input.`);for(let t of a){let r=await this.#n.readToolApproval(t,{title:e});applyToolApprovalResponse(n,t,r);let i=this.#d.get(t.approvalId);i&&(c.push({requestId:i.requestId,optionId:r.approved?`approve`:`deny`}),this.#d.delete(t.approvalId))}}if(s.length>0){if(!this.#n.readInputQuestion)throw Error(`An interactive question was requested, but the renderer does not support input questions.`);for(let t of s){let n=toAgentTUIInputQuestion(t),r=await this.#n.readInputQuestion(n,{title:e});if(r===void 0)continue;let i={requestId:t.requestId};r.optionId!==void 0&&(i.optionId=r.optionId),r.text!==void 0&&(i.text=r.text),c.push(i),this.#d.delete(t.requestId)}}upsertResponseMessage(t,n,o),o=!0,i=c,r=void 0;continue}upsertResponseMessage(t,n,o)}}catch(e){if(isInterruptedError(e))return;throw e}o=!1,i=void 0,r=void 0}}#g(){for(let e of this.#p.values())e.abort();this.#p.clear(),this.#f.clear(),this.#d.clear(),this.#m.clear(),this.#h.clear(),this.#t&&(this.#e=this.#t.session())}async#_(e){let t={};e.prompt!==void 0&&(t.message=e.prompt),e.inputResponses!==void 0&&e.inputResponses.length>0&&(t.inputResponses=e.inputResponses);let n;try{n=await this.#e.send(t)}catch(t){if(isInterruptedError(t))throw t;return{uiMessageStream:errorOnlyUIMessageStream({errorText:this.#u(t),generateMessageId:e.generateMessageId,originalMessages:e.messages}),message:lastAssistantMessage(e.messages)}}return{uiMessageStream:ashEventsToUIMessageStream({events:n,generateMessageId:e.generateMessageId,originalMessages:e.messages,pendingInputRequests:this.#d,subagentRuns:this.#f,onSubagentCalled:e=>this.#x(e),onSubagentCompleted:e=>this.#C(e),onConnectionAuthRequired:e=>this.#v(e),onConnectionAuthCompleted:e=>this.#y(e)}),message:lastAssistantMessage(e.messages)}}#v(e){let t={name:e.data.name,description:e.data.description,state:`required`};e.data.authorization!==void 0&&(t.challenge=e.data.authorization),e.data.webhookUrl!==void 0&&(t.webhookUrl=e.data.webhookUrl),this.#m.set(e.data.name,t),this.#b(t)}#y(e){let t=this.#m.get(e.data.name)??{name:e.data.name,description:``,state:e.data.outcome};t.state=e.data.outcome,e.data.reason!==void 0&&(t.reason=e.data.reason),this.#m.set(e.data.name,t),this.#h.delete(e.data.name),this.#b(t),this.#n.setConnectionAuthPendingCount?.(this.#h.size)}#b(e){let t={name:e.name,description:e.description,state:e.state};e.challenge!==void 0&&(t.challenge=e.challenge),e.reason!==void 0&&(t.reason=e.reason),this.#n.upsertConnectionAuth?.(t)}#x(e){let n=e.data.callId;if(this.#p.has(n))return;let r=this.#t;if(!r)return;let i=new AbortController;this.#p.set(n,i),(async()=>{try{let a=r.session({sessionId:e.data.childSessionId,streamIndex:0}).openStream({signal:i.signal});for await(let e of a)if(i.signal.aborted||(this.#w(n,e),isCurrentTurnBoundaryEvent(e)))break}catch(e){if(!isAbortLikeError(e)){let t=formatErrorMessage(e),r=this.#f.get(n);if(r){let{key:e,step:i}=openCurrentSubagentSection(r);i.message=i.message?`${i.message}\n\nstream error: ${t}`:`stream error: ${t}`,i.finalized=!0,r.currentSectionKey=null,this.#n.upsertSubagentStep?.({callId:n,subagentName:r.name,sectionKey:e,reasoning:i.reasoning,message:i.message,finalized:!0})}}}finally{this.#p.delete(n)}})()}#S(e,t,n){let r=t.tools.get(n.childCallId),i=r??{toolName:n.toolName,input:n.input,status:n.status};if(r){let e={"approval-requested":0,executing:1,done:2,failed:2};e[n.status]>e[r.status]&&(r.status=n.status),r.input=n.input}else t.tools.set(n.childCallId,i);this.#n.markChildToolCallId?.(n.childCallId),this.#n.upsertSubagentTool?.({callId:e,subagentName:t.name,childCallId:n.childCallId,toolName:i.toolName,input:i.input,status:i.status})}#C(e){let t=this.#f.get(e);if(t){for(let[n,r]of t.steps)r.finalized||(r.finalized=!0,this.#n.upsertSubagentStep?.({callId:e,subagentName:t.name,sectionKey:n,reasoning:r.reasoning,message:r.message,finalized:!0}));t.currentSectionKey=null}}#w(e,t){let n=this.#f.get(e);if(!n)return;let r=this.#n,emit=(t,i)=>{r.upsertSubagentStep?.({callId:e,subagentName:n.name,sectionKey:t,reasoning:i.reasoning,message:i.message,finalized:i.finalized})},finalizeCurrent=()=>{if(n.currentSectionKey===null)return;let e=n.steps.get(n.currentSectionKey);e&&(e.finalized=!0,emit(n.currentSectionKey,e)),n.currentSectionKey=null};switch(t.type){case`reasoning.appended`:{let{key:e,step:r}=openCurrentSubagentSection(n);r.reasoning+=t.data.reasoningDelta,emit(e,r);break}case`reasoning.completed`:break;case`message.appended`:{let{key:e,step:r}=openCurrentSubagentSection(n);r.message+=t.data.messageDelta,emit(e,r);break}case`message.completed`:{let{key:e,step:r}=openCurrentSubagentSection(n);t.data.message!==null&&r.message.length===0&&(r.message=t.data.message),r.finalized=!0,emit(e,r),n.currentSectionKey=null;break}case`step.completed`:finalizeCurrent();break;case`actions.requested`:finalizeCurrent();for(let r of t.data.actions)r.kind===`tool-call`&&this.#S(e,n,{childCallId:r.callId,toolName:r.toolName,input:r.input,status:`executing`});break;case`input.requested`:finalizeCurrent();for(let r of t.data.requests)r.action.kind===`tool-call`&&this.#S(e,n,{childCallId:r.action.callId,toolName:r.action.toolName,input:r.action.input,status:`approval-requested`});break;case`action.result`:{let i=t.data.result;if(i.kind!==`tool-result`)break;let a=n.tools.get(i.callId);if(!a)break;t.data.status===`failed`?(a.status=`failed`,a.errorText=formatActionResultError(t)):(a.status=`done`,a.output=i.output);let o={callId:e,subagentName:n.name,childCallId:i.callId,toolName:a.toolName,input:a.input,status:a.status};a.output!==void 0&&(o.output=a.output),a.errorText!==void 0&&(o.errorText=a.errorText),r.upsertSubagentTool?.(o);break}default:break}}};function createDefaultRenderer(t){return t.tools===void 0&&t.reasoning===void 0&&t.subagents===void 0&&t.connectionAuth===void 0&&t.assistantResponseStats===void 0&&t.contextSize===void 0&&t.logs===void 0?new TerminalRenderer:new TerminalRenderer({tools:t.tools,reasoning:t.reasoning,subagents:t.subagents,connectionAuth:t.connectionAuth,assistantResponseStats:t.assistantResponseStats,contextSize:t.contextSize,logs:t.logs})}function createRenderer(t){if(t.renderer)return t.renderer;if(!(!t.screen&&!t.userInput))return new TerminalRenderer({tools:t.tools,reasoning:t.reasoning,subagents:t.subagents,connectionAuth:t.connectionAuth,assistantResponseStats:t.assistantResponseStats,contextSize:t.contextSize,logs:t.logs,input:t.userInput,output:t.screen})}async function*ashEventsToUIMessageStream(e){let{events:t,generateMessageId:n,originalMessages:r,pendingInputRequests:i,subagentRuns:a,onSubagentCalled:o,onSubagentCompleted:s,onConnectionAuthRequired:c,onConnectionAuthCompleted:l}=e,u=new Set,d=new Set,f=new Set,p=!1,m=!1,h,ensureStart=()=>{if(!p)return p=!0,{type:`start`,messageId:lastAssistantMessage(r)?.id??n()}};for await(let e of t){let t=ensureStart();switch(t&&(yield t),e.type){case`session.started`:case`turn.started`:case`message.received`:break;case`step.started`:yield{type:`start-step`};break;case`step.completed`:h=e.data.usage,yield*closeOpenTextParts(u),yield*closeOpenReasoningParts(d),yield{type:`finish-step`};break;case`message.appended`:{let t=e,n=textPartId(t.data.turnId,t.data.stepIndex);u.has(n)||(u.add(n),yield{type:`text-start`,id:n}),yield{type:`text-delta`,id:n,delta:t.data.messageDelta};break}case`message.completed`:{let t=textPartId(e.data.turnId,e.data.stepIndex);u.delete(t)&&(yield{type:`text-end`,id:t});break}case`reasoning.appended`:{let t=e,n=reasoningPartId(t.data.turnId,t.data.stepIndex);d.has(n)||(d.add(n),yield{type:`reasoning-start`,id:n}),yield{type:`reasoning-delta`,id:n,delta:t.data.reasoningDelta};break}case`reasoning.completed`:{let t=reasoningPartId(e.data.turnId,e.data.stepIndex);d.delete(t)&&(yield{type:`reasoning-end`,id:t});break}case`actions.requested`:{let t=e.data.actions;for(let e of t)e.kind===`tool-call`&&(f.add(e.callId),yield{type:`tool-input-available`,toolCallId:e.callId,toolName:e.toolName,input:e.input});break}case`input.requested`:{let t=e.data.requests;for(let e of t){if(e.action.kind!==`tool-call`)continue;let t=e.action.callId;f.has(t)||(f.add(t),yield{type:`tool-input-available`,toolCallId:t,toolName:e.action.toolName,input:e.action.input}),i.set(e.requestId,e),!isQuestionRequest(e)&&(yield{type:`tool-approval-request`,approvalId:e.requestId,toolCallId:t})}break}case`action.result`:{let t=e,n=t.data.result.callId;if(!f.has(n))break;t.data.status===`failed`?yield{type:`tool-output-error`,toolCallId:n,errorText:formatActionResultError(t)}:yield{type:`tool-output-available`,toolCallId:n,output:t.data.result.output};break}case`step.failed`:case`turn.failed`:case`session.failed`:yield{type:`error`,errorText:formatFailureMessage(e)};break;case`session.waiting`:case`session.completed`:case`turn.completed`:yield*closeOpenTextParts(u),yield*closeOpenReasoningParts(d),yield{type:`finish`,messageMetadata:createResponseMetadata(h)},m=!0;break;case`subagent.called`:{let t=e;if(!a.has(t.data.callId))a.set(t.data.callId,{name:t.data.name,steps:new Map,currentSectionKey:null,nextSectionKey:0,tools:new Map});else{let e=a.get(t.data.callId);e&&(e.name=t.data.name)}o?.(t);break}case`subagent.started`:case`subagent.event`:break;case`subagent.completed`:s?.(e.data.callId);break;case`authorization.required`:c?.(e);break;case`authorization.completed`:l?.(e);break;default:break}}p||(yield{type:`start`,messageId:n()}),m||(yield*closeOpenTextParts(u),yield*closeOpenReasoningParts(d),yield{type:`finish`,messageMetadata:createResponseMetadata(h)})}async function*errorOnlyUIMessageStream(e){yield{type:`start`,messageId:lastAssistantMessage(e.originalMessages)?.id??e.generateMessageId()},yield{type:`error`,errorText:e.errorText},yield{type:`finish`}}function textPartId(e,t){return`text:${e}:${t}`}function reasoningPartId(e,t){return`reasoning:${e}:${t}`}function*closeOpenTextParts(e){for(let t of e)yield{type:`text-end`,id:t};e.clear()}function*closeOpenReasoningParts(e){for(let t of e)yield{type:`reasoning-end`,id:t};e.clear()}function createResponseMetadata(e){if(!e)return;let{inputTokens:t,outputTokens:n}=e,r=t!=null||n!=null?(t??0)+(n??0):void 0;if(r==null&&n==null)return;let i={};return r!=null&&(i.totalTokens=r),n!=null&&(i.outputTokens=n),{usage:i}}function formatActionResultError(e){if(e.data.error?.message)return e.data.error.message;let t=e.data.result.output;if(typeof t==`string`)return t;try{return JSON.stringify(t)}catch{return`Tool execution failed.`}}function formatFailureMessage(e){let{code:t,message:n}=e.data;return t?`${t}: ${n}`:n}function createUserMessage(e,t){return{id:e,role:`user`,parts:[{type:`text`,text:t}]}}function upsertResponseMessage(e,t,n){if(n&&e.at(-1)?.role===`assistant`){e[e.length-1]=t;return}e.push(t)}function lastAssistantMessage(e){let t=e.at(-1);return t?.role===`assistant`?t:void 0}function isQuestionRequest(e){return e.display===`select`||e.display===`text`?!0:e.display===`confirmation`?!1:e.options!==void 0&&e.options.length>0}function findPendingQuestionRequests(e){let t=[];for(let n of e.values())isQuestionRequest(n)&&t.push(n);return t}function toAgentTUIInputQuestion(e){let t=e.display===`text`?`text`:e.display===`select`||e.options!==void 0&&e.options.length>0?`select`:`text`,n={requestId:e.requestId,prompt:e.prompt,display:t};return e.options!==void 0&&(n.options=e.options.map(e=>{let t={id:e.id,label:e.label};return e.description!==void 0&&(t.description=e.description),e.style!==void 0&&(t.style=e.style),t})),e.allowFreeform!==void 0&&(n.allowFreeform=e.allowFreeform),n}function findPendingToolApprovalRequests(e){let t=[];for(let[i,a]of e.parts.entries()){if(!isToolUIPart(a)||a.state!==`approval-requested`||a.approval.isAutomatic===!0)continue;let o={approvalId:a.approval.id,toolCallId:a.toolCallId,toolName:getToolName(a),input:a.input,messageId:e.id,partIndex:i};a.title!==void 0&&(o.title=a.title),a.providerExecuted!==void 0&&(o.providerExecuted=a.providerExecuted),t.push(o)}return t}function applyToolApprovalResponse(e,t,n){let i=e.parts[t.partIndex];if(!i||!isToolUIPart(i)||i.toolCallId!==t.toolCallId)throw Error(`Could not find tool approval request ${t.approvalId}.`);i.state=`approval-responded`;let a={id:t.approvalId,approved:n.approved};n.reason&&(a.reason=n.reason),i.approval=a}function isInterruptedError(e){return e instanceof Error&&e.message===`Interrupted`}function parsePromptCommand(e){let t=e.trim();return t===`/new`?`new`:t===`/exit`||t===`/quit`?`exit`:null}function isAbortLikeError(e){return!!(e instanceof Error&&(e.name===`AbortError`||e.message.toLowerCase().includes(`abort`)))}function formatErrorMessage(e){if(e instanceof Error)return e.message;if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return String(e)}}function openCurrentSubagentSection(e){e.currentSectionKey===null&&(e.currentSectionKey=e.nextSectionKey++,e.steps.set(e.currentSectionKey,{reasoning:``,message:``,finalized:!1}));let t=e.steps.get(e.currentSectionKey);if(!t)throw Error(`invariant: subagent section state missing for current key`);return{key:e.currentSectionKey,step:t}}export{AshTUIRunner};
@@ -0,0 +1,21 @@
1
+ export type TerminalFrameOutput = {
2
+ write(chunk: string | Uint8Array, encodingOrCallback?: BufferEncoding | ((error?: Error | null) => void), callback?: (error?: Error | null) => void): boolean;
3
+ };
4
+ export type TerminalFrameBufferOptions = {
5
+ useSynchronizedUpdates?: boolean;
6
+ };
7
+ export declare class TerminalFrameBuffer {
8
+ #private;
9
+ constructor(output: TerminalFrameOutput, options?: TerminalFrameBufferOptions);
10
+ present(frame: string): void;
11
+ reset(): void;
12
+ /**
13
+ * Writes a control sequence (e.g. alt-screen enter/exit, cursor
14
+ * show/hide) straight to the underlying terminal via the original
15
+ * write captured at construction. This bypasses any later monkey-patch
16
+ * of the output's `write` — notably the renderer's foreign-output log
17
+ * capture — so the renderer's own terminal control writes are never
18
+ * mistaken for captured log output.
19
+ */
20
+ writeControl(sequence: string): void;
21
+ }
@@ -0,0 +1,2 @@
1
+ var TerminalFrameBuffer=class{#e;#t;#n;#r=!1;#i=!1;constructor(e,t){this.#t=e.write.bind(e),this.#e=t?.useSynchronizedUpdates??!0,e.write=((e,t,n)=>(this.#r||(this.#i=!0),this.#t(e,t,n)))}present(e){let t=snapshotFrame(e),n=this.#n&&!this.#i?diffFrame(this.#n,t):`${e}`;this.#n=t,this.#i=!1,n.length!==0&&this.#a(n)}reset(){this.#n=void 0}writeControl(e){this.#t(e)}#a(e){this.#r=!0;try{if(!this.#e){this.#t(e);return}this.#t(`[?2026h${e}[?2026l`)}finally{this.#r=!1}}};function snapshotFrame(e){return{lines:e.split(`
2
+ `)}}function diffFrame(e,t){let n=``,r=Math.max(e.lines.length,t.lines.length);for(let i=0;i<r;i++){let r=t.lines[i];e.lines[i]!==r&&(n+=`[${i+1};1H${r??``}`)}return n}export{TerminalFrameBuffer};
@@ -0,0 +1,111 @@
1
+ import type { AgentTUIInputQuestion, AgentTUIInputQuestionResponse, AgentTUIStreamResult, AgentTUIToolApprovalRequest, AgentTUIToolApprovalResponse, ConnectionAuthUpdate, SubagentStepUpdate, SubagentToolUpdate } from "./runner.js";
2
+ import type { AssistantResponseStatsMode, LogDisplayMode, TerminalPartDisplayMode } from "./types.js";
3
+ import { TerminalFrameBuffer } from "./terminal-frame-buffer.js";
4
+ import { type UIMessage } from "ai";
5
+ export type TerminalInput = {
6
+ isTTY?: boolean;
7
+ on(event: "data", listener: (chunk: Buffer) => void): TerminalInput;
8
+ off(event: "data", listener: (chunk: Buffer) => void): TerminalInput;
9
+ resume(): TerminalInput;
10
+ pause(): TerminalInput;
11
+ setRawMode?: (mode: boolean) => TerminalInput;
12
+ };
13
+ export type TerminalOutput = {
14
+ columns?: number;
15
+ rows?: number;
16
+ write(chunk: string | Uint8Array, encodingOrCallback?: BufferEncoding | ((error?: Error | null) => void), callback?: (error?: Error | null) => void): boolean;
17
+ on(event: "resize", listener: () => void): TerminalOutput;
18
+ off(event: "resize", listener: () => void): TerminalOutput;
19
+ };
20
+ export type TerminalRendererOptions = {
21
+ input?: TerminalInput;
22
+ output?: TerminalOutput;
23
+ frameBuffer?: TerminalFrameBuffer;
24
+ tools?: TerminalPartDisplayMode;
25
+ reasoning?: TerminalPartDisplayMode;
26
+ subagents?: TerminalPartDisplayMode;
27
+ connectionAuth?: TerminalPartDisplayMode;
28
+ assistantResponseStats?: AssistantResponseStatsMode;
29
+ contextSize?: number;
30
+ /**
31
+ * Whether to capture foreign `process.stdout` / `process.stderr` writes
32
+ * (from the in-process dev server and agent code) and render them as
33
+ * inline log regions. Defaults to `true` only when the renderer is bound
34
+ * to the real `process.stdout`; tests can force it on with an injected
35
+ * screen.
36
+ */
37
+ captureForeignOutput?: boolean;
38
+ logs?: LogDisplayMode;
39
+ };
40
+ export type TerminalSessionOptions = {
41
+ title?: string;
42
+ initialPrompt?: string;
43
+ submittedPrompt?: string;
44
+ waitForExit?: boolean;
45
+ continueSession?: boolean;
46
+ tools?: TerminalPartDisplayMode;
47
+ reasoning?: TerminalPartDisplayMode;
48
+ subagents?: TerminalPartDisplayMode;
49
+ connectionAuth?: TerminalPartDisplayMode;
50
+ assistantResponseStats?: AssistantResponseStatsMode;
51
+ contextSize?: number;
52
+ };
53
+ export type TerminalKey = {
54
+ type: "character";
55
+ value: string;
56
+ } | {
57
+ type: "backspace";
58
+ } | {
59
+ type: "enter";
60
+ } | {
61
+ type: "up";
62
+ } | {
63
+ type: "down";
64
+ } | {
65
+ type: "left";
66
+ } | {
67
+ type: "right";
68
+ } | {
69
+ type: "tab";
70
+ } | {
71
+ type: "escape";
72
+ } | {
73
+ type: "ctrl-r";
74
+ } | {
75
+ type: "ctrl-c";
76
+ } | {
77
+ type: "ignore";
78
+ };
79
+ export declare class TerminalRenderer {
80
+ #private;
81
+ constructor(options?: TerminalRendererOptions);
82
+ readPrompt(options?: TerminalSessionOptions): Promise<string>;
83
+ renderStream(result: AgentTUIStreamResult, options?: TerminalSessionOptions): Promise<UIMessage | undefined>;
84
+ readToolApproval(request: AgentTUIToolApprovalRequest, options?: TerminalSessionOptions): Promise<AgentTUIToolApprovalResponse>;
85
+ readInputQuestion(question: AgentTUIInputQuestion, options?: TerminalSessionOptions): Promise<AgentTUIInputQuestionResponse | undefined>;
86
+ upsertSubagentStep(update: SubagentStepUpdate): void;
87
+ upsertSubagentTool(update: SubagentToolUpdate): void;
88
+ markChildToolCallId(callId: string): void;
89
+ isChildToolCallId(callId: string): boolean;
90
+ upsertConnectionAuth(update: ConnectionAuthUpdate): void;
91
+ /**
92
+ * Sets the count of connections currently awaiting an OAuth
93
+ * callback. While the count is > 0, the bottom status bar shows a
94
+ * "waiting for connection authorization" hint that overrides the
95
+ * default streaming / processing message. The runner calls this on
96
+ * `authorization.required` (incrementing) and
97
+ * `authorization.completed` (decrementing) transitions.
98
+ */
99
+ setConnectionAuthPendingCount(count: number): void;
100
+ /**
101
+ * Clears the rendered transcript and per-conversation display state,
102
+ * leaving the UI interactive on a fresh screen. Backs the `/new` command.
103
+ */
104
+ reset(): void;
105
+ /**
106
+ * Tears down interactive mode and restores the terminal. Backs the
107
+ * `/exit` command, matching the Ctrl+C teardown.
108
+ */
109
+ shutdown(): void;
110
+ }
111
+ export declare function parseKey(chunk: Buffer): TerminalKey;
@@ -0,0 +1,12 @@
1
+ import{sliceVisible,stripAnsi,visibleLength}from"./terminal-text.js";import{renderMarkdown}from"./markdown.js";import{renderScreenViewport}from"./layout.js";import{TerminalFrameBuffer}from"./terminal-frame-buffer.js";import{getToolName,isToolUIPart,readUIMessageStream}from"ai";const colors={reset:`\x1B[0m`,dim:`\x1B[2m`,user:`\x1B[96m`,assistant:`\x1B[92m`,reasoning:`\x1B[94m`,tool:`\x1B[95m`,error:`\x1B[91m`,question:`\x1B[93m`,subagent:`\x1B[38;5;208m`,connectionAuth:`\x1B[35m`,log:`\x1B[90m`},sectionStyles={user:{color:colors.user,border:`─`},assistant:{color:colors.assistant,border:`─`},reasoning:{color:colors.reasoning,border:`·`},tool:{color:colors.tool,border:`─`},error:{color:colors.error,border:`─`},question:{color:colors.question,border:`─`},"subagent-step":{color:colors.subagent,border:`─`},"subagent-tool":{color:colors.subagent,border:`─`},"connection-auth":{color:colors.connectionAuth,border:`─`},log:{color:colors.log,border:`·`}},processingStatus=`Processing input... ↑/↓ scroll · Ctrl+C quit`,processingToolResultsStatus=`Processing tool results... ↑/↓ scroll · Ctrl+C quit`,streamingStatus=`Streaming... ↑/↓ scroll · Ctrl+C quit`,executingToolsStatus=`Executing tools... ↑/↓ scroll · Ctrl+C quit`;var TerminalRenderer=class{#e;#t;#n;#r;#i;#a;#o;#s;#c;#l=[];#u=new Set;#d=new Map;#f=0;#p=``;#m=!1;#h=0;#g=`Agent TUI`;#_=streamingStatus;#v=!1;#y=!1;#b;#x;#S;#C;#w=!0;#T;#E;#D;#O;#k;#A=``;#j=``;#M;#N;#P;constructor(e){this.#e=e?.input??process.stdin,this.#t=e?.output??process.stdout,this.#n=e?.frameBuffer??new TerminalFrameBuffer(this.#t),this.#r=e?.tools??`full`,this.#i=e?.reasoning??`full`,this.#a=e?.subagents??`full`,this.#o=e?.connectionAuth??`full`,this.#s=e?.assistantResponseStats??`tokensPerSecond`,this.#c=e?.contextSize,this.#x=e?.contextSize,this.#M=e?.captureForeignOutput??this.#t===process.stdout,this.#N=e?.logs??`all`}async readPrompt(e){return this.#F(e),this.#m=!0,this.#p=e?.initialPrompt??``,this.#_=`Type a prompt and press Enter · ↑/↓ scroll · Ctrl+C quit`,this.#V(),this.#ne(),await new Promise((e,t)=>{this.#E=n=>{let r=parseKey(n);switch(r.type){case`character`:this.#p+=r.value,this.#U(),this.#ne();break;case`backspace`:this.#p=this.#p.slice(0,-1),this.#U(),this.#ne();break;case`enter`:{let t=this.#p;this.#m=!1,this.#H(),this.#_=processingStatus,this.#G(t),this.#P=t,this.#p=``,this.#ne(),this.#R(),e(t);break}case`up`:case`down`:this.#B(r.type);break;case`ctrl-r`:this.#re();break;case`ctrl-c`:this.#H(),this.#I(),t(interruptedError());break;case`ignore`:break}},this.#L()})}async renderStream(e,t){this.#F(t),this.#m=!1,this.#_=processingStatus,this.#W(t?.submittedPrompt),this.#y=!1,this.#b=void 0,this.#S=void 0,this.#C=void 0;let n={tools:t?.tools??this.#r,reasoning:t?.reasoning??this.#i,assistantResponseStats:t?.assistantResponseStats??this.#s};this.#ne();let r=new Promise(e=>{this.#O=e});this.#E=e=>this.#z(e),this.#L();let i,a=toReadableStream(this.#Y(e.uiMessageStream));try{let t=readUIMessageStream({message:e.message,stream:a,onError:e=>this.#X(`Error`,formatStreamError(e))});for await(let e of takeUntil(t,r)){if(this.#y)break;i=e,this.#K(e,n)}!this.#y&&i&&this.#S!=null&&this.#K(i,n)}finally{this.#O=void 0,this.#y&&e.abort?.(),this.#R(),this.#_=this.#y?`Interrupted`:t?.continueSession?`Done · Enter another prompt · ↑/↓ scroll · Ctrl+C quit`:`Done · ↑/↓ scroll · q/Ctrl+C quit`,this.#ne(),await this.#ue(t),(this.#y||!t?.continueSession)&&this.#I()}if(this.#y)throw interruptedError();return i}async readToolApproval(e,t){return this.#F(t),this.#m=!1,this.#_=`Approve ${formatToolApprovalTitle(e)}? y/n · ↑/↓ scroll · Ctrl+C quit`,this.#y=!1,this.#ne(),await new Promise((e,t)=>{this.#E=n=>{let r=parseKey(n);switch(r.type){case`character`:{let t=r.value.toLowerCase();t===`y`?(this.#_=`Approved · Processing input... ↑/↓ scroll · Ctrl+C quit`,this.#R(),this.#ne(),e({approved:!0})):t===`n`&&(this.#_=`Denied · Processing input... ↑/↓ scroll · Ctrl+C quit`,this.#R(),this.#ne(),e({approved:!1,reason:`Denied by user.`}));break}case`up`:case`down`:this.#B(r.type);break;case`ctrl-r`:this.#re();break;case`ctrl-c`:this.#y=!0,this.#I(),t(interruptedError());break;default:break}},this.#L()})}async readInputQuestion(e,t){this.#F(t),this.#m=!1,this.#y=!1;let n=e.options??[],r=n.length>0,i=(e.allowFreeform===!0||!r)&&r,a=n.length+ +!!i,o=questionSectionId(e.requestId),s=r?`select`:`text`,c=r?0:n.length,l=``,isOnFreeformRow=()=>i&&c===n.length,renderSection=()=>{let t=this.#oe();this.#q({id:o,kind:`question`,title:`Question`,rightTitle:`pending`,content:formatQuestionSectionContent(e,c)}),this.#te(t)};renderSection();let repaintStatus=()=>{if(s===`select`){let e=[`↑/↓ select`];e.push(isOnFreeformRow()?`enter type`:`enter confirm`),e.push(`Ctrl+C quit`),this.#_=e.join(` · `)}else{let e=this.#w?`_`:` `,t=[`enter send`];t.push(r?`esc back`:`esc clear`),t.push(`Ctrl+C quit`),this.#_=`> ${l}${e} ${t.join(` · `)}`}this.#ne()};s===`text`&&this.#V(),repaintStatus();let finalize=t=>{let n=this.#oe();this.#q({id:o,kind:`question`,title:`Question`,rightTitle:`answered`,content:formatAnsweredQuestionContent(e,t.label)}),this.#_=`Answered · Processing input... ↑/↓ scroll · Ctrl+C quit`,this.#H(),this.#R(),this.#te(n);let r={};return t.optionId!==void 0&&(r.optionId=t.optionId),t.text!==void 0&&(r.text=t.text),r};return await new Promise((t,i)=>{this.#E=o=>{let u=parseKey(o);if(u.type===`ctrl-c`){this.#y=!0,this.#H(),this.#I(),i(interruptedError());return}if(u.type===`ctrl-r`){this.#re();return}if(s===`select`){switch(u.type){case`up`:a>0&&(c=(c-1+a)%a,renderSection(),repaintStatus());break;case`down`:a>0&&(c=(c+1)%a,renderSection(),repaintStatus());break;case`enter`:{if(isOnFreeformRow()){s=`text`,l=``,this.#V(),repaintStatus();break}let e=n[c];e&&t(finalize({optionId:e.id,label:e.label}));break}default:break}return}switch(u.type){case`character`:l+=u.value,this.#U(),repaintStatus();break;case`backspace`:l=l.slice(0,-1),this.#U(),repaintStatus();break;case`enter`:{let n=resolveQuestionText(l,e);if(n===void 0)break;t(finalize(n));break}case`escape`:if(r){if(l.length>0){l=``,this.#U(),repaintStatus();break}s=`select`,l=``,this.#H(),repaintStatus();break}l=``,this.#U(),repaintStatus();break;default:break}},this.#L()})}#F(e){this.#g=e?.title??this.#g,this.#x=e?.contextSize??this.#c,!this.#v&&(this.#v=!0,this.#n.reset(),this.#n.writeControl(`\x1B[?1049h\x1B[?25l`),this.#Z(),this.#e.isTTY&&(this.#e.setRawMode?.(!0),this.#e.resume()),this.#D=()=>this.#re(),this.#t.on(`resize`,this.#D))}#I(){this.#R(),this.#H(),this.#v&&=(this.#e.isTTY&&(this.#e.setRawMode?.(!1),this.#e.pause()),this.#D&&=(this.#t.off(`resize`,this.#D),void 0),this.#Q(),this.#n.writeControl(`\x1B[?25h\x1B[?1049l`),this.#n.reset(),!1)}#L(){this.#E&&this.#e.on(`data`,this.#E)}#R(){this.#E&&=(this.#e.off(`data`,this.#E),void 0)}#z(e){let t=parseKey(e);switch(t.type){case`up`:case`down`:this.#B(t.type);break;case`ctrl-r`:this.#re();break;case`ctrl-c`:this.#y=!0,this.#O?.();break;default:break}}#B(e){let t=e===`up`?1:-1;this.#h=this.#ae(this.#h+t),this.#ne()}#V(){this.#H(),this.#U(),this.#T=setInterval(()=>{this.#w=!this.#w,this.#ne()},500),this.#T.unref?.()}#H(){this.#T&&=(clearInterval(this.#T),void 0),this.#w=!0}#U(){this.#w=!0}#W(e){if(e!=null){if(this.#P===e){this.#P=void 0;return}this.#G(e)}}#G(e){let t=this.#oe();this.#l.push({kind:`user`,title:`User`,content:e}),this.#te(t)}#K(e,t){let n=this.#oe(),r=new Set,i=extractAssistantResponseStatsFromMetadata(e.metadata);this.#b=i.totalTokens??this.#b,this.#S=i.outputTokens??this.#S,this.#C=i.tokensPerSecond??this.#C;for(let[n,i]of e.parts.entries()){let a=sectionId(e.id,n);switch(i.type){case`text`:{let e=i.text.trim();if(e.length===0)break;r.add(a),this.#q({id:a,kind:`assistant`,title:`Assistant`,rightTitle:formatAssistantResponseStats({totalTokens:this.#b,outputTokens:this.#S,tokensPerSecond:this.#C},t.assistantResponseStats),content:e});break}case`reasoning`:{let o=i.text.trim();if(t.reasoning===`hidden`||o.length===0)break;r.add(a),this.#q({id:a,kind:`reasoning`,title:`Reasoning`,content:o,collapsed:shouldCollapsePart(e,n,t.reasoning,t)});break}default:if(isToolUIPart(i)){if(t.tools===`hidden`||this.#u.has(i.toolCallId))break;r.add(a),this.#d.set(i.toolCallId,a),this.#q({id:a,...renderToolInvocation(i,{mode:t.tools,collapsed:shouldCollapsePart(e,n,t.tools,t)})})}break}}this.#J(e.id,r),this.#te(n)}#q(e){let t=e.id?this.#l.find(t=>t.id===e.id):void 0;if(t){let n=t.cache;t.kind=e.kind,t.title=e.title,t.rightTitle=e.rightTitle,t.content=e.content,t.collapsed=e.collapsed,t.cache=n&&sectionMatchesCache(e,n)?n:void 0;return}this.#l.push(e)}#J(e,t){let n=`${e}:`;this.#l=this.#l.filter(e=>e.id==null||!e.id.startsWith(n)||t.has(e.id))}async*#Y(e){let t=!1;for await(let n of iterateUIMessageStream(e)){let e=statusForStreamChunk(n,{hasPendingToolResults:t});if(n.type===`start-step`?t=!1:finishesToolExecution(n)&&(t=!0),e&&this.#_!==e?(this.#_=e,this.#ne()):startsVisibleAssistantStream(n)&&this.#_!==streamingStatus&&(this.#_=streamingStatus,this.#ne()),n.type===`finish`){let e=extractAssistantResponseStats(n);this.#b=e.totalTokens,this.#S=e.outputTokens,this.#C=e.tokensPerSecond}yield n}}upsertSubagentStep(e){if(this.#a===`hidden`)return;let t=this.#oe(),n=e.reasoning?.trim()??``,r=e.message?.trim()??``;if(n.length===0&&r.length===0){this.#te(t);return}let i=this.#a===`collapsed`||this.#a===`auto-collapsed`&&e.finalized,a=[];if(n.length>0){let e=n.split(`
2
+ `).map(e=>`${colors.dim} ${e}${colors.reset}`).join(`
3
+ `);a.push(e)}r.length>0&&a.push(r);let o={id:subagentStepSectionId(e.callId,e.sectionKey),kind:`subagent-step`,title:`Subagent · ${e.subagentName}`,content:a.join(`
4
+
5
+ `)};e.finalized||(o.rightTitle=`streaming`),i&&(o.collapsed=!0),this.#q(o),this.#te(t)}upsertSubagentTool(e){if(this.#a===`hidden`)return;let t=this.#oe(),n=this.#a===`collapsed`||this.#a===`auto-collapsed`&&e.status!==`executing`,r=[];e.input!==void 0&&r.push(`Input:\n${formatValue(e.input)}`),e.output===void 0?e.errorText!==void 0&&r.push(`Error:\n${e.errorText}`):r.push(`Output:\n${formatValue(e.output)}`);let i={id:subagentToolSectionId(e.callId,e.childCallId),kind:`subagent-tool`,title:`Subagent · ${e.subagentName} · Tool · ${e.toolName}`,rightTitle:e.status,content:r.join(`
6
+
7
+ `)};n&&(i.collapsed=!0),this.#q(i),this.#te(t)}markChildToolCallId(e){this.#u.add(e);let t=this.#d.get(e);if(t===void 0)return;let n=this.#oe();this.#l=this.#l.filter(e=>e.id!==t),this.#d.delete(e),this.#te(n)}isChildToolCallId(e){return this.#u.has(e)}upsertConnectionAuth(e){if(this.#o===`hidden`)return;let t=this.#oe(),n=e.state===`authorized`||e.state===`declined`||e.state===`failed`||e.state===`timed-out`,r=this.#o===`collapsed`||this.#o===`auto-collapsed`&&n,i={id:connectionAuthSectionId(e.name),kind:`connection-auth`,title:`Connection · ${e.name} · Auth`,rightTitle:e.state,content:formatConnectionAuthSectionContent(e)};r&&(i.collapsed=!0),this.#q(i),this.#te(t)}setConnectionAuthPendingCount(e){let t=Math.max(0,e);if(t===this.#f)return;let n=this.#f>0;this.#f=t,t>0?(this.#_=`Waiting for connection authorization — visit the URL above · Ctrl+C quit`,this.#ne()):n&&(this.#_=processingStatus,this.#ne())}reset(){this.#l=[],this.#u.clear(),this.#d.clear(),this.#P=void 0,this.#f=0,this.#h=0,this.#b=void 0,this.#S=void 0,this.#C=void 0,this.#v&&this.#re()}shutdown(){this.#I()}#X(e,t){let n=this.#oe();this.#l.push({kind:`error`,title:e,content:t}),this.#te(n)}#Z(){if(this.#k!==void 0||!this.#M)return;this.#A=``,this.#j=``;let capture=(e,t)=>{let n=e.write.bind(e);return e.write=((e,n,r)=>{let i=typeof n==`string`?n:void 0,a=typeof n==`function`?n:r;return this.#$(t,chunkToString(e,i)),a?.(),!0}),()=>{e.write=n}},e=capture(process.stdout,`stdout`),t=capture(process.stderr,`stderr`);this.#k=()=>{e(),t()}}#Q(){let e=this.#k;e!==void 0&&(this.#k=void 0,e(),this.#A.length>0&&(this.#ee(`stdout`)&&process.stdout.write(`${this.#A}\n`),this.#A=``),this.#j.length>0&&(this.#ee(`stderr`)&&process.stderr.write(`${this.#j}\n`),this.#j=``))}#$(e,n){let r=(e===`stdout`?this.#A:this.#j)+n,i=r.lastIndexOf(`
8
+ `),a=i===-1?r:r.slice(i+1);if(e===`stdout`?this.#A=a:this.#j=a,i===-1||!this.#ee(e))return;let o=stripAnsi(r.slice(0,i)).replace(/\s+$/u,``);if(o.trim().length===0)return;let s=this.#oe();this.#l.push({kind:`log`,title:`Log · ${e}`,content:o}),this.#te(s)}#ee(e){switch(this.#N){case`none`:return!1;case`stderr`:return e===`stderr`;case`all`:return!0}}#te(e){if(this.#h!==0){let t=this.#oe()-e;this.#h=this.#ae(this.#h+t)}this.#ne()}#ne(){let e=renderScreenViewport({width:this.#ce(),height:this.#le(),title:this.#g,rightTitle:formatTokenCount(this.#b,this.#x),visibleBodyLines:this.#ie(),input:this.#p,inputActive:this.#m,inputCursorVisible:this.#w,status:this.#_});this.#n.present(e)}#re(){this.#n.reset(),this.#ne()}#ie(){if(this.#l.length===0)return[`Waiting for input...`];let e=this.#se(),t=this.#oe(),n=Math.max(0,t-e-this.#h),r=n+e,i=[],a=0;for(let t of this.#l){let o=renderSectionLines(t,this.#ce()-4),s=a+o.length;if(s>n&&a<r&&i.push(...o.slice(Math.max(0,n-a),r-a)),i.length>=e)break;a=s}return i}#ae(e){let t=Math.max(0,this.#oe()-this.#se());return Math.min(Math.max(0,e),t)}#oe(){if(this.#l.length===0)return 1;let e=0;for(let t of this.#l)e+=renderSectionLines(t,this.#ce()-4).length;return e}#se(){return Math.max(1,this.#le()-5)}#ce(){return Math.max(20,this.#t.columns??80)}#le(){return Math.max(8,this.#t.rows??24)}async#ue(e){e?.waitForExit===!1||!this.#e.isTTY||this.#y||await new Promise(e=>{this.#E=t=>{let n=parseKey(t);switch(n.type){case`up`:case`down`:this.#B(n.type);break;case`ctrl-r`:this.#re();break;case`character`:n.value===`q`&&(this.#R(),e());break;case`ctrl-c`:this.#R(),process.exitCode=130,e();break;default:break}},this.#L()})}};function interruptedError(){return Error(`Interrupted`)}function chunkToString(e,t){return typeof e==`string`?e:Buffer.from(e).toString(t)}async function*takeUntil(e,t){let n=e[Symbol.asyncIterator](),r=t.then(()=>({done:!0,value:void 0}));for(;;){let e=await Promise.race([n.next(),r]);if(e.done)break;yield e.value}}function formatStreamError(e){return e instanceof Error?e.message:typeof e==`string`?e:JSON.stringify(e)}function renderToolInvocation(e,t){let n=getToolName(e),r=`Tool · ${e.title??n}`,i=`input`in e?e.input:void 0,a=i===void 0?`Input: (streaming...)`:`Input:\n${formatValue(i)}`,s=toolStatus(e);if(t.collapsed)return{kind:`tool`,title:r,rightTitle:s,content:``,collapsed:!0};switch(e.state){case`input-streaming`:return{kind:`tool`,title:r,rightTitle:s,content:a};case`input-available`:return{kind:`tool`,title:r,rightTitle:s,content:a};case`approval-requested`:return{kind:`tool`,title:r,rightTitle:s,content:a};case`approval-responded`:return{kind:`tool`,title:r,rightTitle:s,content:a};case`output-available`:return{kind:`tool`,title:r,rightTitle:s,content:`${a}\n\nOutput:\n${formatValue(e.output)}`};case`output-error`:return{kind:`error`,title:`Tool Error · ${e.title??n}`,rightTitle:s,content:`${a}\n\nError:\n${e.errorText}`};case`output-denied`:return{kind:`error`,title:`Tool Denied · ${e.title??n}`,rightTitle:s,content:`${a}\n\nReason: ${e.approval.reason??`denied`}`}}}function shouldCollapsePart(e,t,n,r){switch(n){case`collapsed`:return!0;case`auto-collapsed`:return e.parts.slice(t+1).some(e=>isVisibleAssistantPart(e,r));case`full`:case`hidden`:return!1}}function isVisibleAssistantPart(e,t){switch(e.type){case`text`:return e.text.trim().length>0;case`reasoning`:return t.reasoning!==`hidden`&&e.text.trim().length>0;default:return isToolUIPart(e)&&t.tools!==`hidden`}}function startsVisibleAssistantStream(e){switch(e.type){case`text-start`:case`text-delta`:case`reasoning-start`:case`reasoning-delta`:case`tool-input-start`:case`tool-input-delta`:return!0;default:return!1}}function statusForStreamChunk(e,{hasPendingToolResults:t}){switch(e.type){case`start-step`:return t?processingToolResultsStatus:processingStatus;case`tool-output-available`:case`tool-output-error`:case`tool-output-denied`:return processingToolResultsStatus;case`tool-input-available`:return executingToolsStatus;case`tool-approval-response`:return e.approved?executingToolsStatus:void 0;default:return}}function finishesToolExecution(e){switch(e.type){case`tool-output-available`:case`tool-output-error`:case`tool-output-denied`:return!0;default:return!1}}function toolStatus(e){switch(e.state){case`input-streaming`:return`waiting`;case`approval-requested`:return`approval requested`;case`input-available`:return`executing`;case`approval-responded`:return e.approval.approved?`executing`:`denied`;case`output-available`:case`output-error`:return`done`;case`output-denied`:return`denied`}}function formatValue(e){return typeof e==`string`?e:JSON.stringify(e,null,2)}function formatToolApprovalTitle(e){return`tool ${e.title??e.toolName}`}function sectionId(e,t){return`${e}:${t}`}function questionSectionId(e){return`question:${e}`}function subagentStepSectionId(e,t){return`subagent:${e}:step:${t}`}function subagentToolSectionId(e,t){return`subagent:${e}:tool:${t}`}function connectionAuthSectionId(e){return`connection-auth:${e}`}function formatConnectionAuthSectionContent(e){let t=[];e.description.length>0&&t.push(e.description);let n=e.challenge;return n?.url&&t.push(`URL: ${n.url}`),n?.userCode&&t.push(`Code: ${n.userCode}`),n?.expiresAt&&t.push(`Expires: ${n.expiresAt}`),n?.instructions&&t.push(n.instructions),e.reason!==void 0&&e.reason.length>0&&t.push(`Reason: ${e.reason}`),t.join(`
9
+ `)}function formatQuestionSectionContent(e,t){let n=[];n.push(e.prompt),n.push(``);let r=e.options??[];if(r.length>0){for(let[e,i]of r.entries()){let r=i.description?` — ${i.description}`:``,a=t===e,o=a?`▸ `:` `,s=`${i.label}${r}`,c=a?`${colors.question}${o}${s}${colors.reset}`:`${o}${s}`;n.push(c)}if(e.allowFreeform===!0){let e=t===r.length,i=e?`▸ `:` `,a=`Type your own answer`;n.push(e?`${colors.question}${i}${a}${colors.reset}`:`${colors.dim}${i}${a}${colors.reset}`)}}else n.push(`${colors.dim} (type your answer)${colors.reset}`);return n.join(`
10
+ `)}function formatAnsweredQuestionContent(e,t){return`${e.prompt}\n\n ✓ ${t}`}function resolveQuestionText(e,t){let n=e.trim();if(n.length===0)return;let r=n.toLowerCase(),i=t.options??[];if(i.length>0){let e=matchQuestionOption(r,i);if(e!==void 0)return{optionId:e.id,label:e.label}}if(t.allowFreeform===!0||i.length===0)return{text:n,label:n}}function matchQuestionOption(e,t){let n=t.find(t=>t.id.toLowerCase()===e);if(n!==void 0)return n;let r=t.find(t=>t.label.toLowerCase()===e);if(r!==void 0)return r;let i=Number(e);if(Number.isInteger(i)&&i>0&&i<=t.length)return t[i-1]}function toReadableStream(e){return e instanceof ReadableStream?e:new ReadableStream({async start(t){try{for await(let n of e)t.enqueue(n);t.close()}catch(e){t.error(e)}}})}async function*iterateUIMessageStream(e){if(e instanceof ReadableStream){let t=e.getReader();try{for(;;){let{done:e,value:n}=await t.read();if(e)return;yield n}}finally{t.releaseLock()}return}yield*e}function renderSectionLines(e,t){if(e.cache&&sectionMatchesCache(e,e.cache,t))return e.cache.lines;let n=createSectionLines(e,t);return e.cache={width:t,kind:e.kind,title:e.title,rightTitle:e.rightTitle,content:e.content,collapsed:e.collapsed===!0,lines:n},n}function sectionMatchesCache(e,t,n=t.width){return t.width===n&&t.kind===e.kind&&t.title===e.title&&t.rightTitle===e.rightTitle&&t.content===e.content&&t.collapsed===(e.collapsed===!0)}function createSectionLines(e,t){let n=sectionStyles[e.kind],i=Math.max(1,t-4),a=` ${e.title} `,o=e.rightTitle?` ${e.rightTitle} `:``;if(e.collapsed){let e=Math.max(0,t-2-a.length-o.length);return[`${n.color}╭${a}${n.border.repeat(e)}${o}╮${colors.reset}`,`${n.color}╰${n.border.repeat(Math.max(0,t-2))}╯${colors.reset}`]}let s=Math.max(0,t-2-a.length-o.length),c=`${n.color}╭${a}${n.border.repeat(s)}${o}╮${colors.reset}`,d=`${n.color}╰${n.border.repeat(Math.max(0,t-2))}╯${colors.reset}`;return[c,...(e.content.length===0?colors.dim+`(streaming...)`+colors.reset:e.kind===`log`?e.content:renderMarkdown(e.content)).split(`
11
+ `).flatMap(e=>wrapVisibleLine(e,i)).map(e=>sectionLine(e,i,n.color)),d]}function sectionLine(t,r,i){let a=sliceVisible(t,r),o=` `.repeat(Math.max(0,r-visibleLength(a)));return`${i}│${colors.reset} ${a}${o} ${i}│${colors.reset}`}function wrapVisibleLine(e,t){if(e.length===0)return[``];let r=[],i=e;for(;visibleLength(i)>t;){let e=findVisibleBreakPoint(i,t);r.push(i.slice(0,e).trimEnd()),i=i.slice(e).trimStart()}return r.push(i),r}function findVisibleBreakPoint(t,n){let r=sliceVisible(t,n+1).lastIndexOf(` `);return r>0?r:sliceVisible(t,n).length}function extractAssistantResponseStats(e){let t=`usage`in e?e.usage:void 0,n=`messageMetadata`in e?e.messageMetadata?.usage:void 0,r=`messageMetadata`in e?e.messageMetadata?.performance:void 0;return{totalTokens:extractTotalTokenCountFromUsage(t??n),outputTokens:extractOutputTokenCountFromUsage(t??n),tokensPerSecond:r?.outputTokensPerSecond}}function extractAssistantResponseStatsFromMetadata(e){let t=e;return{totalTokens:extractTotalTokenCountFromUsage(t?.usage),outputTokens:extractOutputTokenCountFromUsage(t?.usage),tokensPerSecond:t?.performance?.outputTokensPerSecond}}function extractTotalTokenCountFromUsage(e){let t=e?.totalTokens;if(typeof t==`number`)return t;if(typeof t?.total==`number`)return t.total;let n=extractInputTokenCountFromUsage(e),r=extractOutputTokenCountFromUsage(e);if(n!=null&&r!=null)return n+r}function extractInputTokenCountFromUsage(e){let t=e?.inputTokens;return typeof t==`number`?t:typeof t?.total==`number`?t.total:e?.promptTokens}function extractOutputTokenCountFromUsage(e){let t=e?.outputTokens;return typeof t==`number`?t:typeof t?.total==`number`?t.total:e?.completionTokens}function formatTokenCount(e,t){if(e==null)return;let n=`${e.toLocaleString()} ${e===1?`token`:`tokens`}`,r=formatContextPercentage(e,t);return r==null?n:`${n} ${r}`}function formatContextPercentage(e,t){if(!(t==null||t<=0||!Number.isFinite(t)))return`${Math.round(e/t*100).toLocaleString()}%`}function formatAssistantResponseStats(e,t){return t===`tokensPerSecond`?formatTokensPerSecond(e.tokensPerSecond):formatTokenCount(e.outputTokens)}function formatTokensPerSecond(e){if(e!=null)return`${formatNumber(e)} tok/s`}function formatNumber(e){return Number.isInteger(e)?e.toLocaleString():e.toLocaleString(void 0,{maximumFractionDigits:1})}function parseKey(e){let t=e.toString(`utf8`);switch(t){case``:return{type:`ctrl-r`};case``:return{type:`ctrl-c`};case`\r`:case`
12
+ `:return{type:`enter`};case``:case`\b`:return{type:`backspace`};case`\x1B[A`:return{type:`up`};case`\x1B[B`:return{type:`down`};case`\x1B[C`:return{type:`right`};case`\x1B[D`:return{type:`left`};case` `:return{type:`tab`};case`\x1B`:return{type:`escape`};default:return t>=` `&&t!==``?{type:`character`,value:t}:{type:`ignore`}}}export{TerminalRenderer,parseKey};
@@ -0,0 +1,6 @@
1
+ export declare const ansiPattern: RegExp;
2
+ export declare const ansiPrefixPattern: RegExp;
3
+ export declare function stripAnsi(input: string): string;
4
+ export declare function visibleLength(input: string): number;
5
+ export declare function sliceVisible(input: string, width: number): string;
6
+ export declare function codePointWidth(codePoint: number): number;
@@ -0,0 +1 @@
1
+ const ansiPattern=RegExp(`\x1B\\[[0-?]*[ -/]*[@-~]`,`g`),ansiPrefixPattern=RegExp(`^\x1B\\[[0-?]*[ -/]*[@-~]`);function stripAnsi(t){return t.replaceAll(ansiPattern,``)}function visibleLength(e){let n=0,r=0;for(;r<e.length;){let i=e.slice(r).match(ansiPrefixPattern);if(i){r+=i[0].length;continue}let a=e.codePointAt(r);if(a==null)break;let o=String.fromCodePoint(a);n+=codePointWidth(a),r+=o.length}return n}function sliceVisible(e,n){if(n<=0)return``;let r=``,i=0,a=0;for(;a<e.length&&i<n;){let o=e.slice(a).match(ansiPrefixPattern);if(o){r+=o[0],a+=o[0].length;continue}let s=e.codePointAt(a);if(s==null)break;let c=String.fromCodePoint(s),l=codePointWidth(s);if(l>0&&i+l>n)break;r+=c,a+=c.length,i+=l}for(;a<e.length;){let n=e.slice(a).match(ansiPrefixPattern);if(!n)break;r+=n[0],a+=n[0].length}return r}function codePointWidth(e){return e===9?4:e<32||e>=127&&e<160||isZeroWidthCodePoint(e)?0:isWideCodePoint(e)?2:1}function isZeroWidthCodePoint(e){return e>=768&&e<=879||e>=1155&&e<=1161||e>=1425&&e<=1469||e===1471||e>=1473&&e<=1474||e>=1476&&e<=1477||e===1479||e>=1552&&e<=1562||e>=1611&&e<=1631||e===1648||e>=1750&&e<=1756||e>=1759&&e<=1764||e>=1767&&e<=1768||e>=1770&&e<=1773||e===1809||e>=1840&&e<=1866||e>=1958&&e<=1968||e>=2027&&e<=2035||e>=2070&&e<=2073||e>=2075&&e<=2083||e>=2085&&e<=2087||e>=2089&&e<=2093||e>=2137&&e<=2139||e>=2259&&e<=2306||e===2362||e===2364||e>=2369&&e<=2376||e===2381||e>=2385&&e<=2391||e===8205||e>=65024&&e<=65039||e>=917760&&e<=917999}function isWideCodePoint(e){return e>=4352&&(e<=4447||e===9001||e===9002||e>=11904&&e<=42191&&e!==12351||e>=44032&&e<=55203||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65135||e>=65280&&e<=65376||e>=65504&&e<=65510||e>=127744&&e<=128591||e>=129280&&e<=129535||e>=131072&&e<=262141)}export{ansiPattern,ansiPrefixPattern,codePointWidth,sliceVisible,stripAnsi,visibleLength};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Test-only entrypoint for driving the `ash dev` terminal UI from
3
+ * end-to-end smoke tests. Exposed via the `experimental-ash/dev-tui-test`
4
+ * subpath export. Not part of the supported public API — production code
5
+ * reaches the TUI through the internal `#cli/dev/tui/*` modules instead.
6
+ */
7
+ export { AshTUIRunner } from "../runner.js";
8
+ export type { AshTUIRunnerOptions } from "../runner.js";
9
+ export { TerminalRenderer } from "../terminal-renderer.js";
10
+ export { MockScreen, MockUserInput } from "./mock-terminal.js";
@@ -0,0 +1 @@
1
+ import{TerminalRenderer}from"../terminal-renderer.js";import{AshTUIRunner}from"../runner.js";import{MockScreen,MockUserInput}from"./mock-terminal.js";export{AshTUIRunner,MockScreen,MockUserInput,TerminalRenderer};
@@ -0,0 +1,28 @@
1
+ import { EventEmitter } from "node:events";
2
+ import type { TerminalInput, TerminalOutput } from "../terminal-renderer.js";
3
+ export declare class MockUserInput extends EventEmitter implements TerminalInput {
4
+ isTTY: boolean;
5
+ rawModes: boolean[];
6
+ resumeCalls: number;
7
+ pauseCalls: number;
8
+ setRawMode(mode: boolean): this;
9
+ resume(): this;
10
+ pause(): this;
11
+ type(text: string): void;
12
+ enter(): void;
13
+ ctrlC(): void;
14
+ }
15
+ export declare class MockScreen extends EventEmitter implements TerminalOutput {
16
+ #private;
17
+ columns: number;
18
+ rows: number;
19
+ constructor({ columns, rows }: {
20
+ columns: number;
21
+ rows: number;
22
+ });
23
+ write(chunk: string | Uint8Array, encodingOrCallback?: BufferEncoding | ((error?: Error | null) => void), callback?: (error?: Error | null) => void): boolean;
24
+ resize(columns: number, rows: number): void;
25
+ snapshot(): string;
26
+ rawOutput(): string;
27
+ waitForText(text: string, timeoutMs?: number, getDebugOutput?: () => string): Promise<void>;
28
+ }
@@ -0,0 +1,3 @@
1
+ import{EventEmitter}from"node:events";const ansiControlSequencePattern=RegExp(`^\x1B\\[([0-9?;]*)([ -/]*)([@-~])`);var MockUserInput=class extends EventEmitter{isTTY=!0;rawModes=[];resumeCalls=0;pauseCalls=0;setRawMode(e){return this.rawModes.push(e),this}resume(){return this.resumeCalls+=1,this}pause(){return this.pauseCalls+=1,this}type(e){this.emit(`data`,Buffer.from(e))}enter(){this.emit(`data`,Buffer.from(`\r`))}ctrlC(){this.emit(`data`,Buffer.from(``))}},MockScreen=class extends EventEmitter{columns;rows;#e=``;#t=[];#n=0;#r=0;#i=[];constructor({columns:e,rows:t}){super(),this.columns=e,this.rows=t}write(e,t,n){let r=String(e);return this.#e+=r,this.#o(r),typeof t==`function`&&t(),n?.(),this.#a(),!0}resize(e,t){this.columns=e,this.rows=t,this.emit(`resize`)}snapshot(){return this.#t.join(`
2
+ `)}rawOutput(){return this.#e}async waitForText(e,t=1e3,n=()=>this.snapshot()){this.snapshot().includes(e)||await new Promise((r,i)=>{let a={text:e,resolve:r,reject:i,timeout:setTimeout(()=>{this.#i=this.#i.filter(e=>e!==a),i(Error(`Timed out waiting for screen text: ${e}\n\nScreen:\n${n()}`))},t)};this.#i.push(a)})}#a(){let e=this.snapshot();for(let t of this.#i.slice())e.includes(t.text)&&(clearTimeout(t.timeout),this.#i=this.#i.filter(e=>e!==t),t.resolve())}#o(e){let t=0;for(;t<e.length;){if(e[t]===`\x1B`){let n=this.#s(e,t);if(n>t){t=n;continue}}let n=e[t];if(t+=1,n!==void 0){if(n===`
3
+ `){this.#n+=1,this.#r=0;continue}if(n===`\r`){this.#r=0;continue}this.#c(n)}}}#s(e,n){let r=e.slice(n).match(ansiControlSequencePattern);if(!r)return n;let[i,a,,o]=r,s=a?a.split(`;`):[];return o===`H`&&s.length===0?(this.#n=0,this.#r=0):o===`J`&&s[0]===`2`?this.#t=[]:o===`K`&&s[0]===`2`?(this.#t[this.#n]=``,this.#r=0):o===`H`&&(this.#n=Number(s[0]??1)-1,this.#r=Number(s[1]??1)-1),n+i.length}#c(e){let t=this.#t[this.#n]??``,n=t.slice(0,this.#r)+e+t.slice(this.#r+e.length);this.#t[this.#n]=n,this.#r+=e.length}};export{MockScreen,MockUserInput};
@@ -0,0 +1,32 @@
1
+ import type { ClientOptions } from "#client/index.js";
2
+ import type { TuiDisplayOptions } from "./types.js";
3
+ /**
4
+ * Options for running the `ash dev` terminal UI against a server URL.
5
+ */
6
+ export interface RunDevelopmentTuiInput extends TuiDisplayOptions {
7
+ /**
8
+ * The Ash server URL the TUI connects to — either the in-process dev
9
+ * server started by `ash dev`, or a remote `--url` target.
10
+ */
11
+ readonly serverUrl: string;
12
+ }
13
+ /**
14
+ * Runs the `ash dev` terminal UI against the given server URL until the
15
+ * user exits.
16
+ *
17
+ * Builds the client with the same development auth/header resolution the
18
+ * REPL's dev client uses (`#services/dev-client`): local hosts skip the
19
+ * Vercel OIDC bearer, remote hosts attach it alongside any protection
20
+ * bypass headers. The configured client is handed to the runner so its
21
+ * subagent child-session streams inherit the same auth. Turn-dispatch
22
+ * failures — including the Vercel Deployment Protection challenge — are
23
+ * formatted into the inline error region rather than crashing the command.
24
+ */
25
+ /**
26
+ * Builds the client options the `ash dev` TUI connects with, matching the
27
+ * REPL's dev client: local hosts skip the Vercel OIDC bearer (the framework's
28
+ * `localDev()` channel auth accepts unauthenticated calls); remote hosts
29
+ * attach it alongside any protection-bypass headers resolved per request.
30
+ */
31
+ export declare function resolveDevTuiClientOptions(serverUrl: string): ClientOptions;
32
+ export declare function runDevelopmentTui(input: RunDevelopmentTuiInput): Promise<void>;
@@ -0,0 +1 @@
1
+ import{AshTUIRunner}from"./runner.js";import{toErrorMessage}from"#shared/errors.js";import{isLocalDevelopmentServerUrl,resolveDevelopmentClientHeaders,resolveDevelopmentOidcToken}from"#services/dev-client/request-headers.js";import{formatVercelAuthChallengeMessage,isVercelAuthChallenge}from"#services/dev-client/vercel-auth-error.js";import{Client}from"#client/index.js";function resolveDevTuiClientOptions(e){let t={headers:()=>resolveDevelopmentClientHeaders({serverUrl:e}),host:e};return isLocalDevelopmentServerUrl(e)?t:{...t,auth:{bearer:resolveDevelopmentOidcToken}}}async function runDevelopmentTui(n){let{serverUrl:r,...i}=n,a=new Client(resolveDevTuiClientOptions(r));await new AshTUIRunner({...i,session:a.session(),client:a,formatTransportError:e=>isVercelAuthChallenge(e)?formatVercelAuthChallengeMessage({serverUrl:r}):toErrorMessage(e)}).run()}export{resolveDevTuiClientOptions,runDevelopmentTui};
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Controls how terminal UI sections for stream parts are displayed.
3
+ */
4
+ export type TerminalPartDisplayMode = "full" | "collapsed" | "auto-collapsed" | "hidden";
5
+ /**
6
+ * Controls which usage statistic is shown for assistant responses.
7
+ */
8
+ export type AssistantResponseStatsMode = "tokens" | "tokensPerSecond";
9
+ /**
10
+ * Controls which in-process server/agent logs the TUI surfaces as inline
11
+ * regions. `all` shows both stdout and stderr; `stderr` shows only stderr;
12
+ * `none` suppresses log regions entirely.
13
+ */
14
+ export type LogDisplayMode = "all" | "stderr" | "none";
15
+ /**
16
+ * Display options shared by the terminal UI runner and renderer.
17
+ */
18
+ export type TuiDisplayOptions = {
19
+ /**
20
+ * The title shown in the terminal UI.
21
+ */
22
+ name?: string;
23
+ /**
24
+ * How tool calls should render.
25
+ */
26
+ tools?: TerminalPartDisplayMode;
27
+ /**
28
+ * How reasoning parts should render.
29
+ */
30
+ reasoning?: TerminalPartDisplayMode;
31
+ /**
32
+ * How subagent sections should render. `full` shows every nested child
33
+ * event line and the subagent's output; `auto-collapsed` collapses the
34
+ * section once the subagent reaches `done`; `collapsed` always shows
35
+ * only the header; `hidden` skips the section entirely.
36
+ */
37
+ subagents?: TerminalPartDisplayMode;
38
+ /**
39
+ * How MCP connection authorization sections should render. `full`
40
+ * shows the challenge URL and any user code; `auto-collapsed`
41
+ * collapses the section once authorization reaches a terminal
42
+ * outcome (`authorized`, `declined`, `failed`, `timed-out`);
43
+ * `collapsed` always shows only the header. `hidden` is supported
44
+ * but strongly discouraged: a hidden auth challenge looks identical
45
+ * to a hung turn from the user's perspective.
46
+ */
47
+ connectionAuth?: TerminalPartDisplayMode;
48
+ /**
49
+ * Which statistic to show in assistant response headers.
50
+ *
51
+ * @default "tokensPerSecond"
52
+ */
53
+ assistantResponseStats?: AssistantResponseStatsMode;
54
+ /**
55
+ * The model context window size in tokens.
56
+ *
57
+ * When provided, the terminal UI shows the current total token usage as a
58
+ * percentage of this context window.
59
+ */
60
+ contextSize?: number;
61
+ /**
62
+ * Which in-process server/agent logs to surface as inline regions.
63
+ * Output is always captured so it cannot corrupt the frame; this only
64
+ * controls what is rendered. Defaults to `all` — the TUI is primarily a
65
+ * development tool.
66
+ */
67
+ logs?: LogDisplayMode;
68
+ };
@@ -0,0 +1 @@
1
+ export{};