pi-crew 0.9.36 → 0.9.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.38] — cold context activation + UI polish (2026-07-15)
4
+
5
+ Fixes and UX polish following v0.9.37's cold-context groundwork:
6
+
7
+ ### Fixes
8
+
9
+ - **`parseRuntimeConfig()` default activation** — `src/config/config.ts`. The v0.9.37 cold-context feature shipped with `inheritContext: true` as default in `settings-overlay.ts` and `handle-settings.ts`, but `parseRuntimeConfig(undefined)` was returning `undefined` (not the default config), so the default never reached `buildParentContext()`. Now returns `{ inheritContext: true }` for missing/invalid input. Cold context inheritance now actually works on first subagent spawn.
10
+ - **Crew-vibes animation resilience** — `src/extension/crew-vibes/`. Three layered fixes:
11
+ - `figures.ts`: `crewFrames()` default flipped `"pua"` → `"braille"` (Windows Terminal can't reliably do PUA font fallback).
12
+ - `figures.ts`: added `hasCrewFontFile()` runtime check — falls back to braille when `assets/crew-vibes.ttf` is missing.
13
+ - `index.ts`: `safeUiCall()` wrapper around all pi UI calls (`setWorkingIndicator`, `setWorkingMessage`, `setFooter`, `requestRender`, `setSpeedStatus`). Any throw is logged + falls back to pi default. "A broken spinner is not worth a crashed pi."
14
+ - **Test fix**: `test/unit/resilient-config-parser.test.ts` updated to match the new `parseConfig({})` output (now includes the `runtime.inheritContext=true` default).
15
+
16
+ ### UX Polish
17
+
18
+ - **Animated spinner in agent loading display** — `src/ui/tool-renderers/index.ts`. The static `"◉ agent working..."` line is now replaced with an animated `spinnerFrame(agentId)` + parsed elapsed/tokens/tool from `formatCompactToolProgress`. Live feedback while subagents run.
19
+ - **Widget tok/s metric** — `src/ui/widget/widget-formatters.ts`. Agent stats line now shows throughput (`tok/s = tokens / elapsed_sec`) for both live-session and child-process paths. `TPS_METRIC_WIDTH=9` reserved for layout stability.
20
+
21
+ ### Cleanup
22
+
23
+ - **Dead streaming code removed** — `-643 lines net`. The v0.9.37 Phase 2 (subagent streaming via `crewEventBus`) added `ProgressTracker.handleWorkerEvent()`, `register.ts` onJsonEvent bridge, and inline progress enrichment. Audit confirmed `crewEventBus` had ZERO subscribers and `getWorkerProgress` polling never rendered in pi TUI. Removed all related code + `test/unit/progress-tracker-worker.test.ts`. Cold context (Phase 1) kept and verified working.
24
+
25
+ ## [0.9.37] — cold context + subagent streaming (2026-07-14)
26
+
27
+ Two performance improvements targeting perceived subagent slowness:
28
+
29
+ ### Phase 1: Cold Context (inheritContext default → true)
30
+
31
+ - **`inheritContext` default changed from `false` → `true`** — subagents now receive parent conversation context by default, eliminating redundant re-exploration of files the main session already processed. Configurable via `runtime.inheritContext`.
32
+ - **`buildParentContext()` token budget** — 12K char budget with most-recent-first retention prevents token bloat on large parent sessions.
33
+ - **Noisy content filtering** — skips file dumps (code blocks, `ls` output, import lists >1K chars) and truncates long assistant messages to 200 chars. Compaction summaries kept in full.
34
+ - **Tests**: `test/unit/build-parent-context.test.ts` (10 cases).
35
+
36
+ ### Phase 2: Subagent Streaming (child-process → widget)
37
+
38
+ - **`ProgressTracker.handleWorkerEvent()`** — bridges child-process JSON events (from `onJsonEvent`) into `crewEventBus`, enabling real-time widget display of tool calls, assistant text, and token usage for BOTH runtimes (previously only live-session had streaming).
39
+ - **`register.ts` onJsonEvent** — now forwards events to `globalProgressTracker` (was only feeding `overflowTracker`).
40
+ - **`subagent-tools.ts` inline progress** — enriched with real-time worker progress: ⚡ current tool, 📊 tokens, 💬 partial assistant text.
41
+ - **Throttled to 500ms per worker** to prevent widget flooding.
42
+ - **Tests**: `test/unit/progress-tracker-worker.test.ts` (8 cases).
43
+
3
44
  ## [0.9.36] — security audit round 1 (2026-07-14)
4
45
 
5
46
  Three security hardening fixes from the first formal security audit pass (run via `team_20260714045239_bb49d72211b966a2` + verified via `team_20260714062932_638dbb0310cc9f77`, 12/12 targeted tests pass, typecheck clean):
@@ -6242,7 +6242,7 @@
6242
6242
  "format": "esm"
6243
6243
  },
