experimental-ash 0.55.0 → 0.55.2
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 +13 -0
- package/dist/docs/public/advanced/cli-build-and-debugging.md +1 -1
- package/dist/docs/public/advanced/dev-tui.md +202 -0
- package/dist/docs/public/advanced/meta.json +1 -0
- package/dist/docs/public/advanced/vercel-deployment.md +3 -3
- package/dist/docs/public/getting-started.mdx +1 -1
- package/dist/docs/public/human-in-the-loop.mdx +4 -4
- package/dist/docs/public/onboarding.md +2 -2
- package/dist/src/cli/dev/{repl.d.ts → repl/repl.d.ts} +1 -1
- package/dist/src/cli/dev/{repl.js → repl/repl.js} +1 -1
- package/dist/src/cli/dev/tui/layout.d.ts +24 -0
- package/dist/src/cli/dev/tui/layout.js +3 -0
- package/dist/src/cli/dev/tui/markdown.d.ts +14 -0
- package/dist/src/cli/dev/tui/markdown.js +3 -0
- package/dist/src/cli/dev/tui/runner.d.ts +205 -0
- package/dist/src/cli/dev/tui/runner.js +1 -0
- package/dist/src/cli/dev/tui/terminal-frame-buffer.d.ts +21 -0
- package/dist/src/cli/dev/tui/terminal-frame-buffer.js +2 -0
- package/dist/src/cli/dev/tui/terminal-renderer.d.ts +111 -0
- package/dist/src/cli/dev/tui/terminal-renderer.js +12 -0
- package/dist/src/cli/dev/tui/terminal-text.d.ts +6 -0
- package/dist/src/cli/dev/tui/terminal-text.js +1 -0
- package/dist/src/cli/dev/tui/test/index.d.ts +10 -0
- package/dist/src/cli/dev/tui/test/index.js +1 -0
- package/dist/src/cli/dev/tui/test/mock-terminal.d.ts +28 -0
- package/dist/src/cli/dev/tui/test/mock-terminal.js +3 -0
- package/dist/src/cli/dev/tui/tui.d.ts +32 -0
- package/dist/src/cli/dev/tui/tui.js +1 -0
- package/dist/src/cli/dev/tui/types.d.ts +68 -0
- package/dist/src/cli/dev/tui/types.js +1 -0
- package/dist/src/cli/run.d.ts +66 -0
- package/dist/src/cli/run.js +2 -2
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/evals/scorers/autoevals.js +1 -1
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/harness/code-mode.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
- package/dist/src/public/channels/ash.js +1 -1
- package/package.json +6 -1
- /package/dist/src/cli/dev/{input-requests.d.ts → repl/input-requests.d.ts} +0 -0
- /package/dist/src/cli/dev/{input-requests.js → repl/input-requests.js} +0 -0
- /package/dist/src/cli/dev/{input.d.ts → repl/input.d.ts} +0 -0
- /package/dist/src/cli/dev/{input.js → repl/input.js} +0 -0
- /package/dist/src/cli/dev/{terminal.d.ts → repl/terminal.d.ts} +0 -0
- /package/dist/src/cli/dev/{terminal.js → repl/terminal.js} +0 -0
|
@@ -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):`[H[2J${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[2K${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&§ionMatchesCache(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&§ionMatchesCache(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{};
|
package/dist/src/cli/run.d.ts
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import type { AssistantResponseStatsMode, LogDisplayMode, TerminalPartDisplayMode, TuiDisplayOptions } from "#cli/dev/tui/types.js";
|
|
1
2
|
interface CliLogger {
|
|
2
3
|
error(message: string): void;
|
|
3
4
|
log(message: string): void;
|
|
4
5
|
}
|
|
6
|
+
interface DevelopmentCliOptions {
|
|
7
|
+
assistantResponseStats?: AssistantResponseStatsMode;
|
|
8
|
+
connectionAuth?: TerminalPartDisplayMode;
|
|
9
|
+
contextSize?: number;
|
|
10
|
+
host?: string;
|
|
11
|
+
logs?: LogDisplayMode;
|
|
12
|
+
name?: string;
|
|
13
|
+
port?: number;
|
|
14
|
+
reasoning?: TerminalPartDisplayMode;
|
|
15
|
+
repl?: boolean;
|
|
16
|
+
subagents?: TerminalPartDisplayMode;
|
|
17
|
+
tools?: TerminalPartDisplayMode;
|
|
18
|
+
ui?: boolean;
|
|
19
|
+
url?: string;
|
|
20
|
+
}
|
|
5
21
|
interface DevelopmentServerHandle {
|
|
6
22
|
readonly url: string;
|
|
7
23
|
close(): Promise<void>;
|
|
@@ -19,6 +35,9 @@ interface CliRuntimeDependencies {
|
|
|
19
35
|
runDevelopmentRepl(input: {
|
|
20
36
|
serverUrl: string;
|
|
21
37
|
}): Promise<void>;
|
|
38
|
+
runDevelopmentTui(input: {
|
|
39
|
+
serverUrl: string;
|
|
40
|
+
} & TuiDisplayOptions): Promise<void>;
|
|
22
41
|
runEvalCommand(options: EvalCliOptions, logger: CliLogger): Promise<void>;
|
|
23
42
|
startHost(appRoot: string, options?: {
|
|
24
43
|
host?: string;
|
|
@@ -39,6 +58,53 @@ interface EvalCliOptions {
|
|
|
39
58
|
timeout?: string;
|
|
40
59
|
url?: string;
|
|
41
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The interactive UI `ash dev` runs against a server.
|
|
63
|
+
*
|
|
64
|
+
* - `tui` — the default terminal UI.
|
|
65
|
+
* - `repl` — the classic line-based REPL (`--repl`).
|
|
66
|
+
* - `headless` — no UI: just keep the server running (`--no-ui`, or a
|
|
67
|
+
* non-interactive terminal).
|
|
68
|
+
*
|
|
69
|
+
* Exported for unit coverage of the flag-routing contract.
|
|
70
|
+
*/
|
|
71
|
+
export type DevUiMode = "tui" | "repl" | "headless";
|
|
72
|
+
/**
|
|
73
|
+
* Resolves which UI `ash dev` should run from the parsed flags and whether
|
|
74
|
+
* the terminal is interactive. `--no-ui` and non-TTY terminals force
|
|
75
|
+
* `headless`; `--repl` selects the classic REPL; otherwise the terminal UI
|
|
76
|
+
* is the default.
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveDevUiMode(input: {
|
|
79
|
+
options: Pick<DevelopmentCliOptions, "repl" | "ui">;
|
|
80
|
+
interactive: boolean;
|
|
81
|
+
}): DevUiMode;
|
|
82
|
+
/**
|
|
83
|
+
* Resolves the terminal UI's header title: an explicit `--name`, else the
|
|
84
|
+
* remote server's host (for `--url`), else the humanized app-folder name
|
|
85
|
+
* (e.g. `apps/weather-agent` → "Weather Agent"). Returns `undefined` when
|
|
86
|
+
* nothing meaningful can be derived, so the runner falls back to its own
|
|
87
|
+
* default.
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveTuiTitle(input: {
|
|
90
|
+
name: string | undefined;
|
|
91
|
+
remoteServerUrl: string | undefined;
|
|
92
|
+
appRoot: string;
|
|
93
|
+
}): string | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* Builds the terminal-UI display options for `ash dev`. Tools default to
|
|
96
|
+
* `auto-collapsed` and reasoning to `full` — the combination the example
|
|
97
|
+
* apps' former `tui` scripts shipped — while the other dimensions fall
|
|
98
|
+
* back to the runner's own defaults when unset.
|
|
99
|
+
*/
|
|
100
|
+
export declare function resolveTuiDisplayOptions(options: DevelopmentCliOptions): TuiDisplayOptions;
|
|
101
|
+
/**
|
|
102
|
+
* Rewrites the deprecated `ash dev --no-repl` flag to its replacement
|
|
103
|
+
* `--no-ui`. The TUI is now the default UI, so "no REPL" no longer
|
|
104
|
+
* describes "no interactive UI"; `--no-repl` is kept as a silent alias so
|
|
105
|
+
* existing scripts and programmatic spawns (e.g. `withAsh`) keep working.
|
|
106
|
+
*/
|
|
107
|
+
export declare function rewriteDeprecatedReplFlag(argv: readonly string[]): string[];
|
|
42
108
|
/**
|
|
43
109
|
* Runs the Ash CLI entrypoint.
|
|
44
110
|
*/
|
package/dist/src/cli/run.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createCliTheme,renderCliTaggedLine}from"#cli/ui/output.js";import{Command,CommanderError,InvalidArgumentError}from"#compiled/commander/index.js";import{resolveApplicationRoot}from"#internal/application/paths.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{parseDevelopmentServerUrl}from"#cli/dev/url.js";async function loadBuildHost(){return(await import(`#internal/nitro/host.js`)).buildApplication}async function loadPrintApplicationInfo(){return(await import(`#cli/commands/info.js`)).printApplicationInfo}async function loadRunDevelopmentRepl(){return(await import(`#cli/dev/repl.js`)).runDevelopmentRepl}async function loadRunEvalCommand(){return(await import(`#evals/cli/eval.js`)).runEvalCommand}async function loadStartHost(){return(await import(`#internal/nitro/host.js`)).startDevelopmentServer}async function loadStartProductionHost(){return(await import(`#internal/nitro/host.js`)).startProductionServer}function shouldPrintCliBootBanner(e){return e.name()===`info`||e.name()===`dev`}async function waitForShutdownSignal(e){await new Promise((t,n)=>{let r=!1,cleanup=()=>{process.off(`SIGINT`,handleSignal),process.off(`SIGTERM`,handleSignal)},handleSignal=()=>{r||(r=!0,cleanup(),e.close().then(t,n))};process.once(`SIGINT`,handleSignal),process.once(`SIGTERM`,handleSignal)})}async function waitForProductionServer(e){await Promise.race([e.wait(),waitForShutdownSignal({close:()=>e.close()})])}function parsePortOption(e){if(!/^-?\d+$/.test(e))throw new InvalidArgumentError(`Expected a numeric port, received "${e}".`);let t=Number(e);if(t<0||t>65535)throw new InvalidArgumentError(`Expected a port between 0 and 65535, received "${e}".`);return t}function hasInteractiveTerminal(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function rewriteDevelopmentUrlShorthand(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function resolveRemoteDevelopmentServerUrl(e){if(e.url){if(e.host!==void 0)throw new InvalidArgumentError(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new InvalidArgumentError(`The --port option cannot be used with --url.`);if(e.
|
|
1
|
+
import{basename}from"node:path";import{createCliTheme,renderCliTaggedLine}from"#cli/ui/output.js";import{Command,CommanderError,InvalidArgumentError}from"#compiled/commander/index.js";import{resolveApplicationRoot}from"#internal/application/paths.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{parseDevelopmentServerUrl}from"#cli/dev/url.js";async function loadBuildHost(){return(await import(`#internal/nitro/host.js`)).buildApplication}async function loadPrintApplicationInfo(){return(await import(`#cli/commands/info.js`)).printApplicationInfo}async function loadRunDevelopmentRepl(){return(await import(`#cli/dev/repl/repl.js`)).runDevelopmentRepl}async function loadRunDevelopmentTui(){return(await import(`#cli/dev/tui/tui.js`)).runDevelopmentTui}async function loadRunEvalCommand(){return(await import(`#evals/cli/eval.js`)).runEvalCommand}async function loadStartHost(){return(await import(`#internal/nitro/host.js`)).startDevelopmentServer}async function loadStartProductionHost(){return(await import(`#internal/nitro/host.js`)).startProductionServer}function shouldPrintCliBootBanner(e){return e.name()===`info`||e.name()===`dev`}async function waitForShutdownSignal(e){await new Promise((t,n)=>{let r=!1,cleanup=()=>{process.off(`SIGINT`,handleSignal),process.off(`SIGTERM`,handleSignal)},handleSignal=()=>{r||(r=!0,cleanup(),e.close().then(t,n))};process.once(`SIGINT`,handleSignal),process.once(`SIGTERM`,handleSignal)})}async function waitForProductionServer(e){await Promise.race([e.wait(),waitForShutdownSignal({close:()=>e.close()})])}function parsePortOption(e){if(!/^-?\d+$/.test(e))throw new InvalidArgumentError(`Expected a numeric port, received "${e}".`);let t=Number(e);if(t<0||t>65535)throw new InvalidArgumentError(`Expected a port between 0 and 65535, received "${e}".`);return t}const DISPLAY_MODES=new Set([`full`,`collapsed`,`auto-collapsed`,`hidden`]),STATS_MODES=new Set([`tokens`,`tokensPerSecond`]),LOG_MODES=new Set([`all`,`stderr`,`none`]);function parseDisplayMode(e){if(!DISPLAY_MODES.has(e))throw new InvalidArgumentError(`Expected one of ${[...DISPLAY_MODES].join(`, `)}, received "${e}".`);return e}function parseStatsMode(e){if(!STATS_MODES.has(e))throw new InvalidArgumentError(`Expected one of ${[...STATS_MODES].join(`, `)}, received "${e}".`);return e}function parseLogsMode(e){if(!LOG_MODES.has(e))throw new InvalidArgumentError(`Expected one of ${[...LOG_MODES].join(`, `)}, received "${e}".`);return e}function parseContextSizeOption(e){let t=Number(e);if(!Number.isFinite(t)||t<=0)throw new InvalidArgumentError(`Expected a positive number, received "${e}".`);return t}function resolveDevUiMode(e){return e.options.ui===!1||!e.interactive?`headless`:e.options.repl===!0?`repl`:`tui`}function resolveTuiTitle(t){if(t.name!==void 0&&t.name.length>0)return t.name;if(t.remoteServerUrl!==void 0)try{return new URL(t.remoteServerUrl).host}catch{return}let n=humanizeProjectName(basename(t.appRoot));return n.length>0?n:void 0}function humanizeProjectName(e){return e.replace(/[-_.]+/gu,` `).trim().split(/\s+/u).filter(e=>e.length>0).map(e=>e[0].toUpperCase()+e.slice(1)).join(` `)}function resolveTuiDisplayOptions(e){let t={reasoning:e.reasoning??`full`,tools:e.tools??`auto-collapsed`};return e.subagents!==void 0&&(t.subagents=e.subagents),e.connectionAuth!==void 0&&(t.connectionAuth=e.connectionAuth),e.assistantResponseStats!==void 0&&(t.assistantResponseStats=e.assistantResponseStats),e.contextSize!==void 0&&(t.contextSize=e.contextSize),e.logs!==void 0&&(t.logs=e.logs),t}function hasInteractiveTerminal(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function rewriteDeprecatedReplFlag(e){return e[0]===`dev`?e.map(e=>e===`--no-repl`?`--no-ui`:e):[...e]}function rewriteDevelopmentUrlShorthand(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function resolveRemoteDevelopmentServerUrl(e){if(e.url){if(e.host!==void 0)throw new InvalidArgumentError(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new InvalidArgumentError(`The --port option cannot be used with --url.`);if(e.ui===!1)throw new InvalidArgumentError(`The --no-ui option cannot be used with --url.`);return e.url}}function createCliProgram(e,i){let a=resolveApplicationRoot(),l=resolveInstalledPackageInfo().version,u=new Command,d=createCliTheme();u.name(`ash`).description(`Build and run an Ash application.`).version(l).showHelpAfterError().exitOverride().hook(`preAction`,(t,n)=>{shouldPrintCliBootBanner(n)&&e.log(`Ash (v${l})`)}).configureOutput({writeErr:t=>{e.error(t.trimEnd())},writeOut:t=>{e.log(t.trimEnd())}});let f=u.command(`channels`).description(`Manage user-authored channels in the current project.`);return f.command(`add [kind]`).description(`Add channels interactively, or scaffold a channel kind (slack | web).`).option(`-f, --force`,`Overwrite existing channel files`).option(`-y, --yes`,`Assume yes for confirmations; requires an explicit channel kind`).action(async(t,n)=>{let{runChannelsAddCommand:r}=await import(`#cli/commands/channels.js`);await r(e,a,{kind:t,options:n})}),f.command(`list`).description(`List user-authored channels in the current project.`).option(`--json`,`Output as JSON`).action(async t=>{let{runChannelsListCommand:n}=await import(`#cli/commands/channels.js`);await n(e,a,t)}),u.command(`build`).description(`Build the current Ash application.`).action(async()=>{let{loadDevelopmentEnvironmentFiles:t}=await import(`#cli/dev/environment.js`);t(a);let r=await(i.buildHost??await loadBuildHost())(a);e.log(renderCliTaggedLine(d,{message:`built output at ${r}`,tag:`build`,tone:`success`}))}),u.command(`start`).description(`Start a built Ash application.`).option(`--host <host>`,`Host interface to bind`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,parsePortOption).action(async t=>{let{loadDevelopmentEnvironmentFiles:r}=await import(`#cli/dev/environment.js`);r(a);let o=await(i.startProductionHost??await loadStartProductionHost())(a,{host:t.host,port:t.port});e.log(renderCliTaggedLine(d,{message:`server listening at ${o.url}`,tag:`start`,tone:`success`})),await waitForProductionServer(o)}),u.command(`dev`).description(`Start the Ash development server or connect to an existing URL.`).option(`--host <host>`,`Host interface to bind`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,parsePortOption).option(`-u, --url <url>`,`Connect to an existing server URL`,parseDevelopmentServerUrl).option(`--repl`,`Use the classic line-based REPL instead of the default terminal UI`).option(`--no-ui`,`Start the server without an interactive UI`).option(`--name <name>`,`Title shown in the terminal UI (defaults to the app folder name)`).option(`--tools <mode>`,`How tool calls render: full | collapsed | auto-collapsed | hidden`,parseDisplayMode).option(`--reasoning <mode>`,`How reasoning renders: full | collapsed | auto-collapsed | hidden`,parseDisplayMode).option(`--subagents <mode>`,`How subagent sections render: full | collapsed | auto-collapsed | hidden`,parseDisplayMode).option(`--connection-auth <mode>`,`How connection authorization renders: full | collapsed | auto-collapsed | hidden`,parseDisplayMode).option(`--assistant-response-stats <mode>`,`Assistant header statistic: tokens | tokensPerSecond`,parseStatsMode).option(`--context-size <tokens>`,`Model context window size, shown as a usage percentage`,parseContextSizeOption).option(`--logs <mode>`,`Which server/agent logs to show: all | stderr | none`,parseLogsMode).addHelpText(`after`,`
|
|
2
2
|
You can also pass a bare URL as the only argument, for example: ash dev https://example.com
|
|
3
|
-
`).action(async
|
|
3
|
+
`).action(async t=>{let r=resolveRemoteDevelopmentServerUrl(t),{loadDevelopmentEnvironmentFiles:o}=await import(`#cli/dev/environment.js`);o(a);let runInteractiveUi=async(n,o)=>{if(e.log(``),o===`repl`){await(i.runDevelopmentRepl??await loadRunDevelopmentRepl())({serverUrl:n});return}let s=i.runDevelopmentTui??await loadRunDevelopmentTui(),c=resolveTuiDisplayOptions(t),l=resolveTuiTitle({name:t.name,remoteServerUrl:r,appRoot:a});l!==void 0&&(c.name=l),await s({serverUrl:n,...c})};if(r){e.log(renderCliTaggedLine(d,{message:`connecting to ${r}`,tag:`dev`,tone:`info`}));let i=resolveDevUiMode({options:t,interactive:hasInteractiveTerminal()});if(i===`headless`){e.log(renderCliTaggedLine(d,{message:`Interactive UI disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`}));return}await runInteractiveUi(r,i);return}let s=await(i.startHost??await loadStartHost())(a,{host:t.host,port:t.port}),c=!1,closeServer=async()=>{c||(c=!0,await s.close())};try{e.log(renderCliTaggedLine(d,{message:`server listening at ${s.url}`,tag:`dev`,tone:`success`}));let r=hasInteractiveTerminal(),i=resolveDevUiMode({options:t,interactive:r});if(i===`headless`)return t.ui!==!1&&!r&&e.log(renderCliTaggedLine(d,{message:`Interactive UI disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`})),await waitForShutdownSignal({close:closeServer});await runInteractiveUi(s.url,i)}finally{await closeServer()}}),u.command(`info`).description(`Print resolved application information.`).option(`--json`,`Output as JSON`).action(async t=>{await(i.printApplicationInfo??await loadPrintApplicationInfo())(e,a,t)}),u.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async t=>{await(i.runEvalCommand??await loadRunEvalCommand())(t,e)}),u}async function runCli(e=process.argv.slice(2),t=console,n={}){let r=createCliProgram(t,n),a=e.length===0?[`info`]:rewriteDevelopmentUrlShorthand(rewriteDeprecatedReplFlag(e));try{await r.parseAsync(a,{from:`user`})}catch(e){if(e instanceof CommanderError){if(e.exitCode===0)return;throw Error(e.message)}throw e}}export{resolveDevUiMode,resolveTuiDisplayOptions,resolveTuiTitle,rewriteDeprecatedReplFlag,runCli};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{LiveStepToolsKey,SessionDynamicToolMetadataKey,TurnDynamicToolMetadataKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{ALLOWED_DYNAMIC_TOOL_EVENTS,isBrandedToolEntry}from"#shared/dynamic-tool-definition.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{LiveStepToolsKey,SessionDynamicToolMetadataKey,TurnDynamicToolMetadataKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{jsonSchema,zodSchema}from"ai";import{ALLOWED_DYNAMIC_TOOL_EVENTS,isBrandedToolEntry}from"#shared/dynamic-tool-definition.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{buildResolveContext}from"#context/dynamic-resolve-context.js";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";const log=createLogger(`dynamic-tools`);function toHarnessToolDefinition(e,t){return{description:t.description,execute:e=>t.execute(e,buildCallbackContext()),inputSchema:convertInputSchema(t.inputSchema),name:e,needsApproval:t.needsApproval,...t.toModelOutput===void 0?{}:{toModelOutput:t.toModelOutput}}}function convertInputSchema(e){return typeof e==`object`&&e&&`~standard`in e?zodSchema(e):jsonSchema(e)}function qualifyDynamicToolNames(e,t,n){let r=Object.keys(n),i=[];if(r.length===0)return i;if(t)return i.push({name:e,entryKey:r[0],entry:n[r[0]]}),i;for(let t of r)i.push({name:`${e}__${t}`,entryKey:t,entry:n[t]});return i}function replayDynamicSessionTools(e,t){let n=[];for(let t of e){if(!t.executeStepFnName||!t.closureVars){log.warn(`Dynamic tool "${t.name}" has no registered step function — skipping on this step. The bundler transform may not have processed this tool file.`);continue}let e=lookupStepFunction(t.executeStepFnName);if(!e){log.warn(`Dynamic tool "${t.name}" references step function "${t.executeStepFnName}" which is not registered — skipping on this step.`);continue}n.push({description:t.description,execute:n=>e(t.closureVars,n,buildCallbackContext()),inputSchema:jsonSchema(t.inputSchema),name:t.name})}return n}function getStepRegistry(){let e=Symbol.for(`@workflow/core//registeredSteps`),t=globalThis,n=t[e];return n===void 0&&(n=new Map,t[e]=n),n}function lookupStepFunction(e){try{return getStepRegistry().get(e)||null}catch{return null}}function registerStepFunction(e,t){getStepRegistry().set(e,t)}function safeSerialize(e){try{return JSON.parse(JSON.stringify(e))}catch{return{}}}function durableKeyForEvent(e){switch(e){case`session.started`:return SessionDynamicToolMetadataKey;case`turn.started`:return TurnDynamicToolMetadataKey;default:return}}async function resolveToolsFromEvent(e,t,n,r){let a=await Promise.allSettled(t.map(async t=>{let i=t.events[n.type];if(i===void 0)return null;let a=await i(n,buildResolveContext(e,r));if(a==null)return null;let o,s;return isBrandedToolEntry(a)?(o={_single:a},s=!0):(o=a,s=!1),{resolver:t,entries:o,isSingle:s}})),o=[],s=[];for(let e of a){if(e.status===`rejected`){log.error(`Dynamic tool resolver (${n.type}) threw — skipping.`,{error:toErrorMessage(e.reason)});continue}if(e.value===null)continue;let{resolver:t,entries:r,isSingle:a}=e.value,c=qualifyDynamicToolNames(t.slug,a,r);for(let{name:e,entryKey:n,entry:r}of c){s.push(toHarnessToolDefinition(e,r));let i=`__executeStepFn`in r?r.__executeStepFn:void 0,a=`__closureVars`in r?r.__closureVars:void 0,c=i?.stepId,l=a===void 0?void 0:safeSerialize(a);if(c===void 0){let e=`ash:framework-dynamic:${t.slug}:${n}`,i=r.execute.bind(r);registerStepFunction(e,(e,t,n)=>i(t,n)),c=e,l={}}o.push({name:e,description:r.description,inputSchema:normalizeJsonSchemaDefinition(r.inputSchema),resolverSlug:t.slug,entryKey:n,executeStepFnName:c,closureVars:l})}}return{metadata:o,liveTools:s}}async function dispatchDynamicToolEvent(e){let{ctx:n,resolvers:r,event:i,messages:a}=e;if(!ALLOWED_DYNAMIC_TOOL_EVENTS.has(i.type))return;let o=r.filter(e=>e.eventNames.includes(i.type));if(o.length===0)return;let{metadata:c,liveTools:l}=await resolveToolsFromEvent(n,o,i,a);if(i.type===`step.started`){n.setVirtualContext(LiveStepToolsKey,l);return}let u=durableKeyForEvent(i.type);if(u===void 0)return;let d=new Set(o.map(e=>e.slug)),f=(n.get(u)??[]).filter(e=>!d.has(e.resolverSlug));n.set(u,[...f,...c])}export{dispatchDynamicToolEvent,replayDynamicSessionTools};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ClosedQA,Factuality,Sql,Summary}from"../../node_modules/.pnpm/autoevals@0.0.132_ws@8.21.0/node_modules/autoevals/jsdist/index.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import
|
|
1
|
+
import{ClosedQA,Factuality,Sql,Summary}from"../../node_modules/.pnpm/autoevals@0.0.132_ws@8.21.0/node_modules/autoevals/jsdist/index.js";import"ai";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{createAutoevalsClient}from"#evals/scorers/autoevals-client.js";function factuality(e={}){return languageScorer(Factuality,e)}function summary(e={}){return languageScorer(Summary,e)}function sql(e={}){return languageScorer(Sql,e)}function closedQA(t){return async n=>{let r=t.model??n.model;return toScorerResult(await ClosedQA({input:n.input,output:n.output,criteria:t.criteria,model:formatLanguageModelGatewayId(r),client:createAutoevalsClient({languageModel:r,providerOptions:mergeProviderOptions(n,t.modelOptions)})}))}}function languageScorer(e,t){return async n=>{let r=t.model??n.model;return toScorerResult(await e({input:n.input,output:n.output,expected:n.expected,model:formatLanguageModelGatewayId(r),client:createAutoevalsClient({languageModel:r,providerOptions:mergeProviderOptions(n,t.modelOptions)})}))}}function mergeProviderOptions(e,t){let n=e.modelOptions?.providerOptions,r=t?.providerOptions;if(!(n===void 0&&r===void 0))return{...n,...r}}function toScorerResult(e){return{name:e.name,score:e.score,metadata:e.metadata}}export{closedQA,factuality,sql,summary};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{jsonSchema}from"ai";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{createToolLoopHarness}from"#harness/tool-loop.js";import{resolveCodeModeEnabled}from"#shared/code-mode.js";import{resolveRuntimeModelReference}from"#runtime/agent/resolve-model.js";import{findRegisteredRuntimeTool}from"#runtime/tools/registry.js";import{createToolCompactionHandler}from"#execution/tool-compaction.js";const log=createLogger(`execution.node-step`);function createExecutionNodeStep(e){let t=createRuntimeModelResolver(e.compiledArtifactsSource),n=createNodeHarnessTools({node:e.node}),r=createToolCompactionHandler(collectResolvedTools(e.node));return createToolLoopHarness({capabilities:e.capabilities,codeMode:resolveCodeModeEnabled(e.node.agent.config?.experimental?.codeMode),handleEvent:e.handleEvent,mode:e.mode,onCompaction:r,resolveModel:t,runtimeIdentity:buildRuntimeIdentity(e.node),tools:n})}function buildRuntimeIdentity(e){let t=resolveInstalledPackageInfo(),r={agentId:e.turnAgent.id,agentName:e.agent.config?.name,ashVersion:t.version,modelId:e.turnAgent.model.id},i=process.env.VERCEL_GIT_COMMIT_SHA?.trim(),a=process.env.VERCEL_GIT_COMMIT_REF?.trim(),o=process.env.VERCEL_DEPLOYMENT_CREATED_AT?.trim();return i||a||o?{...r,build:{deployedAt:o||void 0,gitBranch:a||void 0,gitSha:i||void 0}}:r}function createRuntimeModelResolver(e){return t=>resolveRuntimeModelReference(t,{compiledArtifactsSource:e})}function collectResolvedTools(e){return[...e.toolRegistry.toolsByName.values()].map(e=>e.definition)}function createNodeHarnessTools(e){let t=new Map;for(let n of e.node.turnAgent.tools){let r=resolveHarnessToolDefinition({node:e.node,tool:n});r!==null&&t.set(n.name,r)}return t}function resolveHarnessToolDefinition(e){if(e.tool.kind===`subagent`)return{description:e.tool.description??``,inputSchema:jsonSchema(e.tool.inputSchema??{}),name:e.tool.name,runtimeAction:{kind:`subagent-call`,nodeId:e.tool.nodeId,subagentName:e.tool.name}};if(e.tool.kind===`remote`)return{description:e.tool.description??``,inputSchema:jsonSchema(e.tool.inputSchema??{}),name:e.tool.name,runtimeAction:{kind:`remote-agent-call`,nodeId:e.tool.nodeId,remoteAgentName:e.tool.name,subagentName:e.tool.name}};let n=findRegisteredRuntimeTool(e.node.toolRegistry,e.tool.name);if(n===null)return log.warn(`declared tool is not registered — omitting from toolset`,{toolName:e.tool.name,nodeId:e.node.nodeId}),null;let i=n.definition,a=i.sourceId.startsWith(`ash:`),o=i.execute;return{approvalKey:i.approvalKey,description:i.description,execute:o===void 0?void 0:a?o:e=>o(e,buildCallbackContext()),inputSchema:i.inputStandardSchema??jsonSchema(i.inputSchema??{}),name:i.name,needsApproval:i.needsApproval,toModelOutput:i.toModelOutput}}export{createExecutionNodeStep,createNodeHarnessTools};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{contextStorage}from"#context/container.js";import
|
|
1
|
+
import"ai";import{contextStorage}from"#context/container.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{isAuthorizationSignal}from"#harness/authorization.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,markCodeModeToolExecutionOptions,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{buildDynamicTools}from"#context/build-dynamic-tools.js";import{buildToolSet}from"#harness/tools.js";function createAshCodeModeOptions(e={}){let t={approval:{mode:`interrupt`}};return e.lifecycle!==void 0&&(t.lifecycle=e.lifecycle),t}async function applyCodeModeToToolSet(e){let r={},i={};for(let[t,n]of Object.entries(e.tools)){if(isDirectTool(n,e.harnessTools.get(t))){i[t]=n;continue}r[t]=wrapHostToolForCodeMode(n)}if(Object.keys(r).length>0){let{createCodeModeTool:a}=await loadCodeModeModule();i[CODE_MODE_TOOL_NAME]=a(r,createAshCodeModeOptions({lifecycle:e.lifecycle}))}return{hostTools:r,modelTools:i}}async function buildCodeModeHostTools(t){let n=buildToolSet({approvedTools:t.approvedTools,capabilities:t.capabilities,tools:t.tools}),r=contextStorage.getStore();if(r!==void 0){let e=buildDynamicTools(r);for(let t of e)n[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute}}return(await applyCodeModeToToolSet({harnessTools:t.tools,tools:n})).hostTools}function isDirectTool(e,t){return e.execute===void 0||t?.runtimeAction!==void 0}function wrapHostToolForCodeMode(t){let o=t.execute;if(o===void 0)return t;let s=contextStorage.getStore(),invoke=async(e,t)=>{let s=await resolveExecuteOutput(o(e,markCodeModeToolExecutionOptions(t)));if(isAuthorizationSignal(s)){let{requestCodeModeInterrupt:t}=await loadCodeModeModule(),r=s.challenges[0]?.name;r&&t({args:toCodeModeConnectionAuthArgs(e),challenges:s.challenges,connectionName:r,kind:CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toolName:``})}return s};return{...t,execute:(t,n)=>s===void 0?invoke(t,n):contextStorage.run(s,invoke,t,n)}}async function resolveExecuteOutput(e){if(isAsyncIterable(e)){let t;for await(let n of e)t=n;return t}return await e}function isAsyncIterable(e){return typeof e==`object`&&!!e&&Symbol.asyncIterator in e&&typeof e[Symbol.asyncIterator]==`function`}export{applyCodeModeToToolSet,buildCodeModeHostTools,createAshCodeModeOptions};
|