eve 0.25.3 → 0.26.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 (221) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/src/chunks/{use-eve-agent-Cy73lgA6.js → use-eve-agent-BkztZA0v.js} +76 -55
  3. package/dist/src/chunks/{use-eve-agent-CQIeqmKc.js → use-eve-agent-CruinZmd.js} +76 -55
  4. package/dist/src/cli/banner.d.ts +5 -0
  5. package/dist/src/cli/banner.js +1 -1
  6. package/dist/src/cli/commands/logs-events.d.ts +36 -0
  7. package/dist/src/cli/commands/logs-events.js +2 -0
  8. package/dist/src/cli/commands/logs.d.ts +64 -0
  9. package/dist/src/cli/commands/logs.js +4 -0
  10. package/dist/src/cli/dev/diagnostic-dump.d.ts +60 -0
  11. package/dist/src/cli/dev/diagnostic-dump.js +1 -0
  12. package/dist/src/cli/dev/diagnostic-sink.d.ts +37 -0
  13. package/dist/src/cli/dev/diagnostic-sink.js +1 -0
  14. package/dist/src/cli/dev/diagnostics.d.ts +42 -0
  15. package/dist/src/cli/dev/diagnostics.js +1 -0
  16. package/dist/src/cli/dev/tui/blocks.d.ts +60 -8
  17. package/dist/src/cli/dev/tui/blocks.js +8 -8
  18. package/dist/src/cli/dev/tui/command-typeahead.js +1 -1
  19. package/dist/src/cli/dev/tui/diagnostic-presentation.d.ts +16 -0
  20. package/dist/src/cli/dev/tui/diagnostic-presentation.js +3 -0
  21. package/dist/src/cli/dev/tui/errors.d.ts +21 -16
  22. package/dist/src/cli/dev/tui/errors.js +1 -1
  23. package/dist/src/cli/dev/tui/file-content-cache.d.ts +35 -0
  24. package/dist/src/cli/dev/tui/file-content-cache.js +3 -0
  25. package/dist/src/cli/dev/tui/line-diff.d.ts +24 -0
  26. package/dist/src/cli/dev/tui/line-diff.js +1 -0
  27. package/dist/src/cli/dev/tui/line-editor.js +1 -1
  28. package/dist/src/cli/dev/tui/markdown.d.ts +2 -1
  29. package/dist/src/cli/dev/tui/markdown.js +7 -3
  30. package/dist/src/cli/dev/tui/model-editor.d.ts +103 -0
  31. package/dist/src/cli/dev/tui/model-editor.js +1 -0
  32. package/dist/src/cli/dev/tui/prompt-command-handler.d.ts +1 -1
  33. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  34. package/dist/src/cli/dev/tui/prompt-placeholder.d.ts +12 -0
  35. package/dist/src/cli/dev/tui/prompt-placeholder.js +1 -0
  36. package/dist/src/cli/dev/tui/question-panel.d.ts +24 -0
  37. package/dist/src/cli/dev/tui/question-panel.js +1 -0
  38. package/dist/src/cli/dev/tui/rail.d.ts +15 -0
  39. package/dist/src/cli/dev/tui/rail.js +1 -0
  40. package/dist/src/cli/dev/tui/remote-auth-command.js +1 -1
  41. package/dist/src/cli/dev/tui/runner.d.ts +27 -65
  42. package/dist/src/cli/dev/tui/runner.js +1 -1
  43. package/dist/src/cli/dev/tui/setup-commands.d.ts +1 -1
  44. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  45. package/dist/src/cli/dev/tui/setup-flow.d.ts +3 -0
  46. package/dist/src/cli/dev/tui/setup-issues.js +1 -1
  47. package/dist/src/cli/dev/tui/setup-panel.d.ts +33 -0
  48. package/dist/src/cli/dev/tui/setup-panel.js +4 -4
  49. package/dist/src/cli/dev/tui/status-line.d.ts +8 -6
  50. package/dist/src/cli/dev/tui/status-line.js +1 -1
  51. package/dist/src/cli/dev/tui/stream-format.d.ts +16 -3
  52. package/dist/src/cli/dev/tui/stream-format.js +1 -1
  53. package/dist/src/cli/dev/tui/subagent-pump.d.ts +127 -0
  54. package/dist/src/cli/dev/tui/subagent-pump.js +1 -0
  55. package/dist/src/cli/dev/tui/terminal-renderer.d.ts +37 -1
  56. package/dist/src/cli/dev/tui/terminal-renderer.js +12 -10
  57. package/dist/src/cli/dev/tui/test/fake-setup-flow-renderer.js +1 -1
  58. package/dist/src/cli/dev/tui/test/mock-terminal.d.ts +7 -0
  59. package/dist/src/cli/dev/tui/test/mock-terminal.js +1 -1
  60. package/dist/src/cli/dev/tui/theme.d.ts +30 -2
  61. package/dist/src/cli/dev/tui/theme.js +1 -1
  62. package/dist/src/cli/dev/tui/todo-panel.d.ts +44 -0
  63. package/dist/src/cli/dev/tui/todo-panel.js +1 -0
  64. package/dist/src/cli/dev/tui/tool-block-groups.d.ts +40 -0
  65. package/dist/src/cli/dev/tui/tool-block-groups.js +1 -0
  66. package/dist/src/cli/dev/tui/tool-format.js +1 -1
  67. package/dist/src/cli/dev/tui/tool-presentation.d.ts +67 -0
  68. package/dist/src/cli/dev/tui/tool-presentation.js +1 -0
  69. package/dist/src/cli/dev/tui/tool-rows.d.ts +15 -0
  70. package/dist/src/cli/dev/tui/tool-rows.js +2 -0
  71. package/dist/src/cli/dev/tui/tui.js +1 -1
  72. package/dist/src/cli/dev/tui/turn-clock.d.ts +36 -0
  73. package/dist/src/cli/dev/tui/turn-clock.js +1 -0
  74. package/dist/src/cli/dev/tui/types.d.ts +2 -2
  75. package/dist/src/cli/dev/ui-options.js +1 -1
  76. package/dist/src/cli/run.js +2 -2
  77. package/dist/src/cli/ui/live-row.js +1 -1
  78. package/dist/src/client/agent-info-schema.d.ts +9 -0
  79. package/dist/src/client/agent-info-schema.js +1 -1
  80. package/dist/src/client/client.js +1 -1
  81. package/dist/src/client/eve-agent-store.d.ts +1 -2
  82. package/dist/src/client/eve-agent-store.js +1 -1
  83. package/dist/src/client/index.js +1 -1
  84. package/dist/src/client/open-stream.d.ts +14 -10
  85. package/dist/src/client/open-stream.js +1 -1
  86. package/dist/src/client/session-utils.d.ts +2 -1
  87. package/dist/src/client/session-utils.js +1 -1
  88. package/dist/src/client/session.d.ts +3 -5
  89. package/dist/src/client/session.js +1 -1
  90. package/dist/src/client/types.d.ts +0 -6
  91. package/dist/src/compiled/.vendor-stamp.json +2 -1
  92. package/dist/src/compiled/marked/LICENSE.md +44 -0
  93. package/dist/src/compiled/marked/index.d.ts +24 -0
  94. package/dist/src/compiled/marked/index.js +59 -0
  95. package/dist/src/compiler/extension-compatibility.d.ts +2 -2
  96. package/dist/src/compiler/extension-compatibility.js +1 -1
  97. package/dist/src/context/build-dynamic-tools.js +1 -1
  98. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  99. package/dist/src/evals/session.js +1 -1
  100. package/dist/src/execution/node-step.js +1 -1
  101. package/dist/src/execution/sandbox/bindings/docker-cli.d.ts +4 -0
  102. package/dist/src/execution/sandbox/bindings/docker-cli.js +1 -1
  103. package/dist/src/execution/sandbox/bindings/microsandbox-create.js +1 -1
  104. package/dist/src/execution/terminal-session-failure-step.js +1 -1
  105. package/dist/src/harness/emission.js +1 -1
  106. package/dist/src/harness/model-call-error.d.ts +11 -30
  107. package/dist/src/harness/model-call-error.js +1 -1
  108. package/dist/src/harness/semantic-errors/index.d.ts +9 -0
  109. package/dist/src/harness/semantic-errors/index.js +1 -0
  110. package/dist/src/harness/semantic-errors/rule.d.ts +76 -0
  111. package/dist/src/harness/semantic-errors/rule.js +1 -0
  112. package/dist/src/harness/semantic-errors/rules/gateway.d.ts +11 -0
  113. package/dist/src/harness/semantic-errors/rules/gateway.js +1 -0
  114. package/dist/src/harness/semantic-errors/rules/model-provider.d.ts +11 -0
  115. package/dist/src/harness/semantic-errors/rules/model-provider.js +1 -0
  116. package/dist/src/harness/semantic-errors/rules/sandbox.d.ts +16 -0
  117. package/dist/src/harness/semantic-errors/rules/sandbox.js +1 -0
  118. package/dist/src/harness/semantic-errors/rules/system.d.ts +2 -0
  119. package/dist/src/harness/semantic-errors/rules/system.js +1 -0
  120. package/dist/src/harness/semantic-errors/rules/workflow.d.ts +9 -0
  121. package/dist/src/harness/semantic-errors/rules/workflow.js +1 -0
  122. package/dist/src/harness/semantic-errors/signals.d.ts +28 -0
  123. package/dist/src/harness/semantic-errors/signals.js +1 -0
  124. package/dist/src/harness/tool-loop.js +2 -2
  125. package/dist/src/harness/workflow-sandbox.d.ts +6 -0
  126. package/dist/src/harness/workflow-sandbox.js +2 -2
  127. package/dist/src/internal/application/output-publication-lock.js +1 -1
  128. package/dist/src/internal/application/package.js +1 -1
  129. package/dist/src/internal/logging.d.ts +33 -1
  130. package/dist/src/internal/logging.js +1 -1
  131. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response-from-manifest.js +1 -1
  132. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.d.ts +3 -1
  133. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.js +1 -1
  134. package/dist/src/internal/nitro/routes/info.js +1 -1
  135. package/dist/src/internal/resolve-model-endpoint-status.d.ts +46 -3
  136. package/dist/src/internal/resolve-model-endpoint-status.js +1 -1
  137. package/dist/src/internal/workflow/development-world-client.js +1 -1
  138. package/dist/src/internal/workflow/development-world-server.js +1 -1
  139. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  140. package/dist/src/public/models/openai/chatgpt/model.js +3 -1
  141. package/dist/src/react/use-eve-agent.d.ts +0 -1
  142. package/dist/src/react/use-eve-agent.js +1 -1
  143. package/dist/src/setup/boxes/add-channels.js +1 -1
  144. package/dist/src/setup/boxes/deploy-project.js +1 -1
  145. package/dist/src/setup/boxes/model-capabilities.d.ts +24 -0
  146. package/dist/src/setup/boxes/model-capabilities.js +1 -0
  147. package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
  148. package/dist/src/setup/boxes/select-model.d.ts +30 -8
  149. package/dist/src/setup/boxes/select-model.js +1 -1
  150. package/dist/src/setup/cli/option-row.js +1 -1
  151. package/dist/src/setup/cli/prompt-ui.d.ts +2 -0
  152. package/dist/src/setup/cli/prompt-ui.js +2 -2
  153. package/dist/src/setup/cli/rail-log.d.ts +4 -6
  154. package/dist/src/setup/cli/rail-log.js +3 -2
  155. package/dist/src/setup/connection-connector.js +2 -2
  156. package/dist/src/setup/flows/link.d.ts +13 -3
  157. package/dist/src/setup/flows/link.js +1 -1
  158. package/dist/src/setup/flows/login.js +1 -1
  159. package/dist/src/setup/flows/model-source-change.d.ts +38 -0
  160. package/dist/src/setup/flows/model-source-change.js +1 -0
  161. package/dist/src/setup/flows/model.d.ts +69 -65
  162. package/dist/src/setup/flows/model.js +1 -1
  163. package/dist/src/setup/flows/provider.d.ts +22 -0
  164. package/dist/src/setup/flows/provider.js +3 -3
  165. package/dist/src/setup/prompter.d.ts +2 -0
  166. package/dist/src/setup/scaffold/create/project.js +1 -1
  167. package/dist/src/setup/slack-connect-lifecycle.js +1 -1
  168. package/dist/src/setup/slackbot.js +1 -1
  169. package/dist/src/setup/vercel-project.d.ts +6 -0
  170. package/dist/src/setup/vercel-project.js +0 -0
  171. package/dist/src/shared/dynamic-tool-definition.d.ts +1 -8
  172. package/dist/src/shared/errors.d.ts +5 -0
  173. package/dist/src/shared/errors.js +1 -1
  174. package/dist/src/shared/gateway-service-tier.d.ts +19 -0
  175. package/dist/src/shared/gateway-service-tier.js +1 -0
  176. package/dist/src/shared/model-summary.d.ts +12 -0
  177. package/dist/src/shared/model-summary.js +1 -0
  178. package/dist/src/shared/workflow-sandbox.d.ts +2 -0
  179. package/dist/src/shared/workflow-sandbox.js +1 -1
  180. package/dist/src/source-change/agent-config-ast.d.ts +54 -0
  181. package/dist/src/source-change/agent-config-ast.js +2 -0
  182. package/dist/src/source-change/agent-config-string-path.d.ts +18 -0
  183. package/dist/src/source-change/agent-config-string-path.js +2 -0
  184. package/dist/src/source-change/apply-agent-model-settings.d.ts +26 -0
  185. package/dist/src/source-change/apply-agent-model-settings.js +1 -0
  186. package/dist/src/source-change/apply-model-name.js +1 -2
  187. package/dist/src/source-change/static-source-change.d.ts +14 -0
  188. package/dist/src/source-change/static-source-change.js +1 -1
  189. package/dist/src/svelte/index.js +1 -1
  190. package/dist/src/svelte/use-eve-agent.d.ts +0 -6
  191. package/dist/src/svelte/use-eve-agent.js +1 -1
  192. package/dist/src/vue/index.js +1 -1
  193. package/dist/src/vue/use-eve-agent.d.ts +0 -2
  194. package/dist/src/vue/use-eve-agent.js +1 -1
  195. package/docs/concepts/execution-model-and-durability.md +1 -1
  196. package/docs/concepts/sessions-runs-and-streaming.md +1 -1
  197. package/docs/getting-started.mdx +1 -1
  198. package/docs/guides/auth-and-route-protection.md +1 -1
  199. package/docs/guides/client/overview.mdx +1 -1
  200. package/docs/guides/client/streaming.mdx +1 -10
  201. package/docs/guides/deployment/meta.json +4 -0
  202. package/docs/guides/deployment/overview.md +61 -0
  203. package/docs/guides/deployment/self-hosting.md +88 -0
  204. package/docs/guides/deployment/vercel.mdx +90 -0
  205. package/docs/guides/dev-tui.md +8 -4
  206. package/docs/guides/dynamic-capabilities.md +2 -0
  207. package/docs/guides/frontend/nextjs.mdx +1 -1
  208. package/docs/guides/frontend/nuxt.mdx +1 -1
  209. package/docs/guides/frontend/overview.mdx +2 -5
  210. package/docs/guides/frontend/sveltekit.mdx +1 -1
  211. package/docs/guides/frontend/use-eve-agent-svelte.mdx +1 -4
  212. package/docs/guides/frontend/use-eve-agent-vue.mdx +1 -4
  213. package/docs/guides/instrumentation.md +1 -1
  214. package/docs/reference/cli.md +24 -2
  215. package/docs/tutorial/ship-it.mdx +1 -1
  216. package/package.json +2 -1
  217. package/docs/guides/deployment.md +0 -210
  218. /package/dist/src/cli/{dev/tui → ui}/live-region.d.ts +0 -0
  219. /package/dist/src/cli/{dev/tui → ui}/live-region.js +0 -0
  220. /package/dist/src/cli/{dev/tui → ui}/terminal-text.d.ts +0 -0
  221. /package/dist/src/cli/{dev/tui → ui}/terminal-text.js +0 -0
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Environment facts captured once per `eve dev` process, written into the
3
+ * diagnostic dump so a shared log always travels with the toolchain state
4
+ * that produced it.
5
+ */
6
+ export interface DevEnvironmentInfo {
7
+ readonly eveVersion: string;
8
+ readonly nodeVersion: string;
9
+ readonly platform: string;
10
+ readonly vercelCliVersion?: string;
11
+ readonly vercelCliPath?: string;
12
+ /** Local durable session store measurements; absent when the directory does not exist yet. */
13
+ readonly sessionsDirectory?: {
14
+ readonly path: string;
15
+ readonly files: number;
16
+ readonly bytes: number;
17
+ };
18
+ }
19
+ /**
20
+ * Aggregate counters for the TUI process's session activity. Reported by
21
+ * the renderer at each turn boundary so the dump stays current even if
22
+ * the process dies mid-session.
23
+ */
24
+ export interface DevSessionStats {
25
+ readonly prompts: number;
26
+ readonly inputTokens: number;
27
+ readonly outputTokens: number;
28
+ /** Tool invocations observed, keyed by tool name. */
29
+ readonly toolCalls: Readonly<Record<string, number>>;
30
+ /** Distinct subagent dispatches that produced observable output. */
31
+ readonly subagents: number;
32
+ }
33
+ export interface DevDiagnosticDump {
34
+ readonly path: string;
35
+ /** Project-relative reference, forward slashes on every platform. */
36
+ readonly displayPath: string;
37
+ updateSessionStats(stats: DevSessionStats): void;
38
+ close(): Promise<void>;
39
+ }
40
+ export interface CreateDevDiagnosticDumpOptions {
41
+ /** Injectable environment collector; defaults to {@link collectDevEnvironmentInfo}. */
42
+ readonly environment?: () => Promise<DevEnvironmentInfo>;
43
+ readonly now?: () => Date;
44
+ }
45
+ /**
46
+ * Creates the environment dump paired with one dev diagnostic log: the same
47
+ * instance name with a `.dump` extension. The environment section is
48
+ * collected in the background and written as soon as it resolves; session
49
+ * stats rewrite the file on every update. Like the log sink, write failures
50
+ * disable the dump silently — never through stderr, which the TUI captures.
51
+ */
52
+ export declare function createDevDiagnosticDump(appRoot: string, logPath: string, options?: CreateDevDiagnosticDumpOptions): DevDiagnosticDump;
53
+ /** Collects the real environment facts for {@link createDevDiagnosticDump}. */
54
+ export declare function collectDevEnvironmentInfo(appRoot: string): Promise<DevEnvironmentInfo>;
55
+ /**
56
+ * Counts files and total bytes under the app's local durable session store
57
+ * (`.eve/.workflow-data`). Returns `undefined` when the directory does not
58
+ * exist yet.
59
+ */
60
+ export declare function measureSessionsDirectory(appRoot: string): Promise<DevEnvironmentInfo["sessionsDirectory"]>;
@@ -0,0 +1 @@
1
+ import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{constants}from"node:fs";import{delimiter,join,relative,sep}from"node:path";import{access,readdir,stat,writeFile}from"node:fs/promises";import{LOCAL_WORKFLOW_WORLD_DATA_DIRECTORY_RELATIVE_PATH}from"#internal/workflow/local-world-data-directory.js";import{captureVercel,resolveVercelInvocation}from"#setup/primitives/run-vercel.js";function createDevDiagnosticDump(e,t,n={}){let r=t.replace(/\.log$/,`.dump`),o=n.now??(()=>new Date),s=o().getTime(),c,l,u=Promise.resolve(),d=!1,f=!1,write=()=>{if(f||d)return;let e=formatDump({at:o(),durationMs:o().getTime()-s,environment:c,stats:l});u=u.then(()=>writeFile(r,e,{encoding:`utf8`,mode:384})).catch(()=>{d=!0})};return write(),(n.environment??(()=>collectDevEnvironmentInfo(e)))().then(e=>{c=e,write()}).catch(()=>{}),{path:r,displayPath:relative(e,r).split(sep).join(`/`),updateSessionStats(e){l=e,write()},async close(){f||(write(),f=!0,await u)}}}function formatDump(e){let t={updatedAt:e.at.toISOString(),durationMs:e.durationMs,environment:e.environment??null,session:e.stats??null};return`${JSON.stringify(t,null,2)}\n`}async function collectDevEnvironmentInfo(t){let[n,r]=await Promise.all([detectVercelCli(t),measureSessionsDirectory(t)]),i={eveVersion:resolveInstalledPackageInfo().version,nodeVersion:process.version,platform:`${process.platform} ${process.arch}`,...n};return r!==void 0&&(i.sessionsDirectory=r),i}async function detectVercelCli(e){let t=resolveVercelInvocation(e),[n,r]=await Promise.all([captureVercel([`--version`],{cwd:e,nonInteractive:!0,timeoutMs:5e3}),t.command===`vercel`?findOnPath(`vercel`):Promise.resolve(t.command)]),i=n.ok?n.stdout:`${n.failure.stdout}\n${n.failure.stderr}`,a=/(\d+\.\d+\.\d+\S*)/.exec(i)?.[1],o={};return a!==void 0&&(o.vercelCliVersion=a),r!==void 0&&(o.vercelCliPath=r),o}async function findOnPath(e){let i=(process.env.PATH??``).split(delimiter).filter(e=>e.length>0);for(let n of i){let i=join(n,e);try{return await access(i,constants.X_OK),i}catch{}}}async function measureSessionsDirectory(e){let t=join(e,LOCAL_WORKFLOW_WORLD_DATA_DIRECTORY_RELATIVE_PATH),n;try{n=await readdir(t,{recursive:!0,withFileTypes:!0})}catch{return}let i=0,a=0;for(let e of n)if(e.isFile()){i+=1;try{a+=(await stat(join(e.parentPath,e.name))).size}catch{}}return{path:LOCAL_WORKFLOW_WORLD_DATA_DIRECTORY_RELATIVE_PATH,files:i,bytes:a}}export{collectDevEnvironmentInfo,createDevDiagnosticDump,measureSessionsDirectory};
@@ -0,0 +1,37 @@
1
+ import type { LogLevel } from "#internal/logging.js";
2
+ import type { JsonObject } from "#shared/json.js";
3
+ export type DevDiagnosticSource = "stderr" | "stdout" | "sandbox" | "workflow" | "tool" | "log";
4
+ /** Captured output or a failure summary attributed to one capture point. */
5
+ export interface DevDiagnosticOutputEntry {
6
+ readonly source: Exclude<DevDiagnosticSource, "log">;
7
+ readonly summary?: string;
8
+ readonly detail: string;
9
+ /** Structured remediation carried by cataloged failures. */
10
+ readonly hint?: string;
11
+ }
12
+ /** One structured record from eve's own logger. */
13
+ export interface DevDiagnosticLogRecordEntry {
14
+ readonly source: "log";
15
+ readonly level: LogLevel;
16
+ readonly namespace: string;
17
+ readonly message: string;
18
+ readonly fields?: JsonObject;
19
+ }
20
+ export type DevDiagnosticEntry = DevDiagnosticOutputEntry | DevDiagnosticLogRecordEntry;
21
+ export interface DevDiagnosticSink {
22
+ readonly path: string;
23
+ /**
24
+ * Project-relative reference with forward slashes on every platform:
25
+ * it is a display and correlation token (transcript pointers,
26
+ * `eve logs <ref>` input), not a filesystem path.
27
+ */
28
+ readonly displayPath: string;
29
+ append(entry: DevDiagnosticEntry): void;
30
+ close(): Promise<void>;
31
+ }
32
+ export interface CreateDevDiagnosticSinkOptions {
33
+ readonly now?: () => Date;
34
+ readonly pid?: number;
35
+ }
36
+ /** Creates the local, process-owned diagnostics file used by `eve dev`. */
37
+ export declare function createDevDiagnosticSink(appRoot: string, options?: CreateDevDiagnosticSinkOptions): Promise<DevDiagnosticSink>;
@@ -0,0 +1 @@
1
+ import{join,relative,sep}from"node:path";import{chmod,mkdir,open}from"node:fs/promises";async function createDevDiagnosticSink(t,n={}){let o=join(t,`.eve`,`logs`);await mkdir(o,{recursive:!0,mode:448}),await chmod(o,448);let s=n.now?.()??new Date,c=n.pid??process.pid,l=join(o,`dev-${s.toISOString().replaceAll(`:`,`-`)}-${c}.log`);return createOpenSink(t,l,await open(l,`wx`,384),()=>n.now?.()??new Date)}function createOpenSink(e,r,i,a){let o=Promise.resolve(),s=!1,c=!1;return{path:r,displayPath:relative(e,r).split(sep).join(`/`),append(e){if(c||s)return;let t=formatDiagnosticEntry(e,a());o=o.then(()=>i.appendFile(t,`utf8`)).catch(()=>{s=!0})},async close(){c||(c=!0,await o,await i.close())}}}function formatDiagnosticEntry(e,t){return`${JSON.stringify({at:t.toISOString(),...e})}\n`}export{createDevDiagnosticSink};
@@ -0,0 +1,42 @@
1
+ import { type LogRecord } from "#internal/logging.js";
2
+ import { type CreateDevDiagnosticDumpOptions } from "./diagnostic-dump.js";
3
+ import { type DevDiagnosticEntry } from "./diagnostic-sink.js";
4
+ /**
5
+ * The one diagnostics recorder an `eve dev` process owns: the per-process
6
+ * JSONL log (sink), its environment dump, session-stats accumulation, and
7
+ * ownership of eve's structured log records. The TUI holds a single
8
+ * reference and calls in at its capture points; everything stateful about
9
+ * diagnostics lives here, not in the renderer.
10
+ */
11
+ export interface DevDiagnostics {
12
+ /** Project-relative path of the diagnostic log, for transcript pointers. */
13
+ readonly displayPath: string;
14
+ /** Appends one captured record to the per-process diagnostic log. */
15
+ append(entry: DevDiagnosticEntry): void;
16
+ recordPrompt(): void;
17
+ recordStepUsage(usage: {
18
+ inputTokens?: number;
19
+ outputTokens?: number;
20
+ } | undefined): void;
21
+ recordToolCall(toolName: string): void;
22
+ recordSubagentDispatch(callId: string): void;
23
+ /** Rewrites the environment dump with the stats accumulated so far. */
24
+ reportStats(): void;
25
+ /**
26
+ * Takes ownership of eve's structured log records: each record is
27
+ * persisted to the log with its structure intact and then handed to
28
+ * `onRecord` for display. While subscribed, records never reach the
29
+ * console, so a host that also scrapes stderr sees each record exactly
30
+ * once.
31
+ */
32
+ subscribeLogRecords(onRecord: (record: LogRecord) => void): void;
33
+ unsubscribeLogRecords(): void;
34
+ close(): Promise<void>;
35
+ }
36
+ /**
37
+ * Creates the diagnostics recorder for one `eve dev` process: the
38
+ * exclusive per-process log plus its same-instance environment dump.
39
+ * Rejects when the log cannot be created (callers run without
40
+ * diagnostics rather than crash the TUI).
41
+ */
42
+ export declare function createDevDiagnostics(appRoot: string, options?: CreateDevDiagnosticDumpOptions): Promise<DevDiagnostics>;
@@ -0,0 +1 @@
1
+ import{createDevDiagnosticDump}from"./diagnostic-dump.js";import{createDevDiagnosticSink}from"./diagnostic-sink.js";import{setLogRecordSubscriber}from"#internal/logging.js";async function createDevDiagnostics(n,r={}){let i=await createDevDiagnosticSink(n);return new DevDiagnosticsRecorder(i,createDevDiagnosticDump(n,i.path,r))}var DevDiagnosticsRecorder=class{displayPath;#e;#t;#n=0;#r=0;#i=0;#a=new Map;#o=new Set;constructor(e,t){this.displayPath=e.displayPath,this.#e=e,this.#t=t}append(e){this.#e.append(e)}recordPrompt(){this.#n+=1}recordStepUsage(e){this.#r+=e?.inputTokens??0,this.#i+=e?.outputTokens??0}recordToolCall(e){this.#a.set(e,(this.#a.get(e)??0)+1)}recordSubagentDispatch(e){this.#o.add(e)}reportStats(){let e={prompts:this.#n,inputTokens:this.#r,outputTokens:this.#i,toolCalls:Object.fromEntries(this.#a),subagents:this.#o.size};this.#t.updateSessionStats(e)}subscribeLogRecords(e){setLogRecordSubscriber(t=>{let n={source:`log`,level:t.level,namespace:t.namespace,message:t.message};this.#e.append(t.fields===void 0?n:{...n,fields:t.fields}),e(t)})}unsubscribeLogRecords(){setLogRecordSubscriber(void 0)}async close(){await this.#t.close(),await this.#e.close()}};export{createDevDiagnostics};
@@ -9,9 +9,11 @@
9
9
  * boxes anywhere. Every returned row is already styled and fits within the
10
10
  * given width, so the live region can place rows verbatim.
11
11
  */
12
+ import type { ToolDetailLine } from "./line-diff.js";
12
13
  import type { Theme } from "./theme.js";
14
+ import type { ToolGroupPresentation } from "./tool-presentation.js";
13
15
  export type ToolStatus = "running" | "done" | "error" | "denied" | "approval";
14
- export type BlockKind = "user" | "assistant" | "reasoning" | "tool" | "error" | "notice" | "warning" | "result" | "flow" | "command" | "question" | "subagent" | "subagent-step" | "subagent-tool" | "connection-auth" | "sandbox" | "log" | "agent-header";
16
+ export type BlockKind = "user" | "assistant" | "reasoning" | "tool" | "error" | "notice" | "warning" | "result" | "flow" | "command" | "question" | "subagent" | "subagent-step" | "subagent-tool" | "subagent-close" | "connection-auth" | "sandbox" | "log" | "turn-stats" | "session-boundary" | "todo-list" | "agent-header";
15
17
  /**
16
18
  * One renderable transcript unit. Fields are interpreted per `kind`; unset
17
19
  * fields are simply omitted from the rendered output.
@@ -22,10 +24,12 @@ export interface Block {
22
24
  id?: string;
23
25
  /** Nesting depth: 0 = top level, 1 = inside a subagent, etc. */
24
26
  depth?: number;
25
- /** Whether the block is still streaming / mutating (drives the spinner). */
27
+ /** Whether the block is still streaming / mutating (drives the activity pulse). */
26
28
  live?: boolean;
27
29
  /** Primary label — tool name, subagent name, log source, error title. */
28
30
  title?: string;
31
+ /** Past-tense tool label swapped in once the call settles successfully. */
32
+ doneTitle?: string;
29
33
  /** Compact secondary text — summarized tool args. */
30
34
  subtitle?: string;
31
35
  /** Main multi-line content (markdown for prose, plain for logs). */
@@ -39,6 +43,8 @@ export interface Block {
39
43
  * rendered dim beneath the headline, capped to a handful of lines.
40
44
  */
41
45
  detail?: string;
46
+ /** Structured remediation shown between an error's body and its detail. */
47
+ hint?: string;
42
48
  /** Tool, connection, or synthetic command lifecycle status. */
43
49
  status?: ToolStatus;
44
50
  /** When true, treat `body` as pre-styled and only wrap + indent it. */
@@ -47,18 +53,64 @@ export interface Block {
47
53
  collapsed?: boolean;
48
54
  /** When true, expand tool input/output instead of summarizing. */
49
55
  expanded?: boolean;
56
+ /** Captured-log visibility used for concise-vs-raw diagnostic replay. */
57
+ logVisibility?: "stderr-only" | "all-only";
50
58
  /** Raw tool input / output for the expanded view. */
51
59
  toolInput?: unknown;
52
60
  toolOutput?: unknown;
61
+ /** Original execution name, kept separate from a semantic display title. */
62
+ toolName?: string;
63
+ /** Optional aggregation metadata; execution state remains on this call's block. */
64
+ toolGroup?: ToolGroupPresentation;
65
+ /** Salient body lines rendered behind the `│` rail under the tool header. */
66
+ detailLines?: readonly ToolDetailLine[];
67
+ /** When true, `detailLines` stay visible after the call settles (writes). */
68
+ keepDetailWhenDone?: boolean;
69
+ /** Links a subagent section's header and children so calls can coalesce. */
70
+ subagentCallId?: string;
71
+ /**
72
+ * Monotonic activity stamp, bumped on every push and in-place update.
73
+ * Recency windows key on it so a parallel-announced call that just
74
+ * settled counts as newer than a later-announced one still idle.
75
+ */
76
+ updateSeq?: number;
77
+ }
78
+ /**
79
+ * What the renderers actually draw: an execution {@link Block} plus the
80
+ * synthesized presentation the display grouping may attach. Only the
81
+ * grouping layer creates these fields, so an execution block can never
82
+ * smuggle display state — the type boundary enforces what used to be a
83
+ * comment.
84
+ */
85
+ export interface DisplayBlock extends Block {
86
+ /** Items listed when equivalent tool calls are coalesced into one row. */
87
+ toolGroupItems?: readonly ToolGroupItem[];
88
+ /**
89
+ * Stand-in for this many earlier sibling rows elided from a capped
90
+ * subagent run; renders as a single dim `… +N more` line.
91
+ */
92
+ elided?: number;
93
+ /**
94
+ * This block is the last of its section, so its final row swaps the
95
+ * nesting rule for the closing `└` — the rail ends on the newest child
96
+ * instead of a bare corner row.
97
+ */
98
+ closesRail?: boolean;
99
+ }
100
+ /** One coalesced call's row beneath an aggregated tool header. */
101
+ export interface ToolGroupItem {
102
+ readonly text: string;
103
+ /** Per-call failure summary, present when a failed batch is aggregated. */
104
+ readonly result?: string;
53
105
  }
54
106
  export interface RenderBlockContext {
55
- /** Current spinner frame for live blocks. */
56
- spinner: string;
107
+ /** Current shared square-pulse frame for live activity blocks. */
108
+ activityPulse: string;
57
109
  /**
58
110
  * Kind and title of the block rendered immediately above this one. Lets a
59
- * log block detect that it continues a same-source run (label suppressed,
60
- * lines hang under the previous block's label) without any mutable run
61
- * state — each captured write stays its own immediately-committed block.
111
+ * sandbox block detect that it continues a run (label suppressed, lines
112
+ * hang under the previous block's label) without any mutable run state —
113
+ * each captured write stays its own immediately-committed block.
62
114
  */
63
115
  previous?: {
64
116
  kind: BlockKind;
@@ -69,7 +121,7 @@ export interface RenderBlockContext {
69
121
  * Renders a block to its terminal rows. Each row is fully styled and clipped
70
122
  * to `width` visible columns.
71
123
  */
72
- export declare function renderBlockLines(block: Block, width: number, theme: Theme, context: RenderBlockContext): string[];
124
+ export declare function renderBlockLines(block: DisplayBlock, width: number, theme: Theme, context: RenderBlockContext): string[];
73
125
  /**
74
126
  * The setup attention line (`⚠ 1 setup issue: … · /model`): yellow glyph, body
75
127
  * at full intensity, slash commands painted blue so the fix reads as actionable
@@ -1,8 +1,8 @@
1
- import{isPromptControlCommand}from"./prompt-commands.js";import{sliceVisible,visibleLength,wrapVisibleLine}from"./terminal-text.js";import{formatValuePretty,truncate}from"./tool-format.js";import{renderMarkdown}from"./markdown.js";function renderBlockLines(e,t,r,i){let a=nestingPrefix(e.depth??0,r);return renderBody(e,Math.max(8,t-visibleLength(a)),r,i).map(e=>`${a}${e}`)}function nestingPrefix(e,t){return e<=0?``:`${t.colors.orange(t.glyph.rule)} `.repeat(e)}function renderBody(e,t,n,r){switch(e.kind){case`user`:return renderUser(e,t,n);case`assistant`:case`subagent-step`:return renderProse(e,t,n);case`reasoning`:return renderReasoning(e,t,n);case`tool`:case`subagent-tool`:return renderTool(e,t,n,r);case`error`:return renderError(e,t,n);case`notice`:return renderNotice(e,t,n);case`warning`:return renderWarning(e,t,n);case`result`:return renderResult(e,t,n);case`flow`:return renderFlow(e,t,n);case`command`:return renderCommand(e,n);case`question`:case`connection-auth`:return renderPreformatted(e,t,n);case`sandbox`:return renderSandbox(e,t,n,r);case`log`:return renderLog(e,t,n,r);case`subagent`:return renderSubagentHeader(e,t,n);case`agent-header`:return(e.body??``).split(`
2
- `)}}function renderUser(e,t,n){let r=n.colors.cyan(n.glyph.user);return wrap(e.body??``,t-2).map(e=>`${r} ${e}`)}function renderProse(e,t,n){let i=[],a=e.kind===`subagent-step`,s=a?``:`${n.colors.bold(n.colors.white(n.glyph.brand))} `,c=a?``:` `;e.reasoning&&e.reasoning.trim().length>0&&i.push(...renderReasoningLines(e.reasoning,t,n));let l=(e.body??``).trim();return l.length===0&&i.length===0?[`${s}${n.colors.dim(`thinking${n.glyph.ellipsis}`)}`]:(l.length>0&&renderMarkdown(l).split(`
3
- `).flatMap(e=>wrapVisibleLine(e,t-c.length)).forEach((e,t)=>{t===0&&!a&&i.length===0?i.push(`${s}${e}`):i.push(`${c}${e}`)}),i.length>0?i:[`${s}`])}function renderReasoning(e,t,n){return e.collapsed?[`${n.colors.gray(n.glyph.reasoning)} ${n.colors.dim(`thinking`)}`]:renderReasoningLines(e.body??``,t,n,n.glyph.reasoning)}function renderReasoningLines(e,t,n,r){let i=r?2:0,a=wrap(e.trim(),t-i);return a.length===0?[]:a.map((e,t)=>`${r?t===0?`${n.colors.gray(r)} `:` `:``}${n.colors.dim(n.colors.italic(e))}`)}function renderTool(e,t,n,r){let{icon:i,accent:o}=toolGlyph(e.status??`running`,n,r),s=e.title??`tool`,c=t-2,l=truncatePlain(s,c),u=`${i} ${n.colors.bold(l)}`,d=c-l.length-2,f=e.subtitle??``;f.length>0&&d>=6&&(u+=` ${n.colors.gray(truncate(f,d))}`);let p=[u];return e.expanded?p.push(...renderToolExpanded(e,t,n)):e.status===`done`&&e.result&&e.result.length>0?p.push(resultLine(n.glyph.arrow,e.result,t,n,o)):e.status===`error`&&e.result?p.push(resultLine(n.glyph.arrow,e.result,t,n,n.colors.red)):e.status===`denied`&&p.push(resultLine(n.glyph.arrow,`denied`,t,n,n.colors.yellow)),p}function renderToolExpanded(e,t,n){let r=[],push=(e,a,o)=>{if(a!==void 0){r.push(` ${n.colors.dim(e)}`);for(let e of wrap(formatValuePretty(a),t-4))r.push(` ${o(e)}`)}};return push(`input`,e.toolInput,n.colors.gray),e.status===`error`&&e.result?push(`error`,e.result,n.colors.red):push(`output`,e.toolOutput,n.colors.gray),r}function resultLine(e,t,n,r,i){let o=n-4;return` ${r.colors.dim(e)} ${i(truncate(t,o))}`}function toolGlyph(e,t,n){switch(e){case`done`:return{icon:t.colors.green(t.glyph.success),accent:t.colors.gray};case`error`:return{icon:t.colors.red(t.glyph.error),accent:t.colors.red};case`denied`:return{icon:t.colors.yellow(t.glyph.warning),accent:t.colors.yellow};case`approval`:return{icon:t.colors.yellow(t.glyph.question),accent:t.colors.yellow};default:return{icon:t.colors.yellow(n.spinner),accent:t.colors.gray}}}function renderError(e,t,n){let r=n.colors.red(n.colors.bold(n.glyph.error)),i=e.title??`Error`,a=[`${r} ${n.colors.red(n.colors.bold(i))}`];for(let r of wrap(e.body??``,t-2))a.push(` ${colorizeError(r,n)}`);return a.push(...renderErrorDetail(e.detail,t,n)),a}function renderErrorDetail(e,t,n){if(e===void 0||e.trim().length===0)return[];let r=e.split(`
4
- `),i=r.slice(0,12),a=i.map(e=>` ${n.colors.dim(truncatePlain(e,Math.max(1,t-2)))}`),o=r.length-i.length;return o>0&&a.push(` ${n.colors.dim(`${n.glyph.ellipsis} +${o} more line${o===1?``:`s`}`)}`),a}const URL_PATTERN=/(https?:\/\/\S+)/u;function colorizeError(e,t){return URL_PATTERN.test(e)?e.split(URL_PATTERN).map((e,n)=>n%2==1?t.colors.cyan(e):t.colors.red(e)).join(``):t.colors.red(e)}function renderNotice(e,t,n){let r=n.colors.dim(n.glyph.dot),i=wrap(e.body??``,t-2);return i.length===0?[r]:i.map(e=>`${r} ${n.colors.dim(e)}`)}function renderAttentionRows(e,t,n){let r=n.colors.yellow(n.glyph.warning);return wrap(e,t-2).map((e,t)=>`${t===0?r:` `} ${paintCommands(e,n)}`)}function renderWarning(e,t,n){return renderAttentionRows(e.body??``,t,n)}function paintCommands(t,n){return t.replace(/\/[a-z:-]+/g,t=>isPromptControlCommand(t)?n.colors.blue(t):t)}function renderCommand(e,t){let n=t.colors,r=e.status===`error`?`${n.red(t.glyph.error)} `:``;return[`${n.cyan(t.glyph.user)} ${r}${n.blue(e.body??``)}`]}function renderFlow(e,t,n){let r=n.colors,i=e.title??`info`,a=i===`success`?r.green(n.glyph.success):i===`warning`?r.yellow(n.glyph.warning):i===`error`?r.red(n.glyph.error):r.dim(n.glyph.dot),o=wrap(e.body??``,t-2),paint=e=>i===`info`?r.dim(e):e;return o.map((e,t)=>`${t===0?a:` `} ${paint(e)}`)}function renderResult(e,t,n){let r=n.colors.dim(n.glyph.elbow),i=wrap(e.body??``,t-7);if(i.length===0)return[` ${r}`];let dim=e=>n.colors.dim(e.replaceAll(`\x1B[22m`,`\x1B[22m\x1B[2m`));return i.map((e,t)=>t===0?` ${r} ${dim(e)}`:` ${dim(e)}`)}function renderPreformatted(e,t,n){let i=e.kind===`connection-auth`?n.colors.yellow(n.glyph.connection):n.colors.yellow(n.colors.bold(n.glyph.question)),a=wrap(e.title??``,t-2),o=a.length===0?[`${i} `]:a.map((e,t)=>t===0?`${i} ${n.colors.bold(e)}`:` ${n.colors.bold(e)}`);for(let n of(e.body??``).split(`
5
- `))for(let e of wrapVisibleLine(n,t-2))o.push(` ${e}`);return o}function renderSandbox(e,t,i,a){let o=i.colors.cyan(i.glyph.rule),s=i.colors.dim(`sandbox ${i.glyph.dot} `),c=visibleLength(s),l=` `.repeat(c),u=a.previous?.kind===`sandbox`,d=(e.body??``).split(`
6
- `),f=[];for(let e of d){let n=wrapVisibleLine(e,Math.max(1,t-2-c));for(let e of n){let t=f.length===0&&!u?s:l;f.push(`${o} ${t}${i.colors.gray(e)}`)}}return f.length>0?f:[`${o}`]}function renderLog(e,t,i,a){let o=e.title===`stderr`,s=o?i.colors.red:i.colors.gray,c=i.colors.dim(i.glyph.rule),l=o?`stderr`:`stdout`,u=i.colors.dim(`${l} ${i.glyph.dot} `),d=visibleLength(u),f=` `.repeat(d),p=a.previous?.kind===`log`&&a.previous.title===e.title,m=(e.body??``).split(`
7
- `),h=[];for(let e of m){let n=wrapVisibleLine(e,Math.max(1,t-2-d));for(let e of n){let t=h.length===0&&!p?u:f;h.push(`${c} ${t}${i.colors.dim(s(e))}`)}}return h.length>0?h:[`${c}`]}function renderSubagentHeader(e,t,n){let r=truncatePlain(e.title??`subagent`,Math.max(8,t-14));return[`${n.colors.orange(n.glyph.subagent)} ${n.colors.bold(r)} ${n.colors.dim(`subagent`)}`]}function wrap(e,t){return e.trim().length===0?[]:e.split(`
8
- `).flatMap(e=>wrapVisibleLine(e,Math.max(1,t)))}function truncatePlain(e,r){return visibleLength(e)<=r?e:sliceVisible(e,r)}export{renderAttentionRows,renderBlockLines};
1
+ import{isPromptControlCommand}from"./prompt-commands.js";import{renderMarkdown}from"./markdown.js";import{elisionText}from"./rail.js";import{renderTool}from"./tool-rows.js";import{clipVisible,sliceVisible,visibleLength,wrapVisibleLine}from"#cli/ui/terminal-text.js";function renderBlockLines(e,t,n,r){let i=e.depth??0,a=nestingPrefix(i,n),s=renderBody(e,Math.max(8,t-visibleLength(a)),n,r);if(e.closesRail===!0&&i>0){let e=closingPrefix(i,n);return s.map((t,n)=>`${n===s.length-1?e:a}${t}`)}return s.map(e=>`${a}${e}`)}function nestingPrefix(e,t){return e<=0?``:` ${`${t.colors.dim(t.glyph.rule)} `.repeat(e)}`}function closingPrefix(e,t){return` ${`${t.colors.dim(t.glyph.rule)} `.repeat(e-1)}${t.colors.dim(t.glyph.corner)} `}function renderBody(e,t,a,o){if(e.elided!==void 0&&e.kind===`subagent-step`)return[` ${elisionText(e.elided,a)}`];switch(e.kind){case`user`:return renderUser(e,t,a);case`assistant`:case`subagent-step`:return renderProse(e,t,a);case`reasoning`:return renderReasoning(e,t,a);case`tool`:case`subagent-tool`:return renderTool(e,t,a,o);case`error`:return renderError(e,t,a);case`notice`:return renderNotice(e,t,a);case`warning`:return renderWarning(e,t,a);case`result`:return renderResult(e,t,a);case`flow`:return renderFlow(e,t,a);case`command`:return renderCommand(e,a);case`question`:case`connection-auth`:return renderPreformatted(e,t,a);case`sandbox`:return renderSandbox(e,t,a,o);case`log`:return renderLog(e,t,a);case`subagent`:return renderSubagentHeader(e,t,a);case`subagent-close`:{let n=` ${a.colors.dim(a.glyph.corner)}`;return e.body!==void 0&&e.body.length>0?[clipVisible(`${n} ${a.colors.dim(e.body)}`,Math.max(1,t))]:[n]}case`turn-stats`:return renderTurnStats(e,t,a);case`session-boundary`:case`todo-list`:case`agent-header`:return(e.body??``).split(`
2
+ `)}}function renderUser(e,t,n){let r=n.colors.cyan(n.glyph.user);return wrap(e.body??``,t-2).map(e=>`${r} ${e}`)}function renderProse(e,n,r){let i=[],o=e.kind===`subagent-step`;if(o&&e.collapsed===!0){let t=firstNonEmptyLine(e.body)??(e.reasoning===void 0?void 0:firstNonEmptyLine(e.reasoning));return t===void 0?[]:[r.colors.dim(sliceVisible(t,Math.max(1,n)))]}let c=o?``:`${r.colors.bold(r.glyph.brand)} `,l=o?``:` `;e.reasoning&&e.reasoning.trim().length>0&&i.push(...renderReasoningLines(e.reasoning,n,r));let u=(e.body??``).trim();return u.length===0&&i.length===0?[`${c}${r.colors.dim(`thinking${r.glyph.ellipsis}`)}`]:(u.length>0&&renderMarkdown(u,n-l.length).split(`
3
+ `).flatMap(e=>wrapVisibleLine(e,n-l.length)).forEach((e,t)=>{t===0&&!o&&i.length===0?i.push(`${c}${e}`):i.push(`${l}${e}`)}),i.length>0?i:[`${c}`])}function renderReasoning(e,t,n){return e.collapsed?[`${n.colors.gray(n.glyph.reasoning)} ${n.colors.dim(e.title??`thinking`)}`]:renderReasoningLines(e.body??``,t,n,n.glyph.reasoning)}function renderReasoningLines(e,t,n,r){let i=r?2:0,a=wrap(e.trim(),t-i);return a.length===0?[]:a.map((e,t)=>`${r?t===0?`${n.colors.gray(r)} `:` `:``}${n.colors.dim(n.colors.italic(e))}`)}function renderError(e,t,n){let r=n.colors.red(n.colors.bold(n.glyph.error)),i=e.title??`Error`,a=[`${r} ${n.colors.red(n.colors.bold(i))}`];for(let r of wrap(e.body??``,t-2))a.push(` ${colorizeError(r,n)}`);if(e.hint!==void 0&&e.hint.trim().length>0)for(let[r,i]of wrap(e.hint,t-4).entries()){let e=r===0?`${n.glyph.arrow} `:` `;a.push(` ${n.colors.cyan(`${e}${i}`)}`)}return a.push(...renderErrorDetail(e.detail,t,n)),a}function renderErrorDetail(e,t,n){if(e===void 0||e.trim().length===0)return[];let r=e.split(`
4
+ `),i=r.slice(0,12),a=i.map(e=>` ${n.colors.dim(truncatePlain(e,Math.max(1,t-2)))}`),o=r.length-i.length;return o>0&&a.push(` ${n.colors.dim(`${n.glyph.ellipsis} +${o} more line${o===1?``:`s`}`)}`),a}const URL_PATTERN=/(https?:\/\/\S+)/u;function colorizeError(e,t){return URL_PATTERN.test(e)?e.split(URL_PATTERN).map((e,n)=>n%2==1?t.colors.cyan(e):t.colors.red(e)).join(``):t.colors.red(e)}function renderNotice(e,t,n){let r=n.colors.dim(n.glyph.dot),i=wrap(e.body??``,t-2);return i.length===0?[r]:i.map(e=>`${r} ${n.colors.dim(e)}`)}function renderAttentionRows(e,t,n){let r=n.colors.yellow(n.glyph.warning);return wrap(e,t-2).map((e,t)=>`${t===0?r:` `} ${paintCommands(e,n)}`)}function renderWarning(e,t,n){return renderAttentionRows(e.body??``,t,n)}function paintCommands(t,n){return t.replace(/\/[a-z:-]+/g,t=>isPromptControlCommand(t)?n.colors.blue(t):t)}function renderCommand(e,t){let n=t.colors,r=e.status===`error`?`${n.red(t.glyph.error)} `:``;return[`${n.cyan(t.glyph.user)} ${r}${n.blue(e.body??``)}`]}function renderFlow(e,t,n){let r=n.colors,i=e.title??`info`,a=i===`success`?r.green(n.glyph.success):i===`warning`?r.yellow(n.glyph.warning):i===`error`?r.red(n.glyph.error):r.dim(n.glyph.dot),o=wrap(e.body??``,t-2),paint=e=>i===`info`?r.dim(e):e;return o.map((e,t)=>`${t===0?a:` `} ${paint(e)}`)}function renderResult(e,t,n){let r=n.colors.dim(n.glyph.elbow),i=wrap(e.body??``,t-7);if(i.length===0)return[` ${r}`];let dim=e=>n.colors.dim(e.replaceAll(`\x1B[22m`,`\x1B[22m\x1B[2m`));return i.map((e,t)=>t===0?` ${r} ${dim(e)}`:` ${dim(e)}`)}function renderPreformatted(e,t,n){let r=e.kind===`connection-auth`?n.colors.yellow(n.glyph.connection):n.colors.yellow(n.colors.bold(n.glyph.question)),i=e.kind===`question`?` `:` `,a=wrap(e.title??``,t-2),o=a.length===0?[`${r} `]:a.map((e,t)=>t===0?`${r} ${n.colors.bold(e)}`:` ${n.colors.bold(e)}`);for(let n of(e.body??``).split(`
5
+ `))for(let e of wrapVisibleLine(n,Math.max(1,t-i.length)))o.push(`${i}${e}`);return o}function renderSandbox(e,t,n,r){let i=n.colors.cyan(n.glyph.rule),a=n.colors.dim(`sandbox ${n.glyph.dot} `),c=visibleLength(a),l=` `.repeat(c),u=r.previous?.kind===`sandbox`,d=(e.body??``).split(`
6
+ `),f=[];for(let e of d){let r=wrapVisibleLine(e,Math.max(1,t-2-c));for(let e of r){let t=f.length===0&&!u?a:l;f.push(`${i} ${t}${n.colors.gray(e)}`)}}return f.length>0?f:[`${i}`]}function renderLog(e,t,r){let i=e.title===`stderr`,a=i?r.colors.red:r.colors.gray,o=r.colors.dim(r.glyph.rule),c=i?`stderr`:`stdout`,l=[`${r.colors.dim(r.glyph.reasoning)} ${r.colors.dim(c)}`];e.elided!==void 0&&e.elided>0&&l.push(`${o} ${elisionText(e.elided,r)}`);for(let n of(e.body??``).split(`
7
+ `))for(let e of wrapVisibleLine(n,Math.max(1,t-2)))l.push(`${o} ${r.colors.dim(a(e))}`);return l}function renderTurnStats(e,t,n){let r=`${n.glyph.corner} ${e.body??``}`;return[n.colors.dim(truncatePlain(r,Math.max(1,t)))]}function renderSubagentHeader(e,t,n){let r=truncatePlain(e.title===void 0||e.title===`agent`?`self`:e.title,Math.max(8,t-16)),i=e.subtitle!==void 0&&e.subtitle.startsWith(`#`),a=i?`:${e.subtitle.slice(1)}`:``,o=` ${e.status===`done`?n.colors.green(n.glyph.subagent):n.colors.orange(n.glyph.subagent)} ${n.colors.bold(`subagent(${r}${a})`)}`;return!i&&e.subtitle!==void 0&&e.subtitle.length>0&&(o+=` ${n.colors.dim(e.subtitle)}`),[o]}function firstNonEmptyLine(e){return e===void 0?void 0:e.split(/\r?\n/u).find(e=>e.trim().length>0)?.trim()}function wrap(e,t){return e.trim().length===0?[]:e.split(`
8
+ `).flatMap(e=>wrapVisibleLine(e,Math.max(1,t)))}function truncatePlain(e,t){return visibleLength(e)<=t?e:sliceVisible(e,t)}export{renderAttentionRows,renderBlockLines};
@@ -1 +1 @@
1
- import{sliceVisible,visibleLength}from"./terminal-text.js";import{renderCursorRow}from"#setup/cli/option-row.js";function typeaheadFor(e,t,n){let r=matchingCommands(e,t),i=n===void 0?void 0:n.matches[n.selectedIndex],a=i===void 0?-1:r.indexOf(i);return{query:t,matches:r,selectedIndex:a>=0?a:0,dismissed:n!==void 0&&n.dismissed&&n.query===t}}function matchingCommands(e,t){if(!t.startsWith(`/`)||/\s/.test(t))return[];let n=t.slice(1);return e.filter(e=>[e.name,...e.aliases].some(e=>e.startsWith(n)))}function isTypeaheadOpen(e){return e.matches.length>0&&!e.dismissed}function moveTypeaheadSelection(e,t){let n=e.matches.length;if(n===0)return e;let r=(e.selectedIndex+t+n)%n;return{...e,selectedIndex:r}}function dismissTypeahead(e){return e.dismissed?e:{...e,dismissed:!0}}function selectedTypeaheadCommand(e){return e.matches[e.selectedIndex]}function typeaheadCompletion(e){return`/${e.name}${e.takesArgument?` `:``}`}function inlineCommandHint(e){if(e.dismissed||e.matches.length!==1)return;let t=e.matches[0],n=e.query.startsWith(`/`)?e.query.slice(1):e.query;if([t.name,...t.aliases].includes(n))return t.argumentHint??``}function renderCommandSuggestions(r,i,a){let o=i.colors,s=r.matches.length,c=Math.min(s,10),l=Math.max(0,Math.min(r.selectedIndex-Math.floor(c/2),s-c)),u=Math.min(l+c,s),d=r.matches.slice(l,u),invocation=e=>{let t=e.aliases.map(e=>` (/${e})`).join(``);return`/${e.name}${t}`},f=Math.max(...d.map(e=>invocation(e).length))+2;return d.map((e,t)=>{let a=l+t===r.selectedIndex,s=`/${e.name}`,c=renderCursorRow(a?`${i.glyph.selectedPointer} ${s}`:` ${s}`,a,o),u=invocation(e).slice(`/${e.name}`.length),d=` `.repeat(f-invocation(e).length);return a&&(u.startsWith(` `)?u=u.slice(1):d=d.slice(1)),`${c}${o.dim(u)}${d}${o.dim(e.description)}`}).map(n=>visibleLength(n)>a?sliceVisible(n,a):n)}export{dismissTypeahead,inlineCommandHint,isTypeaheadOpen,moveTypeaheadSelection,renderCommandSuggestions,selectedTypeaheadCommand,typeaheadCompletion,typeaheadFor};
1
+ import{sliceVisible,visibleLength}from"#cli/ui/terminal-text.js";import{renderCursorRow}from"#setup/cli/option-row.js";function typeaheadFor(e,t,n){let r=matchingCommands(e,t),i=n===void 0?void 0:n.matches[n.selectedIndex],a=i===void 0?-1:r.indexOf(i);return{query:t,matches:r,selectedIndex:a>=0?a:0,dismissed:n!==void 0&&n.dismissed&&n.query===t}}function matchingCommands(e,t){if(!t.startsWith(`/`)||/\s/.test(t))return[];let n=t.slice(1);return e.filter(e=>[e.name,...e.aliases].some(e=>e.startsWith(n)))}function isTypeaheadOpen(e){return e.matches.length>0&&!e.dismissed}function moveTypeaheadSelection(e,t){let n=e.matches.length;if(n===0)return e;let r=(e.selectedIndex+t+n)%n;return{...e,selectedIndex:r}}function dismissTypeahead(e){return e.dismissed?e:{...e,dismissed:!0}}function selectedTypeaheadCommand(e){return e.matches[e.selectedIndex]}function typeaheadCompletion(e){return`/${e.name}${e.takesArgument?` `:``}`}function inlineCommandHint(e){if(e.dismissed||e.matches.length!==1)return;let t=e.matches[0],n=e.query.startsWith(`/`)?e.query.slice(1):e.query;if([t.name,...t.aliases].includes(n))return t.argumentHint??``}function renderCommandSuggestions(r,i,a){let o=i.colors,s=r.matches.length,c=Math.min(s,10),l=Math.max(0,Math.min(r.selectedIndex-Math.floor(c/2),s-c)),u=Math.min(l+c,s),d=r.matches.slice(l,u),invocation=e=>{let t=e.aliases.map(e=>` (/${e})`).join(``);return`/${e.name}${t}`},f=Math.max(...d.map(e=>invocation(e).length))+2;return d.map((e,t)=>{let a=l+t===r.selectedIndex,s=`/${e.name}`,c=renderCursorRow(a?`${i.glyph.selectedPointer} ${s}`:` ${s}`,a,o),u=invocation(e).slice(`/${e.name}`.length),d=` `.repeat(f-invocation(e).length);return a&&(u.startsWith(` `)?u=u.slice(1):d=d.slice(1)),`${c}${o.dim(u)}${d}${o.dim(e.description)}`}).map(n=>visibleLength(n)>a?sliceVisible(n,a):n)}export{dismissTypeahead,inlineCommandHint,isTypeaheadOpen,moveTypeaheadSelection,renderCommandSuggestions,selectedTypeaheadCommand,typeaheadCompletion,typeaheadFor};
@@ -0,0 +1,16 @@
1
+ export type DiagnosticPresentation = {
2
+ readonly kind: "inline";
3
+ readonly text: string;
4
+ } | {
5
+ readonly kind: "stored";
6
+ readonly summary: string;
7
+ /** First error-naming line of the collapsed body, when one exists. */
8
+ readonly headline?: string;
9
+ readonly omittedLines: number;
10
+ readonly path: string;
11
+ };
12
+ /** Chooses whether process diagnostics stay inline or collapse to the local sink. */
13
+ export declare function presentDiagnostic(text: string, path: string): DiagnosticPresentation;
14
+ export declare function formatStoredDiagnostic(presentation: Extract<DiagnosticPresentation, {
15
+ kind: "stored";
16
+ }>): string;
@@ -0,0 +1,3 @@
1
+ const ERROR_HEADLINE_PATTERN=/^\s*(?:message|name):\s*\S|^\s*[A-Z][\w$]*Error\b/u;function presentDiagnostic(t,n){let r=t.split(`
2
+ `);if(r.length<=4&&t.length<=600)return{kind:`inline`,text:t};let i=r.findIndex(e=>e.trim().length>0),a=i===-1?`Diagnostic output`:r[i].trim(),o=r.slice(i+1).find(t=>ERROR_HEADLINE_PATTERN.test(t))?.trim(),s=1+(o===void 0?0:1),c={kind:`stored`,summary:clip(a),omittedLines:Math.max(0,r.length-s),path:n};return o===void 0?c:{...c,headline:clip(o)}}function formatStoredDiagnostic(e){let t=e.omittedLines,n=t===0?`diagnostic details`:`${t} diagnostic line${t===1?``:`s`}`,r=[e.summary];return e.headline!==void 0&&r.push(e.headline),r.push(`… ${n} · details: ${e.path}`),r.join(`
3
+ `)}function clip(e){return e.length<=240?e:`${e.slice(0,239)}…`}export{formatStoredDiagnostic,presentDiagnostic};
@@ -40,6 +40,23 @@ export declare function failureKey(event: FailureStreamEvent): string;
40
40
  * which case the message stands alone instead of reading `Code: Code: …`.
41
41
  */
42
42
  export declare function formatFailureMessage(event: FailureStreamEvent): string;
43
+ /**
44
+ * The structured fields the harness attaches to failure-event details:
45
+ * the semantic-error catalog's identity and remediation, plus the raw
46
+ * inspection dump for unrecognized failures. One projection so every
47
+ * consumer narrows the untyped `details` payload the same way.
48
+ */
49
+ export interface FailureDetails {
50
+ readonly semanticErrorId?: string;
51
+ readonly hint?: string;
52
+ readonly detail?: string;
53
+ }
54
+ export declare function failureDetails(event: FailureStreamEvent): FailureDetails;
55
+ /**
56
+ * Extracts the structured remediation hint attached to a failure event's
57
+ * details by the semantic-error catalog, if any.
58
+ */
59
+ export declare function formatFailureHint(event: FailureStreamEvent): string | undefined;
43
60
  /**
44
61
  * Extracts the diagnostic dump attached to a failure event, if any.
45
62
  *
@@ -51,20 +68,8 @@ export declare function formatFailureMessage(event: FailureStreamEvent): string;
51
68
  */
52
69
  export declare function formatFailureDetail(event: FailureStreamEvent): string | undefined;
53
70
  /**
54
- * Minimal TUI rendering for a gateway-auth failure when `/model` is available
55
- * locally. Replaces the harness's full summary whose remediation names CLI
56
- * commands and dashboard URLs with one actionable line; the caller drops
57
- * the diagnostic detail along with it. The variant is picked off the summary
58
- * message the harness wrote, so a stale key, an expired OIDC token, and
59
- * missing credentials each get the fix that actually applies.
60
- */
61
- export declare function formatGatewayAuthFailureNotice(event: FailureStreamEvent): string;
62
- /**
63
- * Recognizes a model-call failure caused by AI Gateway authentication. The
64
- * primary signal is the machine-readable `gatewayName` the harness merges
65
- * into every model-call failure's details (`extractModelCallErrorDetails`);
66
- * the summary name is the fallback for payloads whose gateway error was not
67
- * preserved on the cause chain. Both identifiers are imported from the
68
- * harness module that writes them, so the two sides cannot drift.
71
+ * The surface-local hint for a failure, when the TUI can offer a better
72
+ * fix than the catalog's (currently: gateway credential failures resolve
73
+ * in-session via `/model`). Returns `undefined` to keep the harness hint.
69
74
  */
70
- export declare function isGatewayAuthFailure(event: FailureStreamEvent): boolean;
75
+ export declare function localFailureHint(event: FailureStreamEvent): string | undefined;
@@ -1 +1 @@
1
- import{GATEWAY_AUTHENTICATION_ERROR_NAME,GATEWAY_AUTH_FAILURE_SUMMARY_NAME}from"#harness/model-call-error.js";var InterruptedError=class extends Error{constructor(){super(`Interrupted`),this.name=`InterruptedError`}};function interruptedError(){return new InterruptedError}function isInterruptedError(e){return e instanceof InterruptedError}function isAbortLikeError(e){return e instanceof Error?e.name===`AbortError`||/\babort(?:ed)?\b/iu.test(e.message):!1}function failureKey(e){return`${e.data.code}:${e.data.message}`}function formatFailureMessage(e){let{code:t,message:n}=e.data;return!t||n===t||n.startsWith(`${t}:`)||n.startsWith(`${t} `)?n:`${t}: ${n}`}function formatFailureDetail(e){let t=e.data.details;if(typeof t!=`object`||!t)return;let n=t.detail;if(typeof n!=`string`)return;let r=n.trim();if(!(r.length===0||r===e.data.message.trim()))return r}function formatGatewayAuthFailureNotice(e){let t=e.data.message;return/rejected the provided API key|Invalid API key/i.test(t)?`AI Gateway rejected your AI_GATEWAY_API_KEY. Run /model to refresh credentials, or update it in .env.local (a stale shell export can shadow it).`:/rejected the OIDC token|Invalid OIDC token/i.test(t)?`Your AI Gateway OIDC token is invalid or expired. Run /model to refresh it, or set AI_GATEWAY_API_KEY in .env.local.`:`There is no AI_GATEWAY_API_KEY set. Run /model to connect this to a project and refresh AI Gateway credentials, or set it manually in .env.local.`}function isGatewayAuthFailure(n){let r=n.data.details;if(typeof r!=`object`||!r)return!1;let i=r;return i.gatewayName===GATEWAY_AUTHENTICATION_ERROR_NAME||i.name===GATEWAY_AUTH_FAILURE_SUMMARY_NAME}export{InterruptedError,failureKey,formatFailureDetail,formatFailureMessage,formatGatewayAuthFailureNotice,interruptedError,isAbortLikeError,isGatewayAuthFailure,isInterruptedError};
1
+ var InterruptedError=class extends Error{constructor(){super(`Interrupted`),this.name=`InterruptedError`}};function interruptedError(){return new InterruptedError}function isInterruptedError(e){return e instanceof InterruptedError}function isAbortLikeError(e){return e instanceof Error?e.name===`AbortError`||/\babort(?:ed)?\b/iu.test(e.message):!1}function failureKey(e){return`${e.data.code}:${e.data.message}`}function formatFailureMessage(e){let{code:t,message:n}=e.data;return!t||n===t||n.startsWith(`${t}:`)||n.startsWith(`${t} `)?n:`${t}: ${n}`}function failureDetails(e){let t=e.data.details;if(typeof t!=`object`||!t)return{};let n=t,r={};return typeof n.semanticErrorId==`string`&&(r.semanticErrorId=n.semanticErrorId),typeof n.hint==`string`&&n.hint.trim().length>0&&(r.hint=n.hint.trim()),typeof n.detail==`string`&&(r.detail=n.detail),r}function formatFailureHint(e){return failureDetails(e).hint}function formatFailureDetail(e){let t=failureDetails(e).detail;if(t===void 0)return;let n=t.trim();if(!(n.length===0||n===e.data.message.trim()))return n}const LOCAL_HINT_OVERRIDES={"gateway-auth-invalid-api-key":`Run /model to refresh credentials, or update AI_GATEWAY_API_KEY in .env.local (a stale shell export can shadow it).`,"gateway-auth-invalid-oidc-token":`Run /model to refresh the OIDC token, or set AI_GATEWAY_API_KEY in .env.local.`,"gateway-auth-missing-credentials":`Run /model to connect this to a project and refresh AI Gateway credentials, or set AI_GATEWAY_API_KEY manually in .env.local.`};function localFailureHint(t){let n=failureDetails(t).semanticErrorId;return n===void 0?void 0:LOCAL_HINT_OVERRIDES[n]}export{InterruptedError,failureDetails,failureKey,formatFailureDetail,formatFailureHint,formatFailureMessage,interruptedError,isAbortLikeError,isInterruptedError,localFailureHint};
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Session-local knowledge of file contents, so the write block can show a
3
+ * real diff without any protocol addition. Two feeds keep it current: every
4
+ * `write_file` input is a full replacement (exact), and every full-file
5
+ * `read_file` result reconstructs to the file's exact lines. Files touched
6
+ * outside these two paths (e.g. by `bash`) are invisible here — a write over
7
+ * unknown prior content renders plain rather than pretending to be a diff.
8
+ */
9
+ export declare class FileContentCache {
10
+ #private;
11
+ /**
12
+ * Records one write and returns the content it replaced. Tool blocks
13
+ * re-render on every lifecycle event of the same call, so a repeated
14
+ * observation of `callId` keeps returning the original previous content
15
+ * instead of diffing the write against itself.
16
+ */
17
+ observeWrite(input: {
18
+ path: string;
19
+ content: string;
20
+ callId: string;
21
+ }): string | undefined;
22
+ /**
23
+ * Forgets everything. A new conversation runs against a fresh session —
24
+ * possibly a fresh sandbox — so stale bases would produce confidently
25
+ * wrong diffs.
26
+ */
27
+ clear(): void;
28
+ /**
29
+ * Records a read result when it provably covers the whole file: not
30
+ * truncated, starting at line 1, with exactly `totalLines` numbered lines.
31
+ * Partial or truncated reads are ignored — caching them would produce
32
+ * confidently wrong diffs later.
33
+ */
34
+ observeRead(output: unknown): void;
35
+ }
@@ -0,0 +1,3 @@
1
+ const MAX_CACHED_CONTENT_CHARS=2e5;var FileContentCache=class{#e=new Map;observeWrite(t){let n=this.#e.get(t.path);if(n?.writerCallId===t.callId)return n.previous;if(t.content.length>MAX_CACHED_CONTENT_CHARS){this.#e.delete(t.path);return}let r=n?.content,i={content:t.content,writerCallId:t.callId};return r!==void 0&&(i.previous=r),this.#e.set(t.path,i),r}clear(){this.#e.clear()}observeRead(t){let n=readFileResultShape(t);if(n===void 0||n.truncated||n.content.length>MAX_CACHED_CONTENT_CHARS)return;let r=reconstructNumberedLines(n.content);r===void 0||r.length!==n.totalLines||this.#e.set(n.path,{content:r.join(`
2
+ `)})}};function readFileResultShape(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let{content:t,path:n,totalLines:r,truncated:i}=e;if(!(typeof t!=`string`||typeof n!=`string`)&&!(typeof r!=`number`||typeof i!=`boolean`))return{content:t,path:n,totalLines:r,truncated:i}}function reconstructNumberedLines(e){if(e===``)return[];let t=[];for(let[n,r]of e.split(`
3
+ `).entries()){let e=/^(\d+): (.*)$/su.exec(r);if(e===null||Number(e[1])!==n+1)return;t.push(e[2])}return t}export{FileContentCache};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Pure line diffing for the write block's rail. The TUI computes diffs
3
+ * client-side from content it has already seen (write inputs, full-file read
4
+ * results) — putting prior content into the tool's output would feed it
5
+ * straight back into model context, paying tokens for something only the
6
+ * terminal needs.
7
+ */
8
+ /** One rail row under a tool header. `kind` is unset for plain/context text. */
9
+ export interface ToolDetailLine {
10
+ readonly text: string;
11
+ readonly kind?: "added" | "removed" | "gap";
12
+ }
13
+ /**
14
+ * Builds the write rail for `content` against what it replaced.
15
+ *
16
+ * - `previous` known → a windowed line diff (changes with two context lines,
17
+ * unchanged stretches collapsed behind `gap` rows); identical content
18
+ * yields no rows.
19
+ * - `previous` unknown but the file provably did not exist → every line is
20
+ * an addition.
21
+ * - otherwise → plain content rows, because inventing `+` markers for an
22
+ * overwrite would misread as "everything changed".
23
+ */
24
+ export declare function diffWriteDetail(previous: string | undefined, content: string, existed?: boolean): ToolDetailLine[];
@@ -0,0 +1 @@
1
+ function diffWriteDetail(e,t,n){let r=splitContentLines(t);if(e===void 0)return n===!1?r.map(e=>({text:e,kind:`added`})):r.map(e=>({text:e}));let i=splitContentLines(e);return(i.length+1)*(r.length+1)>25e4?r.map(e=>({text:e})):windowHunks(lcsDiff(i,r))}function splitContentLines(e){let t=e.split(/\r?\n/u);for(;t.length>0&&t.at(-1)?.trim()===``;)t.pop();return t}function lcsDiff(e,t){let n=t.length+1,r=new Uint32Array((e.length+1)*n);for(let i=e.length-1;i>=0;--i)for(let a=t.length-1;a>=0;--a)r[i*n+a]=e[i]===t[a]?r[(i+1)*n+a+1]+1:Math.max(r[(i+1)*n+a],r[i*n+a+1]);let i=[],a=0,o=0;for(;a<e.length&&o<t.length;)e[a]===t[o]?(i.push({text:e[a]}),a+=1,o+=1):r[(a+1)*n+o]>=r[a*n+o+1]?(i.push({text:e[a],kind:`removed`}),a+=1):(i.push({text:t[o],kind:`added`}),o+=1);for(;a<e.length;a+=1)i.push({text:e[a],kind:`removed`});for(;o<t.length;o+=1)i.push({text:t[o],kind:`added`});return i}function windowHunks(e){let t=e.map(e=>e.kind===`added`||e.kind===`removed`);if(!t.includes(!0))return[];let n=e.map((n,r)=>{for(let n=Math.max(0,r-2);n<=Math.min(e.length-1,r+2);n+=1)if(t[n])return!0;return!1}),r=[];for(let[t,i]of e.entries())n[t]?r.push(i):r.at(-1)?.kind!==`gap`&&r.push({text:``,kind:`gap`});return r}export{diffWriteDetail};
@@ -1,4 +1,4 @@
1
- import{inputTextWidth,offsetAtVisibleColumn}from"./terminal-text.js";import{graphemeBoundaryAtOrAfter,graphemes,nextGraphemeBoundary,previousGraphemeBoundary}from"#shared/text-boundaries.js";const EMPTY_LINE={text:``,cursor:0};function lineOf(e){return{text:e,cursor:e.length}}function maskLine(e){return{text:`•`.repeat(graphemes(e.text).length),cursor:graphemes(e.text.slice(0,e.cursor)).length}}function insert(e,t){if(t.length===0)return e;let r=e.text.slice(0,e.cursor)+t+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor+t.length)}}function backspace(e){if(e.cursor===0)return e;let t=previousGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,t)+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,t)}}function deleteForward(e){if(e.cursor>=e.text.length)return e;let t=nextGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,e.cursor)+e.text.slice(t);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor)}}function moveLeft(e){return e.cursor===0?e:{text:e.text,cursor:previousGraphemeBoundary(e.text,e.cursor)}}function moveRight(e){return e.cursor>=e.text.length?e:{text:e.text,cursor:nextGraphemeBoundary(e.text,e.cursor)}}function moveHome(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function moveEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function killToEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor>=t?e:{text:e.text.slice(0,e.cursor)+e.text.slice(t),cursor:e.cursor}}function killToStart(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor<=t?e:{text:e.text.slice(0,t)+e.text.slice(e.cursor),cursor:t}}function deleteWord(e){if(e.cursor===0)return e;let t=logicalLineStart(e.text,e.cursor),r=e.cursor;for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(!isWhitespace(e.text.slice(t,r)))break;r=t}for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(isWhitespace(e.text.slice(t,r)))break;r=t}let i=e.text.slice(0,r)+e.text.slice(e.cursor);return{text:i,cursor:graphemeBoundaryAtOrAfter(i,r)}}function logicalLineStart(e,t){return t===0?0:e.lastIndexOf(`
1
+ import{inputTextWidth,offsetAtVisibleColumn}from"#cli/ui/terminal-text.js";import{graphemeBoundaryAtOrAfter,graphemes,nextGraphemeBoundary,previousGraphemeBoundary}from"#shared/text-boundaries.js";const EMPTY_LINE={text:``,cursor:0};function lineOf(e){return{text:e,cursor:e.length}}function maskLine(e){return{text:`•`.repeat(graphemes(e.text).length),cursor:graphemes(e.text.slice(0,e.cursor)).length}}function insert(e,t){if(t.length===0)return e;let r=e.text.slice(0,e.cursor)+t+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor+t.length)}}function backspace(e){if(e.cursor===0)return e;let t=previousGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,t)+e.text.slice(e.cursor);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,t)}}function deleteForward(e){if(e.cursor>=e.text.length)return e;let t=nextGraphemeBoundary(e.text,e.cursor),r=e.text.slice(0,e.cursor)+e.text.slice(t);return{text:r,cursor:graphemeBoundaryAtOrAfter(r,e.cursor)}}function moveLeft(e){return e.cursor===0?e:{text:e.text,cursor:previousGraphemeBoundary(e.text,e.cursor)}}function moveRight(e){return e.cursor>=e.text.length?e:{text:e.text,cursor:nextGraphemeBoundary(e.text,e.cursor)}}function moveHome(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function moveEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor===t?e:{text:e.text,cursor:t}}function killToEnd(e){let t=logicalLineEnd(e.text,e.cursor);return e.cursor>=t?e:{text:e.text.slice(0,e.cursor)+e.text.slice(t),cursor:e.cursor}}function killToStart(e){let t=logicalLineStart(e.text,e.cursor);return e.cursor<=t?e:{text:e.text.slice(0,t)+e.text.slice(e.cursor),cursor:t}}function deleteWord(e){if(e.cursor===0)return e;let t=logicalLineStart(e.text,e.cursor),r=e.cursor;for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(!isWhitespace(e.text.slice(t,r)))break;r=t}for(;r>t;){let t=previousGraphemeBoundary(e.text,r);if(isWhitespace(e.text.slice(t,r)))break;r=t}let i=e.text.slice(0,r)+e.text.slice(e.cursor);return{text:i,cursor:graphemeBoundaryAtOrAfter(i,r)}}function logicalLineStart(e,t){return t===0?0:e.lastIndexOf(`
2
2
  `,t-1)+1}function logicalLineEnd(e,t){let n=e.indexOf(`
3
3
  `,t);return n===-1?e.length:n}function applyLineEditorKey(e,t,n){let r=n?.multiline??!1;switch(t.type){case`text`:return insert(e,r?t.value:t.value.replaceAll(`
4
4
  `,` `));case`newline`:return r?insert(e,`
@@ -1 +1,2 @@
1
- export declare function renderMarkdown(input: string): string;
1
+ /** Renders parsed GFM blocks to terminal text, fitting tables to `width`. */
2
+ export declare function renderMarkdown(input: string, width?: number): string;
@@ -1,3 +1,7 @@
1
- import{visibleLength}from"./terminal-text.js";const ansi={bold:`\x1B[1m`,boldOff:`\x1B[22m`,italic:`\x1B[3m`,italicOff:`\x1B[23m`};function renderMarkdown(e){let t=e.split(`
2
- `),n=[];for(let e=0;e<t.length;e+=1){let r=parseTable(t,e);if(r!=null){n.push(...renderTable(r)),e=r.endIndex-1;continue}n.push(renderMarkdownLine(t[e]??``))}return n.join(`
3
- `)}function renderMarkdownLine(e){if(e.startsWith(`### `))return renderInlineMarkdown(`▶ ${e.slice(4)}`);if(e.startsWith(`## `))return renderInlineMarkdown(`■ ${e.slice(3)}`);if(e.startsWith(`# `))return renderInlineMarkdown(`█ ${e.slice(2)}`);let t=e.match(/^(\s*)[-+*]\s+(.*)$/);if(t){let[,e,n=``]=t;return renderInlineMarkdown(`${e}•${n.length>0?` ${n}`:``}`)}return/^\d+\. /.test(e)?renderInlineMarkdown(e.replace(/^(\d+)\. /,`$1. `)):e.startsWith(`> `)?renderInlineMarkdown(`│ ${e.slice(2)}`):renderInlineMarkdown(e)}function renderInlineMarkdown(e){let n=[];return e.replaceAll(/https?:\/\/\S+/g,e=>{let t=`\uE000${n.length}\uE001`;return n.push(e),t}).replaceAll(/`([^`]+)`/g,`$1`).replaceAll(/\*\*([^*\n]+)\*\*/g,`${ansi.bold}$1${ansi.boldOff}`).replaceAll(/__([^_\n]+)__/g,`${ansi.bold}$1${ansi.boldOff}`).replaceAll(/\*([^*\n]+)\*/g,`${ansi.italic}$1${ansi.italicOff}`).replaceAll(/_([^_\n]+)_/g,`${ansi.italic}$1${ansi.italicOff}`).replaceAll(/\uE000(\d+)\uE001/g,(e,t)=>n[Number(t)]??``)}function parseTable(e,t){let n=parseTableCells(e[t]??``);if(n==null||n.length<2)return;let r=parseTableCells(e[t+1]??``);if(r==null||r.length!==n.length)return;let i=parseTableAlignments(r);if(i==null)return;let a=[],o=t+2;for(;o<e.length;){let t=parseTableCells(e[o]??``);if(t==null)break;a.push(normalizeTableRow(t,n.length)),o+=1}return{alignments:i,endIndex:o,header:n,rows:a}}function parseTableCells(e){if(!e.includes(`|`))return;let t=e.trim();t.startsWith(`|`)&&(t=t.slice(1)),t.endsWith(`|`)&&!t.endsWith(`\\|`)&&(t=t.slice(0,-1));let n=[],r=``;for(let e=0;e<t.length;e+=1){let i=t[e],a=t[e+1];if(i===`\\`&&a===`|`){r+=`|`,e+=1;continue}if(i===`|`){n.push(r.trim()),r=``;continue}r+=i}return n.push(r.trim()),n}function parseTableAlignments(e){let t=[];for(let n of e){let e=n.match(/^(:)?-{3,}(:)?$/);if(e==null)return;let[,r,i]=e;t.push(r!=null&&i!=null?`center`:i==null?`left`:`right`)}return t}function normalizeTableRow(e,t){return Array.from({length:t},(t,n)=>e[n]??``)}function renderTable(n){let r=n.header.map(e=>`${ansi.bold}${renderInlineMarkdown(e)}${ansi.boldOff}`),i=n.rows.map(e=>e.map(renderInlineMarkdown)),a=[r,...i],o=n.alignments.map((t,n)=>Math.max(3,...a.map(t=>visibleLength(t[n]??``))));return[formatTableRow(r,o,n.alignments),o.map(e=>`─`.repeat(e)).join(` `),...i.map(e=>formatTableRow(e,o,n.alignments))]}function formatTableRow(e,t,n){return e.map((e,r)=>alignTableCell(e,t[r]??0,n[r]??`left`)).join(` `)}function alignTableCell(t,n,r){let i=Math.max(0,n-visibleLength(t));if(r===`right`)return`${` `.repeat(i)}${t}`;if(r===`center`){let e=Math.floor(i/2),n=i-e;return`${` `.repeat(e)}${t}${` `.repeat(n)}`}return`${t}${` `.repeat(i)}`}export{renderMarkdown};
1
+ import{sliceVisible,visibleLength}from"#cli/ui/terminal-text.js";import{lexer}from"#compiled/marked/index.js";const ansi={bold:`\x1B[1m`,boldOff:`\x1B[22m`,cyan:`\x1B[36m`,cyanOff:`\x1B[39m`,dim:`\x1B[2m`,dimOff:`\x1B[22m`,italic:`\x1B[3m`,italicOff:`\x1B[23m`,strike:`\x1B[9m`,strikeOff:`\x1B[29m`};function renderMarkdown(e,t=1/0){return trimTrailingBlankRows(renderBlocks(lexer(e),t)).join(`
2
+ `)}function renderBlocks(e,t){let n=[];for(let i of e)switch(i.type){case`space`:pushBlankRow(n);break;case`heading`:{let e=i.depth===1?`█`:i.depth===2?`■`:`▶`;n.push(`${e} ${ansi.bold}${renderInline(i.tokens)}${ansi.boldOff}`);break}case`paragraph`:case`text`:n.push(...renderInline(i.tokens,i.text).split(`
3
+ `));break;case`code`:n.push(...renderCodeBlock(i));break;case`blockquote`:n.push(...renderBlocks(i.tokens??[],Math.max(8,t-2)).map(e=>`${ansi.dim}│${ansi.dimOff} ${e}`));break;case`list`:n.push(...renderList(i,t));break;case`table`:n.push(...renderTable(i,t));break;case`hr`:n.push(ansi.dim+`─`.repeat(horizontalRuleWidth(t))+ansi.dimOff);break;case`html`:n.push(...(i.text??i.raw??``).split(`
4
+ `));break;case`def`:break;default:i.tokens===void 0?i.text!==void 0&&n.push(i.text):n.push(...renderBlocks(i.tokens,t));break}return n}function renderCodeBlock(e){let t=[];e.lang?.trim()&&t.push(` ${ansi.dim}${e.lang.trim()}${ansi.dimOff}`);let n=(e.text??``).replace(/\n$/u,``).split(`
5
+ `);for(let e of n)t.push(`${ansi.dim}│${ansi.dimOff} ${ansi.cyan}${e}${ansi.cyanOff}`);return t}function renderList(e,t){let n=[],r=typeof e.start==`number`?e.start:1;for(let i of e.items??[]){let a=e.ordered?`${r}.`:`•`,o=i.checked===!0?`☑ `:i.checked===!1?`☐ `:``,s=renderBlocks(i.tokens??[],Math.max(8,t-a.length-1));(s.length===0?[``]:s).forEach((e,t)=>{n.push(t===0?`${a} ${o}${e}`:`${` `.repeat(a.length+1)}${e}`)}),r+=1}return n}function renderInline(e,t=``){return e===void 0?t:e.map(renderInlineToken).join(``)}function renderInlineToken(e){switch(e.type){case`text`:return renderInline(e.tokens,e.text??``);case`strong`:return`${ansi.bold}${renderInline(e.tokens,e.text)}${ansi.boldOff}`;case`em`:return`${ansi.italic}${renderInline(e.tokens,e.text)}${ansi.italicOff}`;case`del`:return`${ansi.strike}${renderInline(e.tokens,e.text)}${ansi.strikeOff}`;case`codespan`:return`${ansi.cyan}${e.text??``}${ansi.cyanOff}`;case`link`:{let t=renderInline(e.tokens,e.text),n=e.href??``;return n.length===0||t===n?`${ansi.cyan}${t}${ansi.cyanOff}`:`${t} (${ansi.cyan}${n}${ansi.cyanOff})`}case`image`:{let t=e.text?.trim()||`image`,n=e.href??``;return n.length===0?`[${t}]`:`[${t}] (${ansi.cyan}${n}${ansi.cyanOff})`}case`br`:return`
6
+ `;case`checkbox`:return e.checked?`☑ `:`☐ `;case`escape`:return e.text??``;case`html`:return e.text??e.raw??``;default:return renderInline(e.tokens,e.text??e.raw??``)}}function renderTable(e,n){let i=e.header??[];if(i.length===0)return[];let a=i.map((t,n)=>normalizeAlignment(e.align?.[n])),o=i.map(renderTableCell),s=(e.rows??[]).map(e=>normalizeTableRow(e,i.length).map(renderTableCell)),c=[o,...s],l=a.map((e,n)=>Math.max(3,...c.map(e=>visibleLength(e[n]??``))));return fitTableWidths(l,n),[formatTableRow(o.map(e=>`${ansi.bold}${e}${ansi.boldOff}`),l,a),l.map(e=>`─`.repeat(e)).join(` `),...s.map(e=>formatTableRow(e,l,a))]}function renderTableCell(e){return renderInline(e.tokens,e.text).replaceAll(`
7
+ `,` `)}function normalizeTableRow(e,t){return Array.from({length:t},(t,n)=>e[n]??{text:``,tokens:[]})}function normalizeAlignment(e){return e??`left`}function fitTableWidths(e,t){if(!Number.isFinite(t))return;let n=Math.max(0,e.length-1)*2,r=Math.max(e.length*3,Math.floor(t)-n);for(;e.reduce((e,t)=>e+t,0)>r;){let t=Math.max(...e),n=e.findIndex(e=>e===t&&e>3);if(n===-1)return;e[n]=t-1}}function formatTableRow(e,t,n){return e.map((e,r)=>alignTableCell(fitTableCell(e,t[r]??3),t[r]??3,n[r]??`left`)).join(` `)}function fitTableCell(n,r){return visibleLength(n)<=r?n:`${sliceVisible(n,Math.max(1,r-1))}…`}function alignTableCell(e,n,r){let i=Math.max(0,n-visibleLength(e));if(r===`right`)return`${` `.repeat(i)}${e}`;if(r===`center`){let t=Math.floor(i/2);return`${` `.repeat(t)}${e}${` `.repeat(i-t)}`}return`${e}${` `.repeat(i)}`}function horizontalRuleWidth(e){return Number.isFinite(e)?Math.max(3,Math.min(40,Math.floor(e))):40}function pushBlankRow(e){e.length>0&&e.at(-1)!==``&&e.push(``)}function trimTrailingBlankRows(e){for(;e.at(-1)===``;)e.pop();return e}export{renderMarkdown};