pi-crew 0.9.37 → 0.9.39

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/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 = {
@@ -52049,9 +52059,21 @@ function renderAgentResult(result4, options, theme, ctx) {
52049
52059
  const contentLines = [];
52050
52060
  const isPartial = options?.isPartial === true;
52051
52061
  if (isPartial && !ctx.expanded) {
52052
- const spinner = theme.fg("warning", "\u25C9");
52053
- const label = theme.fg("muted", "agent working...");
52054
- 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));
52055
52077
  return buildFrameBottom(contentLines, w, theme, "borderAccent", "borderAccent");
52056
52078
  }
52057
52079
  if (!results?.length && !d.agentId) {
@@ -52292,6 +52314,7 @@ var init_tool_renderers = __esm({
52292
52314
  "src/ui/tool-renderers/index.ts"() {
52293
52315
  "use strict";
52294
52316
  init_visual();
52317
+ init_spinner();
52295
52318
  init_tool_render();
52296
52319
  init_brief_mode();
52297
52320
  init_card_colors();
@@ -82189,6 +82212,7 @@ var CREW_SHORTCUT_KEYS = CREW_SHORTCUTS.map((s) => s.key);
82189
82212
 
82190
82213
  // src/extension/crew-vibes/index.ts
82191
82214
  init_pi_ui_compat();
82215
+ init_internal_error();
82192
82216
 
82193
82217
  // src/extension/crew-vibes/config.ts
82194
82218
  import { existsSync as existsSync77, mkdirSync as mkdirSync43, readFileSync as readFileSync75, writeFileSync as writeFileSync33 } from "node:fs";
@@ -82410,8 +82434,8 @@ var PUA_CREW_FRAMES = [
82410
82434
  "\uE70E ",
82411
82435
  "\uE70F "
82412
82436
  ];
82413
- function crewFrames(style = "pua") {
82414
- if (style === "pua" && isWebTerminal()) return BRAILLE_FRAMES;
82437
+ function crewFrames(style = "braille") {
82438
+ if (style === "pua" && (isWebTerminal() || !hasCrewFontFile())) return BRAILLE_FRAMES;
82415
82439
  return style === "pua" ? PUA_CREW_FRAMES : BRAILLE_FRAMES;
82416
82440
  }
82417
82441
  function intervalForSpeed(config, speed) {
@@ -83207,6 +83231,13 @@ var SpeedAnimator = class {
83207
83231
  };
83208
83232
 
83209
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
+ }
83210
83241
  function isAssistantMessage(message) {
83211
83242
  return typeof message === "object" && message !== null && message.role === "assistant";
83212
83243
  }
@@ -83249,39 +83280,44 @@ function registerCrewVibes(pi) {
83249
83280
  function installFooter(ctx) {
83250
83281
  if (!ctx?.hasUI) return;
83251
83282
  if (!metersActive()) {
83252
- setFooter(ctx, void 0);
83283
+ safeUiCall("clear-footer", () => setFooter(ctx, void 0));
83253
83284
  return;
83254
83285
  }
83255
- setFooter(ctx, (tui, theme, footerData) => createCrewVibesFooter({ tui, theme, footerData, ctx, source: footerSource }));
83256
- requestRender(ctx);
83286
+ safeUiCall("install-footer", () => {
83287
+ setFooter(ctx, (tui, theme, footerData) => createCrewVibesFooter({ tui, theme, footerData, ctx, source: footerSource }));
83288
+ requestRender(ctx);
83289
+ });
83257
83290
  }
83258
83291
  function refreshFooter(ctx) {
83259
- if (ctx?.hasUI) requestRender(ctx);
83292
+ if (ctx?.hasUI) safeUiCall("refresh-footer", () => requestRender(ctx));
83260
83293
  }
83261
83294
  function publishSpeedFooter(ctx, speed = footerAnimator.value()) {
83262
83295
  if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
83263
- setSpeedStatus(ctx, config, void 0);
83296
+ safeUiCall("clear-speed-status", () => setSpeedStatus(ctx, config, void 0));
83264
83297
  return;
83265
83298
  }
83266
- setSpeedStatus(ctx, config, renderSpeedFooter(themeOf(ctx), config.speed, speed));
83299
+ safeUiCall("publish-speed-status", () => setSpeedStatus(ctx, config, renderSpeedFooter(themeOf(ctx), config.speed, speed)));
83267
83300
  }
83268
83301
  function applyIndicator(ctx, speed, force = false) {
83269
83302
  if (!ctx.hasUI || !ctx.ui.setWorkingIndicator) return;
83270
83303
  if (!config.enabled || !config.speed.enabled || !config.speed.indicator) {
83271
- ctx.ui.setWorkingIndicator();
83304
+ safeUiCall("reset-indicator", () => ctx.ui.setWorkingIndicator?.());
83272
83305
  return;
83273
83306
  }
83274
83307
  const next = intervalForSpeed(config.speed, speed);
83275
83308
  if (!force && Math.abs(next - currentIntervalMs) < 10) return;
83276
- ctx.ui.setWorkingIndicator({
83277
- frames: crewIndicatorFrames(themeOf(ctx)),
83278
- intervalMs: next
83279
- });
83309
+ safeUiCall(
83310
+ "set-indicator",
83311
+ () => ctx.ui.setWorkingIndicator?.({
83312
+ frames: crewIndicatorFrames(themeOf(ctx)),
83313
+ intervalMs: next
83314
+ })
83315
+ );
83280
83316
  currentIntervalMs = next;
83281
83317
  }
83282
83318
  function renderWorking(ctx, speed) {
83283
83319
  if (!config.enabled || !config.speed.enabled || !ctx.hasUI) return;
83284
- ctx.ui.setWorkingMessage(renderWorkingMessage(themeOf(ctx), config.speed, speed));
83320
+ safeUiCall("set-working-message", () => ctx.ui.setWorkingMessage?.(renderWorkingMessage(themeOf(ctx), config.speed, speed)));
83285
83321
  }
83286
83322
  function stopLiveTimer() {
83287
83323
  if (!liveTimer) return;
@@ -83452,7 +83488,7 @@ function registerCrewVibes(pi) {
83452
83488
  stopLiveTimer();
83453
83489
  if (ctx && config.enabled && ctx.hasUI) {
83454
83490
  applyIndicator(ctx, speedTracker.lastTokS);
83455
- ctx.ui.setWorkingMessage();
83491
+ safeUiCall("clear-working-message", () => ctx.ui.setWorkingMessage?.());
83456
83492
  }
83457
83493
  });
83458
83494
  pi.on("model_select", (event, ctx) => {
@@ -84118,259 +84154,6 @@ function initI18n(pi) {
84118
84154
 
84119
84155
  // src/extension/registration/subagent-tools.ts
84120
84156
  init_crew_agent_records();
84121
-
84122
- // src/observability/event-bus.ts
84123
- init_internal_error();
84124
- var EventBus = class _EventBus {
84125
- listeners = /* @__PURE__ */ new Map();
84126
- static _instance;
84127
- static getInstance() {
84128
- if (!_EventBus._instance) {
84129
- _EventBus._instance = new _EventBus();
84130
- }
84131
- return _EventBus._instance;
84132
- }
84133
- /**
84134
- * Dispose of the EventBus instance and clear all listeners.
84135
- * Resets the singleton so a new instance can be created.
84136
- */
84137
- dispose() {
84138
- this.listeners.clear();
84139
- _EventBus._instance = void 0;
84140
- }
84141
- emit(event) {
84142
- const listeners2 = this.listeners.get(event.type);
84143
- if (listeners2) {
84144
- for (const listener of listeners2) {
84145
- try {
84146
- listener(event);
84147
- } catch (e) {
84148
- logInternalError("event-bus.listener", e, `type=${event.type} runId=${event.runId}`);
84149
- }
84150
- }
84151
- }
84152
- }
84153
- on(type, listener) {
84154
- if (!this.listeners.has(type)) {
84155
- this.listeners.set(type, /* @__PURE__ */ new Set());
84156
- }
84157
- this.listeners.get(type).add(listener);
84158
- return () => {
84159
- this.listeners.get(type)?.delete(listener);
84160
- };
84161
- }
84162
- off(type, listener) {
84163
- this.listeners.get(type)?.delete(listener);
84164
- }
84165
- };
84166
- var crewEventBus = EventBus.getInstance();
84167
-
84168
- // src/runtime/progress-tracker.ts
84169
- var ProgressTracker = class _ProgressTracker {
84170
- sessions = /* @__PURE__ */ new Map();
84171
- track(session, agentId, runId) {
84172
- if (this.sessions.has(agentId)) {
84173
- return this.sessions.get(agentId).progress;
84174
- }
84175
- const progress = {
84176
- toolCalls: 0,
84177
- currentTool: null,
84178
- toolStartTime: null,
84179
- errors: [],
84180
- turns: 0,
84181
- tokens: { input: 0, output: 0 },
84182
- status: "running"
84183
- };
84184
- const unsubscribe = session.subscribe((event) => {
84185
- this.handleEvent(event, progress, agentId, runId);
84186
- });
84187
- this.sessions.set(agentId, { unsubscribe, progress });
84188
- return progress;
84189
- }
84190
- handleEvent(event, progress, agentId, runId) {
84191
- switch (event.type) {
84192
- case "tool_execution_start":
84193
- progress.toolCalls++;
84194
- progress.currentTool = event.toolName;
84195
- progress.toolStartTime = Date.now();
84196
- crewEventBus.emit({
84197
- type: "agent:progress",
84198
- runId,
84199
- agentId,
84200
- payload: { ...progress },
84201
- timestamp: Date.now()
84202
- });
84203
- break;
84204
- case "tool_execution_end":
84205
- progress.currentTool = null;
84206
- progress.toolStartTime = null;
84207
- if (event.isError) {
84208
- progress.errors.push(String(event.result ?? "Unknown error"));
84209
- crewEventBus.emit({
84210
- type: "agent:error",
84211
- runId,
84212
- agentId,
84213
- payload: String(event.result ?? "Unknown error"),
84214
- timestamp: Date.now()
84215
- });
84216
- }
84217
- crewEventBus.emit({
84218
- type: "agent:progress",
84219
- runId,
84220
- agentId,
84221
- payload: { ...progress },
84222
- timestamp: Date.now()
84223
- });
84224
- break;
84225
- case "turn_start":
84226
- progress.turns++;
84227
- break;
84228
- case "agent_end":
84229
- progress.status = "completed";
84230
- crewEventBus.emit({
84231
- type: "agent:complete",
84232
- runId,
84233
- agentId,
84234
- payload: { ...progress },
84235
- timestamp: Date.now()
84236
- });
84237
- break;
84238
- case "agent_start":
84239
- progress.status = "running";
84240
- break;
84241
- }
84242
- }
84243
- untrack(agentId) {
84244
- const tracked = this.sessions.get(agentId);
84245
- if (tracked) {
84246
- tracked.unsubscribe();
84247
- this.sessions.delete(agentId);
84248
- }
84249
- }
84250
- getProgress(agentId) {
84251
- return this.sessions.get(agentId)?.progress;
84252
- }
84253
- // ── Child-process worker event bridge ──────────────────────────────────
84254
- //
84255
- // For child-process runtime, events arrive as raw JSON from the child pi
84256
- // process's stdout (via onJsonEvent). These methods bridge those events into
84257
- // the same crewEventBus stream that live-session uses, so the widget shows
84258
- // real-time tool calls and assistant text for BOTH runtimes.
84259
- workerProgress = /* @__PURE__ */ new Map();
84260
- /** Throttle: don't emit more than 1 progress event per 500ms per worker. */
84261
- lastEmitTs = /* @__PURE__ */ new Map();
84262
- static EMIT_THROTTLE_MS = 500;
84263
- /**
84264
- * Handle a raw child-process JSON event (from onJsonEvent callback).
84265
- * Processes tool_execution_start/end, agent_start/end, and assistant text.
84266
- */
84267
- handleWorkerEvent(taskId, runId, event) {
84268
- let progress = this.workerProgress.get(taskId);
84269
- if (!progress) {
84270
- progress = {
84271
- toolCalls: 0,
84272
- currentTool: null,
84273
- toolStartTime: null,
84274
- errors: [],
84275
- turns: 0,
84276
- tokens: { input: 0, output: 0 },
84277
- status: "running"
84278
- };
84279
- this.workerProgress.set(taskId, progress);
84280
- }
84281
- const eventType = typeof event.type === "string" ? event.type : void 0;
84282
- switch (eventType) {
84283
- case "tool_execution_start": {
84284
- progress.toolCalls++;
84285
- progress.currentTool = typeof event.toolName === "string" ? event.toolName : "unknown";
84286
- progress.toolStartTime = Date.now();
84287
- this.emitThrottled(taskId, runId, progress);
84288
- break;
84289
- }
84290
- case "tool_execution_end": {
84291
- progress.currentTool = null;
84292
- progress.toolStartTime = null;
84293
- if (event.isError) {
84294
- progress.errors.push(String(event.result ?? "Unknown error"));
84295
- crewEventBus.emit({
84296
- type: "agent:error",
84297
- runId,
84298
- agentId: taskId,
84299
- payload: String(event.result ?? "Unknown error"),
84300
- timestamp: Date.now()
84301
- });
84302
- }
84303
- this.emitThrottled(taskId, runId, progress);
84304
- break;
84305
- }
84306
- case "turn_start":
84307
- case "turn_end":
84308
- progress.turns++;
84309
- break;
84310
- case "agent_start":
84311
- progress.status = "running";
84312
- break;
84313
- case "agent_end":
84314
- case "agent_settled":
84315
- progress.status = "completed";
84316
- this.emitThrottled(taskId, runId, progress);
84317
- break;
84318
- case "message":
84319
- case "message_end": {
84320
- const message = event.message;
84321
- if (message?.role === "assistant") {
84322
- const text = extractWorkerText(message.content);
84323
- if (text) {
84324
- progress.partialText = text.slice(-2e3);
84325
- this.emitThrottled(taskId, runId, progress);
84326
- }
84327
- }
84328
- if (eventType === "message_end" && event.usage && typeof event.usage === "object") {
84329
- const usage = event.usage;
84330
- if (typeof usage.input === "number") progress.tokens.input += usage.input;
84331
- if (typeof usage.output === "number") progress.tokens.output += usage.output;
84332
- }
84333
- break;
84334
- }
84335
- }
84336
- }
84337
- /** Get the accumulated progress for a child-process worker task. */
84338
- getWorkerProgress(taskId) {
84339
- return this.workerProgress.get(taskId);
84340
- }
84341
- /** Remove a worker from tracking after completion. */
84342
- untrackWorker(taskId) {
84343
- this.workerProgress.delete(taskId);
84344
- this.lastEmitTs.delete(taskId);
84345
- }
84346
- /**
84347
- * Emit a progress event to crewEventBus, throttled to avoid flooding
84348
- * the widget with re-renders (max 1 event per EMIT_THROTTLE_MS per worker).
84349
- */
84350
- emitThrottled(taskId, runId, progress) {
84351
- const now = Date.now();
84352
- const last = this.lastEmitTs.get(taskId) ?? 0;
84353
- if (now - last < _ProgressTracker.EMIT_THROTTLE_MS) return;
84354
- this.lastEmitTs.set(taskId, now);
84355
- crewEventBus.emit({
84356
- type: "agent:progress",
84357
- runId,
84358
- agentId: taskId,
84359
- payload: { ...progress },
84360
- timestamp: now
84361
- });
84362
- }
84363
- };
84364
- var globalProgressTracker = new ProgressTracker();
84365
- function extractWorkerText(content) {
84366
- if (typeof content === "string") return content;
84367
- if (!Array.isArray(content)) return "";
84368
- return content.map(
84369
- (part) => part && typeof part === "object" && !Array.isArray(part) && typeof part.text === "string" ? part.text : ""
84370
- ).filter(Boolean).join("\n");
84371
- }
84372
-
84373
- // src/extension/registration/subagent-tools.ts
84374
84157
  init_role_permission();
84375
84158
  init_state_store();
84376
84159
  init_manager();
@@ -84738,20 +84521,7 @@ function startAgentToolProgress(cwd, agentRecordId, onUpdate, manager) {
84738
84521
  agents,
84739
84522
  error: record.error
84740
84523
  });
84741
- let progressLine = text;
84742
- if (tasks && tasks.length > 0) {
84743
- const wp = globalProgressTracker.getWorkerProgress(tasks[0].id);
84744
- if (wp) {
84745
- const toolLine = wp.currentTool ? `
84746
- \u26A1 tool: ${wp.currentTool}` : "";
84747
- const tokenLine = wp.tokens.input + wp.tokens.output > 0 ? `
84748
- \u{1F4CA} tokens: ${(wp.tokens.input / 1e3).toFixed(1)}K in, ${(wp.tokens.output / 1e3).toFixed(1)}K out` : "";
84749
- const textLine = wp.partialText ? `
84750
- \u{1F4AC} ${wp.partialText.slice(-150).replace(/\n/g, " ")}` : "";
84751
- progressLine = `${text}${toolLine}${tokenLine}${textLine}`;
84752
- }
84753
- }
84754
- onUpdate({ content: [{ type: "text", text: progressLine }] });
84524
+ onUpdate({ content: [{ type: "text", text }] });
84755
84525
  } catch (error) {
84756
84526
  logInternalError("subagent-tools.progress", error, `agentId=${agentRecordId}`);
84757
84527
  }
@@ -85883,9 +85653,6 @@ Subagent may need manual intervention.`
85883
85653
  const record = event;
85884
85654
  const eventType = typeof record.type === "string" ? record.type : void 0;
85885
85655
  if (eventType) lifecycleState.overflowTracker?.feedEvent(taskId, runId, eventType);
85886
- if (record && typeof record === "object") {
85887
- globalProgressTracker.handleWorkerEvent(taskId, runId, record);
85888
- }
85889
85656
  }
85890
85657
  });
85891
85658
  registerSubagentTools(pi, subagentManager, {