pi-cockpit 0.2.0 → 0.3.0

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-cockpit",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Agent cockpit for Pi: a list-mode status stack (live teammates + todo plan) pinned above the editor, plus a Starship-style footer. Renders only via public extension APIs (setWidget/setFooter).",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -48,6 +48,10 @@ export interface ProgressPayload {
48
48
  export interface MessagePayload {
49
49
  correlationId: string;
50
50
  taskCorrelationId?: string;
51
+ // Present on progress/interaction deltas (teammate/.../index.ts publishProgress);
52
+ // absent on send deltas. Used to self-heal a row when `started` was missed.
53
+ agent?: string;
54
+ name?: string;
51
55
  taskIndex?: number;
52
56
  dependencies?: number[];
53
57
  message?: string;
@@ -120,6 +124,9 @@ function latestTool(tools: MessagePayload["recentTools"]): string | undefined {
120
124
  // Self-accumulating roster. The teammate extension only broadcasts deltas
121
125
  // (started/message/complete), never a full snapshot, so we rebuild the list here.
122
126
  // Cold start is empty by design — we only reflect activity observed after load.
127
+ // The roster is self-healing: message/progress deltas for an agent whose
128
+ // `started` delta was missed still materialize a row, so a running agent is
129
+ // always represented as long as it keeps emitting activity.
123
130
  export class AgentsStore {
124
131
  private readonly roster = new Map<string, AgentRow>();
125
132
 
@@ -150,6 +157,22 @@ export class AgentsStore {
150
157
  for (const progress of p.progress) this.applyProgress(p.correlationId, progress, now);
151
158
  }
152
159
  const targetId = p.taskCorrelationId ?? p.correlationId;
160
+ if (!this.roster.has(targetId)) {
161
+ // Self-heal the roster: a running agent must stay visible even when its
162
+ // `started` delta was missed (cold start, event reorder, or a foreground
163
+ // dispatch that detached to background). Materialize the row from this
164
+ // event instead of dropping it; later deltas refine the placeholder.
165
+ this.applyStarted({
166
+ correlationId: targetId,
167
+ agent: p.agent ?? "",
168
+ name: p.name,
169
+ status: p.status,
170
+ lastActivityAt: p.lastActivityAt,
171
+ ...(p.taskCorrelationId && p.taskCorrelationId !== p.correlationId
172
+ ? { spawnedBy: p.correlationId }
173
+ : {}),
174
+ }, now);
175
+ }
153
176
  const row = this.roster.get(targetId);
154
177
  if (!row) return;
155
178
  const progressActivity = p.progress?.find((progress) => progress.correlationId === targetId)?.lastActivityAt;
@@ -223,6 +246,21 @@ export class AgentsStore {
223
246
  }
224
247
 
225
248
  private applyProgress(parentCorrelationId: string, p: ProgressPayload, now: number): void {
249
+ if (!this.roster.has(p.correlationId)) {
250
+ // Self-heal: see applyMessage. A graph child whose `started` delta was
251
+ // missed still materializes from its first progress event.
252
+ this.applyStarted({
253
+ correlationId: p.correlationId,
254
+ agent: p.agent,
255
+ name: p.name,
256
+ status: p.status,
257
+ startedAt: p.startedAt,
258
+ lastActivityAt: p.lastActivityAt,
259
+ ...(parentCorrelationId !== p.correlationId
260
+ ? { spawnedBy: parentCorrelationId }
261
+ : {}),
262
+ }, now);
263
+ }
226
264
  const row = this.roster.get(p.correlationId);
227
265
  if (!row) return;
228
266
  row.parentCorrelationId = parentCorrelationId === p.correlationId