pi-crew 0.2.12 → 0.2.14
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/package.json
CHANGED
|
@@ -204,7 +204,7 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
|
|
|
204
204
|
pi.registerCommand("team-run", {
|
|
205
205
|
description: "Manually start a pi-crew run (agent may also use the team tool autonomously)",
|
|
206
206
|
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
207
|
-
const result = await handleTeamTool(parseRunArgs(args), { ...teamCommandContext(ctx), metricRegistry: deps.getMetricRegistry?.(), startForegroundRun: (runner, runId) => deps.startForegroundRun(ctx as ExtensionContext, runner, runId), abortForegroundRun: deps.abortForegroundRun, onRunStarted:
|
|
207
|
+
const result = await handleTeamTool(parseRunArgs(args), { ...teamCommandContext(ctx), metricRegistry: deps.getMetricRegistry?.(), startForegroundRun: (runner, runId) => deps.startForegroundRun(ctx as ExtensionContext, runner, runId), abortForegroundRun: deps.abortForegroundRun, onRunStarted: undefined });
|
|
208
208
|
await notifyCommandResult(ctx, commandText(result));
|
|
209
209
|
},
|
|
210
210
|
});
|
package/src/ui/crew-widget.ts
CHANGED
|
@@ -152,7 +152,19 @@ function agentStats(agent: CrewAgentRecord, liveHandle?: LiveAgentHandle): strin
|
|
|
152
152
|
const ctxPct = stats?.contextUsage?.percent;
|
|
153
153
|
if (ctxPct != null) parts.push(`${Math.round(ctxPct)}% ctx`);
|
|
154
154
|
} catch { /* ignore */ }
|
|
155
|
-
const
|
|
155
|
+
const rawStarted = act.startedAtMs || 0;
|
|
156
|
+
const rawCompleted = act.completedAtMs || 0;
|
|
157
|
+
const nowMs = Date.now();
|
|
158
|
+
const nowSec = Math.floor(nowMs / 1000);
|
|
159
|
+
// Detect if value is in seconds (Unix timestamp) vs milliseconds
|
|
160
|
+
// If value looks like Unix seconds (within range of ±2 years from now), convert to ms
|
|
161
|
+
const isSeconds = (v: number) => v > 1000000000 && v < 2000000000 + 31536000 * 2;
|
|
162
|
+
const startedMs = isSeconds(rawStarted) ? rawStarted * 1000 : rawStarted;
|
|
163
|
+
const completedMs = isSeconds(rawCompleted) ? rawCompleted * 1000 : rawCompleted;
|
|
164
|
+
// Validate: startedAtMs should be within reasonable bounds
|
|
165
|
+
const isValidStarted = startedMs > 0 && startedMs < nowMs + 60000 && startedMs > nowMs - 3155692600000;
|
|
166
|
+
const isValidCompleted = completedMs === 0 || (completedMs > 0 && completedMs < nowMs + 60000);
|
|
167
|
+
const ms = (isValidCompleted ? completedMs : nowMs) - (isValidStarted ? startedMs : nowMs);
|
|
156
168
|
parts.push(`${(ms / 1000).toFixed(1)}s`);
|
|
157
169
|
} else {
|
|
158
170
|
if (agent.toolUses) parts.push(`${agent.toolUses} tools`);
|
|
@@ -423,7 +435,13 @@ export function updateCrewWidget(
|
|
|
423
435
|
if (!ctx.hasUI) return;
|
|
424
436
|
state.frame += 1;
|
|
425
437
|
const maxLines = config?.widgetMaxLines ?? MAX_LINES_DEFAULT;
|
|
426
|
-
|
|
438
|
+
// Get workspaceId from sessionManager, fallback to ownerSessionId from active runs
|
|
439
|
+
let workspaceId = ctx.sessionManager?.getSessionId?.();
|
|
440
|
+
if (!workspaceId && manifestCache) {
|
|
441
|
+
const runs = manifestCache.list(20);
|
|
442
|
+
const active = runs.find((r) => r.status === "running" || r.status === "queued");
|
|
443
|
+
if (active?.ownerSessionId) workspaceId = active.ownerSessionId;
|
|
444
|
+
}
|
|
427
445
|
const runs = activeWidgetRuns(ctx.cwd, manifestCache, snapshotCache, preloadedManifests, workspaceId);
|
|
428
446
|
const lines = buildCrewWidgetLines(ctx.cwd, state.frame, maxLines, runs, state.notificationCount ?? 0);
|
|
429
447
|
const placement = config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
|
|
@@ -63,9 +63,21 @@ export class LiveConversationOverlay {
|
|
|
63
63
|
|
|
64
64
|
private static readonly SUMMARY_PREFIX = "\u200B"; // zero-width space as summary sentinel
|
|
65
65
|
|
|
66
|
+
private safeElapsedMs(act: typeof this.handle.activity): number {
|
|
67
|
+
const rawStarted = act.startedAtMs || 0;
|
|
68
|
+
const rawCompleted = act.completedAtMs || 0;
|
|
69
|
+
const nowMs = Date.now();
|
|
70
|
+
// Detect if value is in seconds vs milliseconds
|
|
71
|
+
const isSeconds = (v: number) => v > 1000000000 && v < 2000000000 + 31536000 * 2;
|
|
72
|
+
const startedMs = isSeconds(rawStarted) ? rawStarted * 1000 : rawStarted;
|
|
73
|
+
const completedMs = isSeconds(rawCompleted) ? rawCompleted * 1000 : rawCompleted;
|
|
74
|
+
const isValidStarted = startedMs > 0 && startedMs < nowMs + 60000 && startedMs > nowMs - 3155692600000;
|
|
75
|
+
const isValidCompleted = completedMs === 0 || (completedMs > 0 && completedMs < nowMs + 60000);
|
|
76
|
+
return (isValidCompleted ? completedMs : nowMs) - (isValidStarted ? startedMs : nowMs);
|
|
77
|
+
}
|
|
66
78
|
private refreshSummary(): void {
|
|
67
79
|
const act = this.handle.activity;
|
|
68
|
-
const summary = `${LiveConversationOverlay.SUMMARY_PREFIX}[${act.turnCount} turns · ${act.toolUses} tools · ${(
|
|
80
|
+
const summary = `${LiveConversationOverlay.SUMMARY_PREFIX}[${act.turnCount} turns · ${act.toolUses} tools · ${(this.safeElapsedMs(act) / 1000).toFixed(1)}s]`;
|
|
69
81
|
const lastLine = this.cachedLines[this.cachedLines.length - 1];
|
|
70
82
|
if (lastLine?.startsWith(LiveConversationOverlay.SUMMARY_PREFIX)) {
|
|
71
83
|
this.cachedLines[this.cachedLines.length - 1] = summary;
|
|
@@ -100,7 +112,7 @@ export class LiveConversationOverlay {
|
|
|
100
112
|
: iconForStatus(this.handle.status);
|
|
101
113
|
const name = this.handle.agent ?? this.handle.taskId;
|
|
102
114
|
const act = this.handle.activity;
|
|
103
|
-
const elapsed = `${(
|
|
115
|
+
const elapsed = `${(this.safeElapsedMs(act) / 1000).toFixed(1)}s`;
|
|
104
116
|
const headerParts: string[] = [];
|
|
105
117
|
if (act.maxTurns != null) headerParts.push(`turn ${act.turnCount}/${act.maxTurns}`);
|
|
106
118
|
else if (act.turnCount > 0) headerParts.push(`turn ${act.turnCount}`);
|
|
@@ -65,6 +65,8 @@ export class LiveRunSidebar {
|
|
|
65
65
|
private cachedLines: string[] = [];
|
|
66
66
|
private cachedWidth = 0;
|
|
67
67
|
private cachedSignature = "";
|
|
68
|
+
private autoCloseTimeout?: NodeJS.Timeout;
|
|
69
|
+
private hasAutoClosed = false;
|
|
68
70
|
|
|
69
71
|
constructor(input: { cwd: string; runId: string; done: Done; theme?: unknown; config?: CrewUiConfig; snapshotCache?: RunSnapshotCache }) {
|
|
70
72
|
this.cwd = input.cwd;
|
|
@@ -167,6 +169,24 @@ export class LiveRunSidebar {
|
|
|
167
169
|
lines.push(border("├", "─", "┤", w));
|
|
168
170
|
for (const entry of formatTaskGraphLines(tasks).slice(0, 6)) lines.push(line(entry, w));
|
|
169
171
|
lines.push(line("q close · /team-dashboard details", w), border("╰", "─", "╯", w));
|
|
172
|
+
// Auto-close logic: if run is terminal and no active agents, close after delay
|
|
173
|
+
const isTerminal = ["completed", "failed", "cancelled", "blocked"].includes(run.status);
|
|
174
|
+
const hasActiveAgents = agents.some((a) => a.status === "running");
|
|
175
|
+
if (isTerminal && !hasActiveAgents && !this.hasAutoClosed) {
|
|
176
|
+
const autoCloseMs = (this.config?.autoCloseDashboardMs ?? 3000);
|
|
177
|
+
if (autoCloseMs > 0) {
|
|
178
|
+
this.autoCloseTimeout = setTimeout(() => {
|
|
179
|
+
this.hasAutoClosed = true;
|
|
180
|
+
this.done(undefined);
|
|
181
|
+
}, autoCloseMs);
|
|
182
|
+
lines.push(line(`auto-close in ${Math.round(autoCloseMs / 1000)}s…`, w));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Clear timeout if conditions change
|
|
186
|
+
else if (this.autoCloseTimeout) {
|
|
187
|
+
clearTimeout(this.autoCloseTimeout);
|
|
188
|
+
this.autoCloseTimeout = undefined;
|
|
189
|
+
}
|
|
170
190
|
this.cachedLines = renderLines(lines.map((entry) => this.colorLine(entry)), w);
|
|
171
191
|
this.cachedSignature = signature;
|
|
172
192
|
this.cachedWidth = w;
|