@slopus/happy-terminal 0.3.1 → 0.3.3

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/README.md CHANGED
@@ -43,7 +43,8 @@ await runHappyTerminal({
43
43
  ```
44
44
 
45
45
  Embedded sessions use `happy` in resume instructions by default. A different host command can be
46
- provided with `commandName`.
46
+ provided with `commandName`. An embedded host may also provide `version` to replace the installed
47
+ Happy Terminal package version in the startup UI.
47
48
 
48
49
  Run only one inline terminal at a time because each instance owns the process terminal while it is
49
50
  active. Happy Terminal requires Node.js 24 or newer.
@@ -17,6 +17,8 @@ export interface RunAppOptions {
17
17
  sessionSelection?: StartupSessionSelection;
18
18
  showReasoning?: boolean;
19
19
  showUsage?: boolean;
20
+ /** Version displayed by the host, defaulting to the installed Happy Terminal package. */
21
+ version?: string;
20
22
  }
21
23
  export type RunAppResult = {
22
24
  action: "exit";
package/dist/index.js CHANGED
@@ -889,6 +889,9 @@ function delay2(milliseconds) {
889
889
  }
890
890
 
891
891
  // sources/daemon/runDaemonCommand.ts
892
+ import {
893
+ HappyAgentApiError
894
+ } from "@slopus/happy-agent-client";
892
895
  import { createRootContext as createRootContext2 } from "@steve.kite/stdlib";
893
896
 
894
897
  // sources/processes/killProcessTree.ts
@@ -1806,7 +1809,7 @@ async function delay3(ms, signal) {
1806
1809
  import { resolve } from "node:path";
1807
1810
  import { setTimeout as sleep } from "node:timers/promises";
1808
1811
  import {
1809
- HappyAgentApiError
1812
+ HappyAgentApiError as HappyAgentApiError2
1810
1813
  } from "@slopus/happy-agent-client";
1811
1814
  var WORKSPACE_INITIALIZATION_TIMEOUT_MS = 12e4;
1812
1815
  var WORKSPACE_INITIALIZATION_POLL_MS = 50;
@@ -1876,7 +1879,7 @@ async function waitForWorkspaceReady(client, workspaceId) {
1876
1879
  try {
1877
1880
  return (await client.getWorkspace(workspaceId)).workspace;
1878
1881
  } catch (error) {
1879
- if (!(error instanceof HappyAgentApiError) || error.code !== "not_initialized") {
1882
+ if (!(error instanceof HappyAgentApiError2) || error.code !== "not_initialized") {
1880
1883
  throw error;
1881
1884
  }
1882
1885
  if (Date.now() >= deadline) {
@@ -9346,76 +9349,26 @@ function renderPendingSteeringMessages(messages, width) {
9346
9349
 
9347
9350
  // sources/app/renderHappyTerminalBanner.ts
9348
9351
  import { truncateToWidth as truncateToWidth20 } from "@earendil-works/pi-tui";
9349
-
9350
- // sources/app/renderHappyTerminalVersion.ts
9351
- var VERSION_GLYPHS = {
9352
- "0": [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
9353
- "1": [" \u2588\u2588\u2557", "\u2588\u2588\u2588\u2551", "\u255A\u2588\u2588\u2551", " \u2588\u2588\u2551", " \u2588\u2588\u2551", " \u255A\u2550\u255D"],
9354
- "2": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2557", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
9355
- "3": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2557", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
9356
- "4": ["\u2588\u2588\u2557 \u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2551", " \u255A\u2550\u255D"],
9357
- "5": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
9358
- "6": [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
9359
- "7": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2554\u255D", " \u2588\u2588\u2554\u255D ", " \u2588\u2588\u2551 ", " \u255A\u2550\u255D "],
9360
- "8": [" \u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u255D "],
9361
- "9": [" \u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2551", " \u255A\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u255D "],
9362
- ".": [" ", " ", " ", " ", "\u2588\u2588\u2557", "\u255A\u2550\u255D"]
9363
- };
9364
- function renderHappyTerminalVersion(version, width) {
9365
- const glyphs = [...version].map((character) => VERSION_GLYPHS[character]);
9366
- if (width < 40 || glyphs.some((glyph) => glyph === void 0)) {
9367
- return [version.slice(0, width)];
9368
- }
9369
- const chunks = [];
9370
- let chunk = [];
9371
- for (const glyph of glyphs) {
9372
- const candidate = [...chunk, glyph];
9373
- const candidateWidth = Math.max(
9374
- ...Array.from(
9375
- { length: 6 },
9376
- (_, rowIndex) => candidate.reduce((rowWidth, item) => rowWidth + (item[rowIndex]?.length ?? 0), 0)
9377
- )
9378
- );
9379
- if (chunk.length > 0 && candidateWidth > width) {
9380
- chunks.push(chunk);
9381
- chunk = [glyph];
9382
- } else {
9383
- chunk = candidate;
9384
- }
9385
- }
9386
- if (chunk.length > 0) chunks.push(chunk);
9387
- return chunks.flatMap((items) => {
9388
- const rows = Array.from(
9389
- { length: 6 },
9390
- (_, rowIndex) => items.map((glyph) => glyph[rowIndex] ?? "").join("").trimEnd()
9391
- );
9392
- while (rows[0] === "") rows.shift();
9393
- while (rows.at(-1) === "") rows.pop();
9394
- return rows;
9395
- });
9396
- }
9397
-
9398
- // sources/app/renderHappyTerminalBanner.ts
9399
9352
  var RESET19 = "\x1B[0m";
9400
- var HAPPY_TERMINAL_LOGO = [
9353
+ var HAPPY_LOGO = [
9401
9354
  "\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557",
9402
9355
  "\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D",
9403
9356
  "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u255A\u2588\u2588\u2588\u2588\u2554\u255D ",
9404
9357
  "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u255A\u2588\u2588\u2554\u255D ",
9405
- "\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ",
9406
- "\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D TERMINAL"
9358
+ "\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ",
9359
+ "\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D "
9407
9360
  ];
9408
- var HAPPY_TERMINAL_LOGO_WIDTH = 54;
9361
+ var HAPPY_LOGO_WIDTH = Math.max(...HAPPY_LOGO.map((line) => line.length));
9409
9362
  var BANNER_GAP = " ";
9410
9363
  var BANNER_PADDING = " ";
9411
9364
  function renderHappyTerminalBanner(options) {
9412
9365
  const width = Math.max(1, options.width);
9413
9366
  const contentWidth = width - BANNER_PADDING.length * 2;
9414
9367
  if (contentWidth <= 0) return [" ".repeat(width)];
9415
- if (contentWidth < HAPPY_TERMINAL_LOGO_WIDTH + BANNER_GAP.length + options.version.length) {
9368
+ if (contentWidth < HAPPY_LOGO_WIDTH + BANNER_GAP.length + options.version.length) {
9416
9369
  const lines3 = [
9417
9370
  truncateToWidth20(
9418
- `${options.brand}Happy Terminal${RESET19} ${options.secondary}${options.version}${RESET19}`,
9371
+ `${options.brand}HAPPY${RESET19} ${options.secondary}${options.version}${RESET19}`,
9419
9372
  contentWidth,
9420
9373
  "",
9421
9374
  false
@@ -9423,17 +9376,9 @@ function renderHappyTerminalBanner(options) {
9423
9376
  ];
9424
9377
  return padBannerLines(lines3);
9425
9378
  }
9426
- const versionWidth = contentWidth - HAPPY_TERMINAL_LOGO_WIDTH - BANNER_GAP.length;
9427
- const versionLines = renderHappyTerminalVersion(options.version, versionWidth);
9428
- if (versionLines.length === HAPPY_TERMINAL_LOGO.length) {
9429
- const lines3 = HAPPY_TERMINAL_LOGO.map(
9430
- (line, index) => `${options.brand}${line.padEnd(HAPPY_TERMINAL_LOGO_WIDTH)}${RESET19}${BANNER_GAP}${options.secondary}${versionLines[index]}${RESET19}`
9431
- );
9432
- return padBannerLines(lines3);
9433
- }
9434
- const lines2 = HAPPY_TERMINAL_LOGO.map((line, index) => {
9435
- const version = index === HAPPY_TERMINAL_LOGO.length - 1 ? `${BANNER_GAP}${options.secondary}${versionLines[0]}${RESET19}` : "";
9436
- return `${options.brand}${line.padEnd(HAPPY_TERMINAL_LOGO_WIDTH)}${RESET19}${version}`;
9379
+ const lines2 = HAPPY_LOGO.map((line, index) => {
9380
+ const version = index === HAPPY_LOGO.length - 1 ? `${BANNER_GAP}${options.secondary}${options.version}${RESET19}` : "";
9381
+ return `${options.brand}${line.padEnd(HAPPY_LOGO_WIDTH)}${RESET19}${version}`;
9437
9382
  });
9438
9383
  return padBannerLines(lines2);
9439
9384
  }
@@ -16604,6 +16549,7 @@ var StartupStatusApp = class {
16604
16549
  var INITIAL_TUI_MESSAGE_LIMIT = 30;
16605
16550
  async function runApp(ctx, options = {}) {
16606
16551
  const requestedCwd = options.cwd ?? process.cwd();
16552
+ const version = options.version ?? readPackageVersion();
16607
16553
  const loadedConfig = await loadConfig({ cwd: requestedCwd });
16608
16554
  let compactCompletedTurns = options.compactCompletedTurns ?? loadedConfig.config.settings.compactCompletedTurns;
16609
16555
  let completionChime = loadedConfig.config.settings.completionChime;
@@ -16620,7 +16566,7 @@ async function runApp(ctx, options = {}) {
16620
16566
  rows: () => terminal.rows,
16621
16567
  theme: startupTheme,
16622
16568
  tui,
16623
- version: readPackageVersion()
16569
+ version
16624
16570
  });
16625
16571
  const terminalCrashCleanup = installTerminalCrashCleanup({ terminal, tui });
16626
16572
  let terminalAppearance;
@@ -16743,7 +16689,6 @@ async function runApp(ctx, options = {}) {
16743
16689
  if (loadedConfig.config.defaults.serviceTier !== void 0) {
16744
16690
  agent.setServiceTier(loadedConfig.config.defaults.serviceTier);
16745
16691
  }
16746
- const version = readPackageVersion();
16747
16692
  const tuiInspectorUrl = getNodeInspectorUrl();
16748
16693
  const startupQuotas = await resolveStartupProviderQuota(() => agent.providerQuotas());
16749
16694
  const startupUsage = providerQuotaToStartupStatusUsage(
package/dist/main.js CHANGED
@@ -1799,6 +1799,9 @@ function delay2(milliseconds) {
1799
1799
  // sources/daemon/runDaemonCommand.ts
1800
1800
  init_HappyTerminalUserError();
1801
1801
  import { lstat as lstat2 } from "node:fs/promises";
1802
+ import {
1803
+ HappyAgentApiError
1804
+ } from "@slopus/happy-agent-client";
1802
1805
  import { createRootContext as createRootContext2 } from "@steve.kite/stdlib";
1803
1806
 
1804
1807
  // sources/processes/killProcessTree.ts
@@ -2768,7 +2771,7 @@ function errorCode(error) {
2768
2771
 
2769
2772
  // sources/daemon/runDaemonCommand.ts
2770
2773
  var DAEMON_SHUTDOWN_TIMEOUT_MS = 3e4;
2771
- var DAEMON_RELOAD_GRACE_TIMEOUT_MS = 5e3;
2774
+ var DRAIN_POLL_INTERVAL_MS = 100;
2772
2775
  async function runDaemonCommand(command, log = console.log, ctx = createRootContext2().named("daemon-command")) {
2773
2776
  const paths = getHappyDaemonPaths();
2774
2777
  if (command === "start") {
@@ -2791,10 +2794,7 @@ async function runDaemonCommand(command, log = console.log, ctx = createRootCont
2791
2794
  const observed = await observeLocalProtocolServer(paths);
2792
2795
  if (command === "reload") {
2793
2796
  if (observed !== void 0) {
2794
- await stopLocalProtocolServer(ctx, observed.client, paths, {
2795
- forceAfterMs: DAEMON_RELOAD_GRACE_TIMEOUT_MS,
2796
- onForce: log
2797
- });
2797
+ await stopLocalProtocolServer(ctx, observed.client, paths, log);
2798
2798
  } else await assertNoUnresponsiveDaemon(paths.pidPath);
2799
2799
  const connection = await ensureLocalProtocolServer();
2800
2800
  log(`Daemon is running at ${connection.paths.socketPath}`);
@@ -2812,8 +2812,13 @@ async function runDaemonCommand(command, log = console.log, ctx = createRootCont
2812
2812
  log("Daemon is not running.");
2813
2813
  }
2814
2814
  } else if (observed.health.ready) {
2815
- log(`Daemon is running at ${paths.socketPath}`);
2815
+ log(
2816
+ observed.health.draining === true ? `Daemon is draining at ${paths.socketPath}` : `Daemon is running at ${paths.socketPath}`
2817
+ );
2816
2818
  log(`Daemon PID: ${String(pid ?? "unknown")}`);
2819
+ if (observed.health.draining === true) {
2820
+ log(formatDrainProgress(observed.health.drainWaitingFor ?? []));
2821
+ }
2817
2822
  } else {
2818
2823
  log(`Daemon is starting at ${paths.socketPath}`);
2819
2824
  log(`Daemon PID: ${String(pid ?? "unknown")}`);
@@ -2828,28 +2833,25 @@ async function runDaemonCommand(command, log = console.log, ctx = createRootCont
2828
2833
  return;
2829
2834
  }
2830
2835
  log("Daemon is stopping.");
2831
- await stopLocalProtocolServer(ctx, observed.client, paths);
2836
+ await stopLocalProtocolServer(ctx, observed.client, paths, log);
2832
2837
  log("Daemon stopped.");
2833
2838
  }
2834
- async function stopLocalProtocolServer(ctx, client, paths, options = {}) {
2839
+ async function stopLocalProtocolServer(ctx, client, paths, report) {
2835
2840
  let pid = await readDaemonPid(paths.pidPath);
2841
+ await drainLocalProtocolServer(client, report);
2836
2842
  let requestError;
2837
2843
  try {
2838
2844
  pid = (await client.shutdown()).pid;
2839
2845
  } catch (error) {
2840
2846
  requestError = error;
2841
2847
  }
2842
- const timeoutMs = options.forceAfterMs ?? DAEMON_SHUTDOWN_TIMEOUT_MS;
2843
- const stopped = await waitForDaemonShutdown(ctx, paths.socketPath, pid, timeoutMs);
2848
+ const stopped = await waitForDaemonShutdown(
2849
+ ctx,
2850
+ paths.socketPath,
2851
+ pid,
2852
+ DAEMON_SHUTDOWN_TIMEOUT_MS
2853
+ );
2844
2854
  if (stopped.socketRemoved && stopped.processExited) return;
2845
- if (options.forceAfterMs !== void 0 && pid !== void 0) {
2846
- options.onForce?.(
2847
- `Daemon did not stop gracefully; forcing process ${String(pid)} to exit.`
2848
- );
2849
- if (!stopped.processExited) await killDaemonProcess(ctx, pid);
2850
- await removeDaemonPid(paths.pidPath, pid);
2851
- return;
2852
- }
2853
2855
  if (requestError !== void 0) {
2854
2856
  throw new Error(
2855
2857
  `Could not stop the existing local daemon: ${requestError instanceof Error ? requestError.message : String(requestError)}`
@@ -2864,6 +2866,45 @@ async function stopLocalProtocolServer(ctx, client, paths, options = {}) {
2864
2866
  );
2865
2867
  }
2866
2868
  }
2869
+ async function drainLocalProtocolServer(client, report) {
2870
+ try {
2871
+ await client.drain();
2872
+ } catch (error) {
2873
+ if (error instanceof HappyAgentApiError && (error.status === 403 || error.status === 404)) {
2874
+ report("This daemon does not support draining; stopping it directly.");
2875
+ return;
2876
+ }
2877
+ throw error;
2878
+ }
2879
+ let previous;
2880
+ for (; ; ) {
2881
+ const health = await client.getHealth();
2882
+ const waiting = health.drainWaitingFor ?? [];
2883
+ const current = JSON.stringify(waiting);
2884
+ if (current !== previous) {
2885
+ report(formatDrainProgress(waiting));
2886
+ previous = current;
2887
+ }
2888
+ if (waiting.length === 0) return;
2889
+ await new Promise((resolve4) => setTimeout(resolve4, DRAIN_POLL_INTERVAL_MS));
2890
+ }
2891
+ }
2892
+ function formatDrainProgress(waiting) {
2893
+ if (waiting.length === 0) return "Daemon drain is complete.";
2894
+ return `Draining: ${waiting.map(formatDrainWait).join("; ")}.`;
2895
+ }
2896
+ function formatDrainWait(wait) {
2897
+ const noun = wait.name === "api-mutations" ? plural(wait.count, "API mutation") : wait.name === "agent-system" ? plural(wait.count, "agent") : wait.name === "auto-agent-system" ? plural(wait.count, "permission reviewer") : `${String(wait.count)} ${wait.name.replaceAll("-", " ")}`;
2898
+ if (wait.agents === void 0 || wait.agents.length === 0) return noun;
2899
+ const agents = wait.agents.map((agent) => `${agent.id}: ${stageLabel(agent.stage)}`).join(", ");
2900
+ return `${noun} (${agents}${wait.truncated === true ? ", \u2026" : ""})`;
2901
+ }
2902
+ function stageLabel(stage) {
2903
+ return stage === "tools" ? "tool calls" : stage;
2904
+ }
2905
+ function plural(count2, noun) {
2906
+ return `${String(count2)} ${noun}${count2 === 1 ? "" : "s"}`;
2907
+ }
2867
2908
  async function waitForDaemonShutdown(ctx, socketPath, pid, timeoutMs) {
2868
2909
  const [socketRemoved, processExited] = await Promise.all([
2869
2910
  waitForSocketRemoval(socketPath, timeoutMs),
@@ -3017,7 +3058,7 @@ async function delay4(ms, signal) {
3017
3058
  import { resolve } from "node:path";
3018
3059
  import { setTimeout as sleep } from "node:timers/promises";
3019
3060
  import {
3020
- HappyAgentApiError
3061
+ HappyAgentApiError as HappyAgentApiError2
3021
3062
  } from "@slopus/happy-agent-client";
3022
3063
  var WORKSPACE_INITIALIZATION_TIMEOUT_MS = 12e4;
3023
3064
  var WORKSPACE_INITIALIZATION_POLL_MS = 50;
@@ -3087,7 +3128,7 @@ async function waitForWorkspaceReady(client, workspaceId) {
3087
3128
  try {
3088
3129
  return (await client.getWorkspace(workspaceId)).workspace;
3089
3130
  } catch (error) {
3090
- if (!(error instanceof HappyAgentApiError) || error.code !== "not_initialized") {
3131
+ if (!(error instanceof HappyAgentApiError2) || error.code !== "not_initialized") {
3091
3132
  throw error;
3092
3133
  }
3093
3134
  if (Date.now() >= deadline) {
@@ -9937,8 +9978,8 @@ var RESET13 = "\x1B[0m";
9937
9978
  var DIM10 = "\x1B[2m";
9938
9979
  function renderBackgroundTerminalSummary(count2, width) {
9939
9980
  if (count2 === 0) return void 0;
9940
- const plural = count2 === 1 ? "" : "s";
9941
- const summary = ` ${String(count2)} background terminal${plural} running \xB7 /ps to view \xB7 /stop to close`;
9981
+ const plural2 = count2 === 1 ? "" : "s";
9982
+ const summary = ` ${String(count2)} background terminal${plural2} running \xB7 /ps to view \xB7 /stop to close`;
9942
9983
  return truncateToWidth11(`${DIM10}${summary}${RESET13}`, Math.max(1, width), "", true);
9943
9984
  }
9944
9985
 
@@ -11020,76 +11061,26 @@ function renderPendingSteeringMessages(messages, width) {
11020
11061
 
11021
11062
  // sources/app/renderHappyTerminalBanner.ts
11022
11063
  import { truncateToWidth as truncateToWidth20 } from "@earendil-works/pi-tui";
11023
-
11024
- // sources/app/renderHappyTerminalVersion.ts
11025
- var VERSION_GLYPHS = {
11026
- "0": [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
11027
- "1": [" \u2588\u2588\u2557", "\u2588\u2588\u2588\u2551", "\u255A\u2588\u2588\u2551", " \u2588\u2588\u2551", " \u2588\u2588\u2551", " \u255A\u2550\u255D"],
11028
- "2": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2557", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
11029
- "3": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2557", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
11030
- "4": ["\u2588\u2588\u2557 \u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2551", " \u255A\u2550\u255D"],
11031
- "5": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
11032
- "6": [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
11033
- "7": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2554\u255D", " \u2588\u2588\u2554\u255D ", " \u2588\u2588\u2551 ", " \u255A\u2550\u255D "],
11034
- "8": [" \u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u255D "],
11035
- "9": [" \u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2551", " \u255A\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u255D "],
11036
- ".": [" ", " ", " ", " ", "\u2588\u2588\u2557", "\u255A\u2550\u255D"]
11037
- };
11038
- function renderHappyTerminalVersion(version, width) {
11039
- const glyphs = [...version].map((character) => VERSION_GLYPHS[character]);
11040
- if (width < 40 || glyphs.some((glyph) => glyph === void 0)) {
11041
- return [version.slice(0, width)];
11042
- }
11043
- const chunks = [];
11044
- let chunk = [];
11045
- for (const glyph of glyphs) {
11046
- const candidate = [...chunk, glyph];
11047
- const candidateWidth = Math.max(
11048
- ...Array.from(
11049
- { length: 6 },
11050
- (_, rowIndex) => candidate.reduce((rowWidth, item) => rowWidth + (item[rowIndex]?.length ?? 0), 0)
11051
- )
11052
- );
11053
- if (chunk.length > 0 && candidateWidth > width) {
11054
- chunks.push(chunk);
11055
- chunk = [glyph];
11056
- } else {
11057
- chunk = candidate;
11058
- }
11059
- }
11060
- if (chunk.length > 0) chunks.push(chunk);
11061
- return chunks.flatMap((items) => {
11062
- const rows = Array.from(
11063
- { length: 6 },
11064
- (_, rowIndex) => items.map((glyph) => glyph[rowIndex] ?? "").join("").trimEnd()
11065
- );
11066
- while (rows[0] === "") rows.shift();
11067
- while (rows.at(-1) === "") rows.pop();
11068
- return rows;
11069
- });
11070
- }
11071
-
11072
- // sources/app/renderHappyTerminalBanner.ts
11073
11064
  var RESET19 = "\x1B[0m";
11074
- var HAPPY_TERMINAL_LOGO = [
11065
+ var HAPPY_LOGO = [
11075
11066
  "\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557",
11076
11067
  "\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D",
11077
11068
  "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u255A\u2588\u2588\u2588\u2588\u2554\u255D ",
11078
11069
  "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u255A\u2588\u2588\u2554\u255D ",
11079
- "\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ",
11080
- "\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D TERMINAL"
11070
+ "\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ",
11071
+ "\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D "
11081
11072
  ];
11082
- var HAPPY_TERMINAL_LOGO_WIDTH = 54;
11073
+ var HAPPY_LOGO_WIDTH = Math.max(...HAPPY_LOGO.map((line) => line.length));
11083
11074
  var BANNER_GAP = " ";
11084
11075
  var BANNER_PADDING = " ";
11085
11076
  function renderHappyTerminalBanner(options) {
11086
11077
  const width = Math.max(1, options.width);
11087
11078
  const contentWidth = width - BANNER_PADDING.length * 2;
11088
11079
  if (contentWidth <= 0) return [" ".repeat(width)];
11089
- if (contentWidth < HAPPY_TERMINAL_LOGO_WIDTH + BANNER_GAP.length + options.version.length) {
11080
+ if (contentWidth < HAPPY_LOGO_WIDTH + BANNER_GAP.length + options.version.length) {
11090
11081
  const lines3 = [
11091
11082
  truncateToWidth20(
11092
- `${options.brand}Happy Terminal${RESET19} ${options.secondary}${options.version}${RESET19}`,
11083
+ `${options.brand}HAPPY${RESET19} ${options.secondary}${options.version}${RESET19}`,
11093
11084
  contentWidth,
11094
11085
  "",
11095
11086
  false
@@ -11097,17 +11088,9 @@ function renderHappyTerminalBanner(options) {
11097
11088
  ];
11098
11089
  return padBannerLines(lines3);
11099
11090
  }
11100
- const versionWidth = contentWidth - HAPPY_TERMINAL_LOGO_WIDTH - BANNER_GAP.length;
11101
- const versionLines = renderHappyTerminalVersion(options.version, versionWidth);
11102
- if (versionLines.length === HAPPY_TERMINAL_LOGO.length) {
11103
- const lines3 = HAPPY_TERMINAL_LOGO.map(
11104
- (line, index) => `${options.brand}${line.padEnd(HAPPY_TERMINAL_LOGO_WIDTH)}${RESET19}${BANNER_GAP}${options.secondary}${versionLines[index]}${RESET19}`
11105
- );
11106
- return padBannerLines(lines3);
11107
- }
11108
- const lines2 = HAPPY_TERMINAL_LOGO.map((line, index) => {
11109
- const version = index === HAPPY_TERMINAL_LOGO.length - 1 ? `${BANNER_GAP}${options.secondary}${versionLines[0]}${RESET19}` : "";
11110
- return `${options.brand}${line.padEnd(HAPPY_TERMINAL_LOGO_WIDTH)}${RESET19}${version}`;
11091
+ const lines2 = HAPPY_LOGO.map((line, index) => {
11092
+ const version = index === HAPPY_LOGO.length - 1 ? `${BANNER_GAP}${options.secondary}${options.version}${RESET19}` : "";
11093
+ return `${options.brand}${line.padEnd(HAPPY_LOGO_WIDTH)}${RESET19}${version}`;
11111
11094
  });
11112
11095
  return padBannerLines(lines2);
11113
11096
  }
@@ -11247,8 +11230,8 @@ var RESET21 = "\x1B[0m";
11247
11230
  var DIM18 = "\x1B[2m";
11248
11231
  function renderSubagentSummary(options) {
11249
11232
  if (options.count === 0) return void 0;
11250
- const plural = options.count === 1 ? "" : "s";
11251
- const summary = ` ${String(options.count)} agent${plural} running \xB7 /agents to view \xB7 ${formatActivityElapsedTime(options.elapsedMs)} \xB7 ${formatCompactTokens(options.totalTokens)} context tokens`;
11233
+ const plural2 = options.count === 1 ? "" : "s";
11234
+ const summary = ` ${String(options.count)} agent${plural2} running \xB7 /agents to view \xB7 ${formatActivityElapsedTime(options.elapsedMs)} \xB7 ${formatCompactTokens(options.totalTokens)} context tokens`;
11252
11235
  return truncateToWidth23(`${DIM18}${summary}${RESET21}`, Math.max(1, options.width), "", true);
11253
11236
  }
11254
11237
 
@@ -11271,8 +11254,8 @@ var RESET23 = "\x1B[0m";
11271
11254
  var DIM20 = "\x1B[2m";
11272
11255
  function renderWorkflowSummary(count2, width) {
11273
11256
  if (count2 === 0) return void 0;
11274
- const plural = count2 === 1 ? "" : "s";
11275
- const summary = ` ${String(count2)} workflow${plural} running \xB7 /workflows to view`;
11257
+ const plural2 = count2 === 1 ? "" : "s";
11258
+ const summary = ` ${String(count2)} workflow${plural2} running \xB7 /workflows to view`;
11276
11259
  return truncateToWidth25(`${DIM20}${summary}${RESET23}`, Math.max(1, width), "", true);
11277
11260
  }
11278
11261
 
@@ -18281,6 +18264,7 @@ var StartupStatusApp = class {
18281
18264
  var INITIAL_TUI_MESSAGE_LIMIT = 30;
18282
18265
  async function runApp(ctx, options = {}) {
18283
18266
  const requestedCwd = options.cwd ?? process.cwd();
18267
+ const version = options.version ?? readPackageVersion();
18284
18268
  const loadedConfig = await loadConfig({ cwd: requestedCwd });
18285
18269
  let compactCompletedTurns = options.compactCompletedTurns ?? loadedConfig.config.settings.compactCompletedTurns;
18286
18270
  let completionChime = loadedConfig.config.settings.completionChime;
@@ -18297,7 +18281,7 @@ async function runApp(ctx, options = {}) {
18297
18281
  rows: () => terminal.rows,
18298
18282
  theme: startupTheme,
18299
18283
  tui,
18300
- version: readPackageVersion()
18284
+ version
18301
18285
  });
18302
18286
  const terminalCrashCleanup = installTerminalCrashCleanup({ terminal, tui });
18303
18287
  let terminalAppearance;
@@ -18420,7 +18404,6 @@ async function runApp(ctx, options = {}) {
18420
18404
  if (loadedConfig.config.defaults.serviceTier !== void 0) {
18421
18405
  agent.setServiceTier(loadedConfig.config.defaults.serviceTier);
18422
18406
  }
18423
- const version = readPackageVersion();
18424
18407
  const tuiInspectorUrl = getNodeInspectorUrl();
18425
18408
  const startupQuotas = await resolveStartupProviderQuota(() => agent.providerQuotas());
18426
18409
  const startupUsage = providerQuotaToStartupStatusUsage(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slopus/happy-terminal",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Happy Terminal, the embeddable terminal client for Happy Agent.",
5
5
  "keywords": [
6
6
  "coding-agent",
@@ -63,7 +63,7 @@
63
63
  "@sinclair/typebox": "^0.34.49",
64
64
  "@slopus/ghostty-wasm": "1.3.6",
65
65
  "@slopus/ghostty-web": "0.1.3",
66
- "@slopus/happy-agent-client": "0.0.13",
66
+ "@slopus/happy-agent-client": "0.0.20",
67
67
  "@slopus/happy-providers": "0.0.11",
68
68
  "@slopus/murmur": "0.4.4",
69
69
  "@steve.kite/stdlib": "0.0.12",
@@ -1 +0,0 @@
1
- export declare function renderHappyTerminalVersion(version: string, width: number): string[];