pi-crew 0.2.10 → 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
|
@@ -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") {
|
|
@@ -3,6 +3,7 @@ import { Type } from "@sinclair/typebox";
|
|
|
3
3
|
import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
|
|
4
4
|
// Lazy-loaded: team-tool.ts pulls in entire runtime chain.
|
|
5
5
|
import type { handleTeamTool as HandleTeamToolFn } from "../team-tool.ts";
|
|
6
|
+
import { withSessionId } from "../team-tool/context.ts";
|
|
6
7
|
let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
|
|
7
8
|
async function handleTeamTool(params: Parameters<typeof HandleTeamToolFn>[0], ctx: Parameters<typeof HandleTeamToolFn>[1]): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
|
|
8
9
|
if (!_cachedHandleTeamTool) {
|
|
@@ -63,7 +64,10 @@ export function registerSubagentTools(pi: ExtensionAPI, subagentManager: Subagen
|
|
|
63
64
|
const spawnOptions = __test__subagentSpawnParams(params as Record<string, unknown>, ctx);
|
|
64
65
|
spawnOptions.ownerSessionGeneration = options.ownerSessionGeneration?.();
|
|
65
66
|
if (!spawnOptions.prompt.trim()) return subagentToolResult(t("agent.requiresPrompt"), {}, true);
|
|
66
|
-
|
|
67
|
+
// Extract sessionId from sessionManager.getSessionId() so team runs created
|
|
68
|
+
// by the Agent tool have proper session ownership for isolation.
|
|
69
|
+
const ctxWithSession = withSessionId(ctx);
|
|
70
|
+
const runner = async (currentOptions: SubagentSpawnOptions, childSignal?: AbortSignal) => handleTeamTool({ action: "run", agent: currentOptions.type, goal: currentOptions.prompt, model: currentOptions.model, skill: currentOptions.skill, async: currentOptions.background, config: currentOptions.maxTurns ? { runtime: { maxTurns: currentOptions.maxTurns } } : undefined } as TeamToolParamsValue, { ...ctxWithSession, signal: childSignal, ...(options.startForegroundRun ? { startForegroundRun: (runRunner: (sig?: AbortSignal) => Promise<void>, runId?: string) => options.startForegroundRun!(ctxWithSession, runRunner, runId) } : {}) });
|
|
67
71
|
const record = subagentManager.spawn(spawnOptions, runner, spawnOptions.background ? undefined : signal);
|
|
68
72
|
if (spawnOptions.background || record.status === "queued") {
|
|
69
73
|
// Phase 1.1a: Terminate turn for background queued — no LLM follow-up needed.
|
package/src/ui/crew-widget.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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[] = [];
|
package/src/ui/run-dashboard.ts
CHANGED
|
@@ -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
|
-
|
|
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;
|