pi-crew 0.2.11 → 0.2.12

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-crew",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
5
5
  "author": "baphuongna",
6
6
  "license": "MIT",
@@ -387,12 +387,14 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
387
387
 
388
388
  pi.registerCommand("team-dashboard", { description: "Open a pi-crew run dashboard overlay", handler: async (_args: string, ctx: ExtensionCommandContext) => {
389
389
  for (;;) {
390
+ // Extract sessionId for workspace-scoped filtering
391
+ const sessionId = ctx.sessionManager?.getSessionId?.();
390
392
  const runs = deps.getManifestCache(ctx.cwd).list(50);
391
393
  const uiConfig = loadConfig(ctx.cwd).config.ui;
392
394
  const rightPanel = (uiConfig?.dashboardPlacement ?? DEFAULT_UI.dashboardPlacement) === "right";
393
395
  const width = rightPanel ? Math.min(90, Math.max(40, uiConfig?.dashboardWidth ?? DEFAULT_UI.dashboardWidth)) : "90%";
394
396
  const { RunDashboard } = await ui();
395
- const selection = await ctx.ui.custom<RunDashboardSelection | undefined>((tui, theme, _keybindings, done) => new RunDashboard(runs, done, theme, { placement: rightPanel ? "right" : "center", showModel: uiConfig?.showModel, showTokens: uiConfig?.showTokens, showTools: uiConfig?.showTools, snapshotCache: deps.getRunSnapshotCache?.(ctx.cwd), runProvider: () => deps.getManifestCache(ctx.cwd).list(50), registry: deps.getMetricRegistry?.(), requestRender: () => requestRenderTarget(tui) }), { overlay: true, overlayOptions: rightPanel ? { width, minWidth: 40, maxHeight: "100%", anchor: "top-right", offsetX: 0, offsetY: 0, margin: { top: 0, right: 0, bottom: 0, left: 0 } } : { width, maxHeight: "90%", anchor: "center", margin: 2 } });
397
+ const selection = await ctx.ui.custom<RunDashboardSelection | undefined>((tui, theme, _keybindings, done) => new RunDashboard(runs, done, theme, { placement: rightPanel ? "right" : "center", showModel: uiConfig?.showModel, showTokens: uiConfig?.showTokens, showTools: uiConfig?.showTools, snapshotCache: deps.getRunSnapshotCache?.(ctx.cwd), runProvider: () => deps.getManifestCache(ctx.cwd).list(50), registry: deps.getMetricRegistry?.(), workspaceId: sessionId, requestRender: () => requestRenderTarget(tui) }), { overlay: true, overlayOptions: rightPanel ? { width, minWidth: 40, maxHeight: "100%", anchor: "top-right", offsetX: 0, offsetY: 0, margin: { top: 0, right: 0, bottom: 0, left: 0 } } : { width, maxHeight: "90%", anchor: "center", margin: 2 } });
396
398
  if (!selection) return;
397
399
  if (selection.action === "reload") continue;
398
400
  if (selection.action === "notifications-dismiss") {
@@ -175,7 +175,7 @@ function agentsFor(run: TeamRunManifest): CrewAgentRecord[] {
175
175
  let lastStaleReconcileAt = 0;
176
176
  const STALE_RECONCILE_INTERVAL_MS = 60_000;
177
177
 
178
- export function activeWidgetRuns(cwd: string, manifestCache?: ManifestCache, snapshotCache?: RunSnapshotCache, preloadedManifests?: TeamRunManifest[]): WidgetRun[] {
178
+ export function activeWidgetRuns(cwd: string, manifestCache?: ManifestCache, snapshotCache?: RunSnapshotCache, preloadedManifests?: TeamRunManifest[], workspaceId?: string): WidgetRun[] {
179
179
  // Evict stale live-agent handles (terminal status >10min, or running >30min with no update)
180
180
  evictStaleLiveAgentHandles();
181
181
  // Periodic stale reconciliation: detect ghost runs on disk with dead PIDs
@@ -185,7 +185,11 @@ export function activeWidgetRuns(cwd: string, manifestCache?: ManifestCache, sna
185
185
  lastStaleReconcileAt = now;
186
186
  try { reconcileAllStaleRuns(cwd, manifestCache); } catch { /* non-critical background maintenance */ }
187
187
  }
188
- const runs = preloadedManifests ?? (manifestCache ? manifestCache.list(20) : listRecentRuns(cwd, 20));
188
+ let runs = preloadedManifests ?? (manifestCache ? manifestCache.list(20) : listRecentRuns(cwd, 20));
189
+ // Filter by workspaceId for session isolation
190
+ if (workspaceId) {
191
+ runs = runs.filter((run) => !run.ownerSessionId || run.ownerSessionId === workspaceId);
192
+ }
189
193
  return runs
190
194
  .map((run) => {
191
195
  try {
@@ -409,7 +413,7 @@ class CrewWidgetComponent implements WidgetComponent {
409
413
  }
410
414
 
411
415
  export function updateCrewWidget(
412
- ctx: Pick<ExtensionContext, "cwd" | "hasUI" | "ui">,
416
+ ctx: Pick<ExtensionContext, "cwd" | "hasUI" | "ui" | "sessionManager">,
413
417
  state: CrewWidgetState,
414
418
  config?: CrewUiConfig,
415
419
  manifestCache?: ManifestCache,
@@ -419,7 +423,8 @@ export function updateCrewWidget(
419
423
  if (!ctx.hasUI) return;
420
424
  state.frame += 1;
421
425
  const maxLines = config?.widgetMaxLines ?? MAX_LINES_DEFAULT;
422
- const runs = activeWidgetRuns(ctx.cwd, manifestCache, snapshotCache, preloadedManifests);
426
+ const workspaceId = ctx.sessionManager?.getSessionId?.();
427
+ const runs = activeWidgetRuns(ctx.cwd, manifestCache, snapshotCache, preloadedManifests, workspaceId);
423
428
  const lines = buildCrewWidgetLines(ctx.cwd, state.frame, maxLines, runs, state.notificationCount ?? 0);
424
429
  const placement = config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
425
430
  ctx.ui.setStatus(STATUS_KEY, lines.length ? statusSummary(runs) : undefined);
@@ -3,7 +3,7 @@ import { iconForStatus } from "../status-colors.ts";
3
3
  import type { RunUiSnapshot } from "../snapshot-types.ts";
4
4
  import { spinnerFrame } from "../spinner.ts";
5
5
  import type { CrewAgentRecord } from "../../runtime/crew-agent-runtime.ts";
6
- import { listLiveAgents, type LiveAgentHandle } from "../../runtime/live-agent-manager.ts";
6
+ import { listLiveAgents, listLiveAgentsByWorkspace, type LiveAgentHandle } from "../../runtime/live-agent-manager.ts";
7
7
 
8
8
  /**
9
9
  * Returns true if this agent did real work (LLM call, tool use, or non-trivial duration).
@@ -52,7 +52,11 @@ function describeActivity(handle: LiveAgentHandle): string {
52
52
  export function renderAgentsPane(snapshot: RunUiSnapshot | undefined, options: RunDashboardOptions = {}): string[] {
53
53
  if (!snapshot) return ["(snapshot unavailable)"];
54
54
  if (!snapshot.agents.length) return ["(no agents)"];
55
- const liveForRun = listLiveAgents().filter(h => h.runId === snapshot.runId);
55
+ // Filter live agents by workspaceId for session isolation
56
+ const allLive = options.workspaceId
57
+ ? listLiveAgentsByWorkspace(options.workspaceId)
58
+ : listLiveAgents();
59
+ const liveForRun = allLive.filter(h => h.runId === snapshot.runId);
56
60
  const { completed, total } = snapshot.progress;
57
61
 
58
62
  const lines: string[] = [];
@@ -41,6 +41,12 @@ export interface RunDashboardOptions {
41
41
  snapshotCache?: RunSnapshotCache;
42
42
  runProvider?: () => TeamRunManifest[];
43
43
  registry?: MetricRegistry;
44
+ /**
45
+ * Workspace/session ID for filtering runs and live agents. When provided,
46
+ * only runs with matching ownerSessionId and live agents with matching
47
+ * workspaceId are shown. This ensures session isolation in the UI.
48
+ */
49
+ workspaceId?: string;
44
50
  /**
45
51
  * Poke the host TUI to repaint after a state change. Must be wired from
46
52
  * `commands.ts` (`() => requestRenderTarget(tui)`) so keypresses and event-bus
@@ -279,7 +285,12 @@ export class RunDashboard implements DashboardComponent {
279
285
  theme: unknown = {},
280
286
  options: RunDashboardOptions = {},
281
287
  ) {
282
- this.runs = runs;
288
+ // Filter runs by workspaceId for session isolation
289
+ // If workspaceId is provided, only show runs owned by that session or runs with no owner (legacy)
290
+ const filteredRuns = options.workspaceId
291
+ ? runs.filter((run) => !run.ownerSessionId || run.ownerSessionId === options.workspaceId)
292
+ : runs;
293
+ this.runs = filteredRuns;
283
294
  this.done = done;
284
295
  this.theme = asCrewTheme(theme);
285
296
  this.options = options;