6244
6244
  "src/config/config.ts": {
6245
- "bytes": 49492,
6245
+ "bytes": 49536,
6246
6246
  "imports": [
6247
6247
  {
6248
6248
  "path": "node:fs",
@@ -7659,7 +7659,7 @@
7659
7659
  "format": "esm"
7660
7660
  },
7661
7661
  "src/ui/widget/widget-formatters.ts": {
7662
- "bytes": 7431,
7662
+ "bytes": 7962,
7663
7663
  "imports": [
7664
7664
  {
7665
7665
  "path": "src/runtime/usage-tracker.ts",
@@ -8410,7 +8410,7 @@
8410
8410
  "format": "esm"
8411
8411
  },
8412
8412
  "src/extension/team-tool/context.ts": {
8413
- "bytes": 3419,
8413
+ "bytes": 5582,
8414
8414
  "imports": [
8415
8415
  {
8416
8416
  "path": "src/extension/tool-result.ts",
@@ -8577,7 +8577,7 @@
8577
8577
  "format": "esm"
8578
8578
  },
8579
8579
  "src/extension/team-tool/handle-settings.ts": {
8580
- "bytes": 17990,
8580
+ "bytes": 17989,
8581
8581
  "imports": [
8582
8582
  {
8583
8583
  "path": "src/config/config.ts",
@@ -13586,7 +13586,7 @@
13586
13586
  "format": "esm"
13587
13587
  },
13588
13588
  "src/ui/tool-renderers/index.ts": {
13589
- "bytes": 28409,
13589
+ "bytes": 29280,
13590
13590
  "imports": [
13591
13591
  {
13592
13592
  "path": "@earendil-works/pi-tui",
@@ -13598,6 +13598,11 @@
13598
13598
  "kind": "import-statement",
13599
13599
  "original": "../../utils/visual.ts"
13600
13600
  },
13601
+ {
13602
+ "path": "src/ui/spinner.ts",
13603
+ "kind": "import-statement",
13604
+ "original": "../spinner.ts"
13605
+ },
13601
13606
  {
13602
13607
  "path": "src/ui/tool-render.ts",
13603
13608
  "kind": "import-statement",
@@ -15834,7 +15839,7 @@
15834
15839
  "format": "esm"
15835
15840
  },
15836
15841
  "src/extension/team-tool/run.ts": {
15837
- "bytes": 42552,
15842
+ "bytes": 42553,
15838
15843
  "imports": [
15839
15844
  {
15840
15845
  "path": "src/agents/discover-agents.ts",
@@ -16950,7 +16955,7 @@
16950
16955
  "format": "esm"
16951
16956
  },
16952
16957
  "src/ui/settings-overlay.ts": {
16953
- "bytes": 31026,
16958
+ "bytes": 31025,
16954
16959
  "imports": [
16955
16960
  {
16956
16961
  "path": "src/utils/visual.ts",
@@ -17200,7 +17205,7 @@
17200
17205
  "format": "esm"
17201
17206
  },
17202
17207
  "src/extension/crew-vibes/figures.ts": {
17203
- "bytes": 3603,
17208
+ "bytes": 4043,
17204
17209
  "imports": [
17205
17210
  {
17206
17211
  "path": "src/extension/crew-vibes/font-detect.ts",
@@ -17284,13 +17289,18 @@
17284
17289
  "format": "esm"
17285
17290
  },
17286
17291
  "src/extension/crew-vibes/index.ts": {
17287
- "bytes": 13575,
17292
+ "bytes": 14595,
17288
17293
  "imports": [
17289
17294
  {
17290
17295
  "path": "src/ui/pi-ui-compat.ts",
17291
17296
  "kind": "import-statement",
17292
17297
  "original": "../../ui/pi-ui-compat.ts"
17293
17298
  },
17299
+ {
17300
+ "path": "src/utils/internal-error.ts",
17301
+ "kind": "import-statement",
17302
+ "original": "../../utils/internal-error.ts"
17303
+ },
17294
17304
  {
17295
17305
  "path": "src/extension/crew-vibes/config.ts",
17296
17306
  "kind": "import-statement",
@@ -18415,7 +18425,7 @@
18415
18425
  "imports": [],
18416
18426
  "exports": [],
18417
18427
  "inputs": {},
18418
- "bytes": 6697128
18428
+ "bytes": 6704580
18419
18429
  },
18420
18430
  "dist/index.mjs": {
18421
18431
  "imports": [
@@ -20643,7 +20653,7 @@
20643
20653
  "bytesInOutput": 1330
20644
20654
  },
20645
20655
  "src/config/config.ts": {
20646
- "bytesInOutput": 39089
20656
+ "bytesInOutput": 39115
20647
20657
  },
20648
20658
  "src/errors.ts": {
20649
20659
  "bytesInOutput": 8094
@@ -20847,7 +20857,7 @@
20847
20857
  "bytesInOutput": 793
20848
20858
  },
20849
20859
  "src/ui/widget/widget-formatters.ts": {
20850
- "bytesInOutput": 5162
20860
+ "bytesInOutput": 5712
20851
20861
  },
20852
20862
  "src/ui/powerbar-publisher.ts": {
20853
20863
  "bytesInOutput": 9964
@@ -20931,7 +20941,7 @@
20931
20941
  "bytesInOutput": 1541
20932
20942
  },
20933
20943
  "src/extension/team-tool/context.ts": {
20934
- "bytesInOutput": 2198
20944
+ "bytesInOutput": 3237
20935
20945
  },
20936
20946
  "src/extension/team-tool/intent-policy.ts": {
20937
20947
  "bytesInOutput": 1245
@@ -20952,7 +20962,7 @@
20952
20962
  "bytesInOutput": 1876
20953
20963
  },
20954
20964
  "src/extension/team-tool/handle-settings.ts": {
20955
- "bytesInOutput": 16052
20965
+ "bytesInOutput": 16051
20956
20966
  },
20957
20967
  "src/workflows/validate-workflow.ts": {
20958
20968
  "bytesInOutput": 1927
@@ -21612,7 +21622,7 @@
21612
21622
  "bytesInOutput": 1873
21613
21623
  },
21614
21624
  "src/ui/tool-renderers/index.ts": {
21615
- "bytesInOutput": 20722
21625
+ "bytesInOutput": 21497
21616
21626
  },
21617
21627
  "src/ui/tool-progress-formatter.ts": {
21618
21628
  "bytesInOutput": 2691
@@ -21960,7 +21970,7 @@
21960
21970
  "bytesInOutput": 15659
21961
21971
  },
21962
21972
  "src/ui/settings-overlay.ts": {
21963
- "bytesInOutput": 30589
21973
+ "bytesInOutput": 30588
21964
21974
  },
21965
21975
  "src/extension/registration/commands.ts": {
21966
21976
  "bytesInOutput": 40488
@@ -22068,7 +22078,7 @@
22068
22078
  "bytesInOutput": 1204
22069
22079
  },
22070
22080
  "src/extension/crew-vibes/index.ts": {
22071
- "bytesInOutput": 10897
22081
+ "bytesInOutput": 11458
22072
22082
  },
22073
22083
  "src/extension/crew-vibes/config.ts": {
22074
22084
  "bytesInOutput": 5221
@@ -22077,7 +22087,7 @@
22077
22087
  "bytesInOutput": 1467
22078
22088
  },
22079
22089
  "src/extension/crew-vibes/figures.ts": {
22080
- "bytesInOutput": 1218
22090
+ "bytesInOutput": 1246
22081
22091
  },
22082
22092
  "src/extension/crew-vibes/footer.ts": {
22083
22093
  "bytesInOutput": 8220
@@ -22125,7 +22135,7 @@
22125
22135
  "bytesInOutput": 81
22126
22136
  }
22127
22137
  },
22128
- "bytes": 3172923
22138
+ "bytes": 3175900
22129
22139
  }
22130
22140
  }
22131
22141
  }
package/dist/index.mjs CHANGED
@@ -10277,7 +10277,7 @@ function parseIsolationPolicy(value) {
10277
10277
  }
10278
10278
  function parseRuntimeConfig(value) {
10279
10279
  const obj = asRecord(value);
10280
- if (!obj) return void 0;
10280
+ if (!obj) return { inheritContext: true };
10281
10281
  const runtime = {
10282
10282
  mode: parseWithSchema(
10283
10283
  Type.Union([Type.Literal("auto"), Type.Literal("scaffold"), Type.Literal("child-process"), Type.Literal("live-session")]),
@@ -10287,7 +10287,7 @@ function parseRuntimeConfig(value) {
10287
10287
  allowChildProcessFallback: parseWithSchema(Type.Boolean(), obj.allowChildProcessFallback),
10288
10288
  maxTurns: parsePositiveInteger(obj.maxTurns, LIMIT_CEILINGS.runtimeMaxTurns),
10289
10289
  graceTurns: parsePositiveInteger(obj.graceTurns, LIMIT_CEILINGS.runtimeGraceTurns),
10290
- inheritContext: parseWithSchema(Type.Boolean(), obj.inheritContext),
10290
+ inheritContext: parseWithSchema(Type.Boolean(), obj.inheritContext) ?? true,
10291
10291
  promptMode: parseWithSchema(Type.Union([Type.Literal("replace"), Type.Literal("append")]), obj.promptMode),
10292
10292
  groupJoin: parseWithSchema(Type.Union([Type.Literal("off"), Type.Literal("group"), Type.Literal("smart")]), obj.groupJoin),
10293
10293
  groupJoinAckTimeoutMs: parsePositiveInteger(obj.groupJoinAckTimeoutMs, 864e5),
@@ -20964,10 +20964,19 @@ function agentStats(agent, liveHandle) {
20964
20964
  } catch {
20965
20965
  }
20966
20966
  const ms = computeLiveDurationMs(act);
20967
+ if (total > 0 && ms > 1e3) {
20968
+ const tps = Math.round(total / (ms / 1e3));
20969
+ if (tps > 0) parts.push(alignMetric(`${formatTokensCompact(tps)}/s`, TPS_METRIC_WIDTH));
20970
+ }
20967
20971
  parts.push(alignMetric(`${(ms / 1e3).toFixed(1)}s`, DURATION_METRIC_WIDTH));
20968
20972
  } else {
20969
20973
  if (agent.toolUses) parts.push(alignMetric(`${agent.toolUses} tools`, TOOLS_METRIC_WIDTH));
20970
20974
  if (agent.progress?.tokens) parts.push(alignMetric(formatTokensCompact(agent.progress.tokens), TOKENS_METRIC_WIDTH));
20975
+ const ageMs = agent.startedAt ? Math.max(0, Date.now() - new Date(agent.startedAt).getTime()) : 0;
20976
+ if (agent.progress?.tokens && ageMs > 1e3) {
20977
+ const tps = Math.round(agent.progress.tokens / (ageMs / 1e3));
20978
+ if (tps > 0) parts.push(alignMetric(`${formatTokensCompact(tps)}/s`, TPS_METRIC_WIDTH));
20979
+ }
20971
20980
  const age = elapsed(agent.completedAt ?? agent.startedAt);
20972
20981
  if (age) parts.push(alignMetric(age, DURATION_METRIC_WIDTH));
20973
20982
  }
@@ -20980,7 +20989,7 @@ function notificationBadge(count2, env = process.env) {
20980
20989
  const label = count2 > NOTIFICATION_BADGE_CAP ? `${NOTIFICATION_BADGE_CAP}+ alerts` : `${count2} alerts`;
20981
20990
  return supportsEmoji ? ` \xB7 ${label}` : ` [${label}]`;
20982
20991
  }
20983
- var TOOLS_METRIC_WIDTH, TOKENS_METRIC_WIDTH, CTX_METRIC_WIDTH, DURATION_METRIC_WIDTH, TOOL_LABELS, TOOL_ICONS, NOTIFICATION_BADGE_CAP;
20992
+ var TOOLS_METRIC_WIDTH, TOKENS_METRIC_WIDTH, TPS_METRIC_WIDTH, CTX_METRIC_WIDTH, DURATION_METRIC_WIDTH, TOOL_LABELS, TOOL_ICONS, NOTIFICATION_BADGE_CAP;
20984
20993
  var init_widget_formatters = __esm({
20985
20994
  "src/ui/widget/widget-formatters.ts"() {
20986
20995
  "use strict";
@@ -20989,6 +20998,7 @@ var init_widget_formatters = __esm({
20989
20998
  init_live_duration();
20990
20999
  TOOLS_METRIC_WIDTH = 8;
20991
21000
  TOKENS_METRIC_WIDTH = 10;
21001
+ TPS_METRIC_WIDTH = 9;
20992
21002
  CTX_METRIC_WIDTH = 7;
20993
21003
  DURATION_METRIC_WIDTH = 6;
20994
21004
  TOOL_LABELS = {
@@ -24757,6 +24767,7 @@ var init_workflow_serializer = __esm({
24757
24767
  // src/extension/team-tool/context.ts
24758
24768
  var context_exports = {};
24759
24769
  __export(context_exports, {
24770
+ MAX_PARENT_CONTEXT_CHARS: () => MAX_PARENT_CONTEXT_CHARS,
24760
24771
  buildParentContext: () => buildParentContext,
24761
24772
  configRecord: () => configRecord,
24762
24773
  formatScoped: () => formatScoped,
@@ -24780,6 +24791,10 @@ function extractTextContent(content) {
24780
24791
  (part) => part && typeof part === "object" && !Array.isArray(part) && typeof part.text === "string" ? part.text : ""
24781
24792
  ).filter(Boolean).join("\n");
24782
24793
  }
24794
+ function isNoisyContent(text) {
24795
+ if (text.length < NOISY_THRESHOLD) return false;
24796
+ return NOISY_PREFIXES.some((prefix) => text.startsWith(prefix));
24797
+ }
24783
24798
  function buildParentContext(ctx) {
24784
24799
  const branch = ctx.sessionManager?.getBranch?.();
24785
24800
  if (!Array.isArray(branch) || branch.length === 0) return void 0;
@@ -24787,28 +24802,50 @@ function buildParentContext(ctx) {
24787
24802
  for (const entry of branch.slice(-20)) {
24788
24803
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
24789
24804
  const record = entry;
24790
- if (record.type === "compaction" && typeof record.summary === "string") parts.push(`[Summary]: ${record.summary}`);
24805
+ if (record.type === "compaction" && typeof record.summary === "string") {
24806
+ parts.push(`[Summary]: ${record.summary}`);
24807
+ continue;
24808
+ }
24791
24809
  const message = record.message && typeof record.message === "object" && !Array.isArray(record.message) ? record.message : void 0;
24792
24810
  if (!message || message.role !== "user" && message.role !== "assistant") continue;
24793
- const text = extractTextContent(message.content).trim();
24794
- if (text) parts.push(`[${message.role === "user" ? "User" : "Assistant"}]: ${text}`);
24811
+ let text = extractTextContent(message.content).trim();
24812
+ if (!text) continue;
24813
+ if (isNoisyContent(text)) continue;
24814
+ if (message.role === "assistant" && text.length > MAX_ASSISTANT_MSG_CHARS) {
24815
+ text = `${text.slice(0, TRUNCATED_ASSISTANT_CHARS)}\u2026`;
24816
+ }
24817
+ parts.push(`[${message.role === "user" ? "User" : "Assistant"}]: ${text}`);
24795
24818
  }
24796
24819
  if (!parts.length) return void 0;
24820
+ let totalChars = 0;
24821
+ const budgeted = [];
24822
+ for (const part of [...parts].reverse()) {
24823
+ if (totalChars + part.length > MAX_PARENT_CONTEXT_CHARS) break;
24824
+ budgeted.unshift(part);
24825
+ totalChars += part.length;
24826
+ }
24827
+ if (!budgeted.length) return void 0;
24797
24828
  return [
24798
24829
  `# Parent Conversation Context`,
24799
24830
  "The following context was inherited from the parent Pi session. Treat it as reference-only.",
24800
24831
  "",
24801
- parts.join("\n\n")
24832
+ budgeted.join("\n\n")
24802
24833
  ].join("\n");
24803
24834
  }
24804
24835
  function configRecord(config) {
24805
24836
  if (!config || typeof config !== "object" || Array.isArray(config)) return {};
24806
24837
  return config;
24807
24838
  }
24839
+ var MAX_PARENT_CONTEXT_CHARS, MAX_ASSISTANT_MSG_CHARS, TRUNCATED_ASSISTANT_CHARS, NOISY_THRESHOLD, NOISY_PREFIXES;
24808
24840
  var init_context = __esm({
24809
24841
  "src/extension/team-tool/context.ts"() {
24810
24842
  "use strict";
24811
24843
  init_tool_result();
24844
+ MAX_PARENT_CONTEXT_CHARS = 12e3;
24845
+ MAX_ASSISTANT_MSG_CHARS = 500;
24846
+ TRUNCATED_ASSISTANT_CHARS = 200;
24847
+ NOISY_THRESHOLD = 1e3;
24848
+ NOISY_PREFIXES = ["```", "total ", "drwx", "-rw", "import ", "export "];
24812
24849
  }
24813
24850
  });
24814
24851
 
@@ -26191,7 +26228,7 @@ var init_handle_settings = __esm({
26191
26228
  "runtime.mode": "auto",
26192
26229
  "runtime.maxTurns": 1e4,
26193
26230
  "runtime.graceTurns": 5,
26194
- "runtime.inheritContext": false,
26231
+ "runtime.inheritContext": true,
26195
26232
  "runtime.promptMode": "replace",
26196
26233
  "runtime.completionMutationGuard": "warn",
26197
26234
  "runtime.isolationPolicy": void 0,
@@ -52022,9 +52059,21 @@ function renderAgentResult(result4, options, theme, ctx) {
52022
52059
  const contentLines = [];
52023
52060
  const isPartial = options?.isPartial === true;
52024
52061
  if (isPartial && !ctx.expanded) {
52025
- const spinner = theme.fg("warning", "\u25C9");
52026
- const label = theme.fg("muted", "agent working...");
52027
- contentLines.push(padVisual(` ${spinner} ${label}`, innerW));
52062
+ const spinner = theme.fg("accent", spinnerFrame(d.agentId));
52063
+ const parts = [theme.fg("muted", "working")];
52064
+ const progressText = extractContentText(result4?.content);
52065
+ if (progressText) {
52066
+ const elapsedMatch = progressText.match(/elapsed=(\d+)s/);
52067
+ const tokenMatch = progressText.match(/tokens=(\d+)/);
52068
+ const toolMatch = progressText.match(/tool:\s+(\S+)/);
52069
+ const elapsedSec = elapsedMatch ? parseInt(elapsedMatch[1], 10) : 0;
52070
+ const tokens = tokenMatch ? parseInt(tokenMatch[1], 10) : 0;
52071
+ const tps = elapsedSec > 0 ? Math.round(tokens / elapsedSec) : 0;
52072
+ if (elapsedSec > 0) parts.push(theme.fg("dim", formatDuration2(elapsedSec * 1e3)));
52073
+ if (tps > 0) parts.push(theme.fg("dim", `${formatTokens2(tps)} tok/s`));
52074
+ if (toolMatch) parts.push(theme.fg("dim", `\xB7 ${toolMatch[1]}`));
52075
+ }
52076
+ contentLines.push(padVisual(` ${spinner} ${parts.join(" ")}`, innerW));
52028
52077
  return buildFrameBottom(contentLines, w, theme, "borderAccent", "borderAccent");
52029
52078
  }
52030
52079
  if (!results?.length && !d.agentId) {
@@ -52265,6 +52314,7 @@ var init_tool_renderers = __esm({
52265
52314
  "src/ui/tool-renderers/index.ts"() {
52266
52315
  "use strict";
52267
52316
  init_visual();
52317
+ init_spinner();
52268
52318
  init_tool_render();
52269
52319
  init_brief_mode();
52270
52320
  init_card_colors();
@@ -75794,7 +75844,7 @@ var init_settings_overlay = __esm({
75794
75844
  "runtime.mode": "auto",
75795
75845
  "runtime.maxTurns": 1e4,
75796
75846
  "runtime.graceTurns": 5,
75797
- "runtime.inheritContext": false,
75847
+ "runtime.inheritContext": true,
75798
75848
  "runtime.promptMode": "replace",
75799
75849
  "runtime.completionMutationGuard": "warn",
75800
75850
  "runtime.isolationPolicy": void 0,
@@ -82162,6 +82212,7 @@ var CREW_SHORTCUT_KEYS = CREW_SHORTCUTS.map((s) => s.key);
82162
82212
 
82163
82213
  // src/extension/crew-vibes/index.ts
82164
82214
  init_pi_ui_compat();
82215
+ init_internal_error();
82165
82216
 
82166
82217
  // src/extension/crew-vibes/config.ts
82167
82218
  import { existsSync as existsSync77, mkdirSync as mkdirSync43, readFileSync as readFileSync75, writeFileSync as writeFileSync33 } from "node:fs";
@@ -82383,8 +82434,8 @@ var PUA_CREW_FRAMES = [
82383
82434
  "\uE70E ",
82384
82435
  "\uE70F "
82385
82436
  ];
82386
- function crewFrames(style = "pua") {
82387
- if (style === "pua" && isWebTerminal()) return BRAILLE_FRAMES;
82437
+ function crewFrames(style = "braille") {
82438
+ if (style === "pua" && (isWebTerminal() || !hasCrewFontFile())) return BRAILLE_FRAMES;
82388
82439
  return style === "pua" ? PUA_CREW_FRAMES : BRAILLE_FRAMES;
82389
82440
  }
82390
82441
  function intervalForSpeed(config, speed) {
@@ -83180,6 +83231,13 @@ var SpeedAnimator = class {
83180
83231
  };
83181
83232
 
83182
83233
  // src/extension/crew-vibes/index.ts
83234
+ function safeUiCall(scope, fn) {
83235
+ try {
83236
+ fn();
83237
+ } catch (error) {
83238
+ logInternalError(`crew-vibes.${scope}`, error, void 0, "warn");
83239
+ }
83240
+ }
83183
83241
  function isAssistantMessage(message) {
83184
83242
  return typeof message === "object" && message !== null && message.role === "assistant";
83185
83243
  }
@@ -83222,39 +83280,44 @@ function registerCrewVibes(pi) {
83222
83280
  function installFooter(ctx) {
83223
83281
  if (!ctx?.hasUI) return;
83224
83282
  if (!metersActive()) {
83225
- setFooter(ctx, void 0);
83283
+ safeUiCall("clear-footer", () => setFooter(ctx, void 0));
83226
83284
  return;
83227
83285
  }
83228
- setFooter(ctx, (tui, theme, footerData) => createCrewVibesFooter({ tui, theme, footerData, ctx, source: footerSource }));
83229
- requestRender(ctx);
83286
+ safeUiCall("install-footer", () => {
83287
+ setFooter(ctx, (tui, theme, footerData) => createCrewVibesFooter({ tui, theme, footerData, ctx, source: footerSource }));
83288
+ requestRender(ctx);
83289
+ });
83230
83290
  }
83231
83291
  function refreshFooter(ctx) {
83232
- if (ctx?.hasUI) requestRender(ctx);
83292
+ if (ctx?.hasUI) safeUiCall("refresh-footer", () => requestRender(ctx));
83233
83293
  }
83234
83294
  function publishSpeedFooter(ctx, speed = footerAnimator.value()) {
83235
83295
  if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
83236
- setSpeedStatus(ctx, config, void 0);
83296
+ safeUiCall("clear-speed-status", () => setSpeedStatus(ctx, config, void 0));
83237
83297
  return;
83238
83298
  }
83239
- setSpeedStatus(ctx, config, renderSpeedFooter(themeOf(ctx), config.speed, speed));
83299
+ safeUiCall("publish-speed-status", () => setSpeedStatus(ctx, config, renderSpeedFooter(themeOf(ctx), config.speed, speed)));
83240
83300
  }
83241
83301
  function applyIndicator(ctx, speed, force = false) {
83242
83302
  if (!ctx.hasUI || !ctx.ui.setWorkingIndicator) return;
83243
83303
  if (!config.enabled || !config.speed.enabled || !config.speed.indicator) {
83244
- ctx.ui.setWorkingIndicator();
83304
+ safeUiCall("reset-indicator", () => ctx.ui.setWorkingIndicator?.());
83245
83305
  return;
83246
83306
  }
83247
83307
  const next = intervalForSpeed(config.speed, speed);
83248
83308
  if (!force && Math.abs(next - currentIntervalMs) < 10) return;
83249
- ctx.ui.setWorkingIndicator({
83250
- frames: crewIndicatorFrames(themeOf(ctx)),
83251
- intervalMs: next
83252
- });
83309
+ safeUiCall(
83310
+ "set-indicator",
83311
+ () => ctx.ui.setWorkingIndicator?.({
83312
+ frames: crewIndicatorFrames(themeOf(ctx)),
83313
+ intervalMs: next
83314
+ })
83315
+ );
83253
83316
  currentIntervalMs = next;
83254
83317
  }
83255
83318
  function renderWorking(ctx, speed) {
83256
83319
  if (!config.enabled || !config.speed.enabled || !ctx.hasUI) return;
83257
- ctx.ui.setWorkingMessage(renderWorkingMessage(themeOf(ctx), config.speed, speed));
83320
+ safeUiCall("set-working-message", () => ctx.ui.setWorkingMessage?.(renderWorkingMessage(themeOf(ctx), config.speed, speed)));
83258
83321
  }
83259
83322
  function stopLiveTimer() {
83260
83323
  if (!liveTimer) return;
@@ -83425,7 +83488,7 @@ function registerCrewVibes(pi) {
83425
83488
  stopLiveTimer();
83426
83489
  if (ctx && config.enabled && ctx.hasUI) {
83427
83490
  applyIndicator(ctx, speedTracker.lastTokS);
83428
- ctx.ui.setWorkingMessage();
83491
+ safeUiCall("clear-working-message", () => ctx.ui.setWorkingMessage?.());
83429
83492
  }
83430
83493
  });
83431
83494
  pi.on("model_select", (event, ctx) => {