@shawnowen/comet-mcp 2.3.1 → 2.4.2

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.
Files changed (65) hide show
  1. package/README.md +97 -19
  2. package/dist/alert-dispatcher.d.ts +23 -0
  3. package/dist/alert-dispatcher.js +101 -0
  4. package/dist/binding-reaper.d.ts +46 -0
  5. package/dist/binding-reaper.js +73 -0
  6. package/dist/bound-session.d.ts +23 -0
  7. package/dist/bound-session.js +119 -0
  8. package/dist/bridge-config.d.ts +6 -0
  9. package/dist/bridge-config.js +78 -0
  10. package/dist/cdp-client.d.ts +40 -4
  11. package/dist/cdp-client.js +502 -155
  12. package/dist/comet-ai.d.ts +15 -0
  13. package/dist/comet-ai.js +114 -38
  14. package/dist/delegate-binding.d.ts +19 -0
  15. package/dist/delegate-binding.js +73 -0
  16. package/dist/http-server.js +2188 -47
  17. package/dist/index.js +3545 -788
  18. package/dist/observer.d.ts +47 -0
  19. package/dist/observer.js +516 -0
  20. package/dist/project-config.d.ts +46 -0
  21. package/dist/project-config.js +166 -0
  22. package/dist/session-registry.d.ts +57 -0
  23. package/dist/session-registry.js +500 -0
  24. package/dist/sidecar-artifacts.d.ts +49 -0
  25. package/dist/sidecar-artifacts.js +146 -0
  26. package/dist/snapshot-capture.d.ts +3 -0
  27. package/dist/snapshot-capture.js +91 -0
  28. package/dist/tab-group-archive.js +3 -1
  29. package/dist/tab-groups.d.ts +28 -1
  30. package/dist/tab-groups.js +205 -3
  31. package/dist/types.d.ts +237 -0
  32. package/dist/window-bindings.d.ts +160 -0
  33. package/dist/window-bindings.js +561 -0
  34. package/extension/background.js +1577 -300
  35. package/extension/icons/icon.svg +9 -0
  36. package/extension/icons/icon128.png +0 -0
  37. package/extension/icons/icon16.png +0 -0
  38. package/extension/icons/icon48.png +0 -0
  39. package/extension/manifest.json +34 -4
  40. package/extension/perplexity-capability-manifest.json +1181 -0
  41. package/extension/perplexity-capability-manifest.schema.json +142 -0
  42. package/extension/session-logic.js +3054 -0
  43. package/extension/session-manager.html +311 -0
  44. package/extension/sidepanel.css +5338 -528
  45. package/extension/sidepanel.html +282 -2
  46. package/extension/sidepanel.js +10604 -950
  47. package/extension/window-policy.js +162 -0
  48. package/package.json +10 -7
  49. package/vendor/lifecycle-mcp-adapter.mjs +103 -0
  50. package/vendor/lifecycle-metadata.mjs +252 -0
  51. package/vendor/readiness-report.mjs +742 -0
  52. package/dist/cdp-client.d.ts.map +0 -1
  53. package/dist/cdp-client.js.map +0 -1
  54. package/dist/comet-ai.d.ts.map +0 -1
  55. package/dist/comet-ai.js.map +0 -1
  56. package/dist/http-server.d.ts.map +0 -1
  57. package/dist/http-server.js.map +0 -1
  58. package/dist/index.d.ts.map +0 -1
  59. package/dist/index.js.map +0 -1
  60. package/dist/tab-group-archive.d.ts.map +0 -1
  61. package/dist/tab-group-archive.js.map +0 -1
  62. package/dist/tab-groups.d.ts.map +0 -1
  63. package/dist/tab-groups.js.map +0 -1
  64. package/dist/types.d.ts.map +0 -1
  65. package/dist/types.js.map +0 -1
@@ -0,0 +1,3 @@
1
+ import type { AgentSession, TaskThreadSnapshot, SnapshotReason, TaskThreadStatus } from "./types.js";
2
+ export declare function captureTaskThreadSnapshot(session: AgentSession, reason: SnapshotReason, taskStatus?: TaskThreadStatus): Promise<TaskThreadSnapshot>;
3
+ //# sourceMappingURL=snapshot-capture.d.ts.map
@@ -0,0 +1,91 @@
1
+ // Snapshot Capture — Task Thread state preservation before automated closure (Spec 016, FR-003)
2
+ // Captures all tabs, Sidecar threads, window metadata, and task status.
3
+ // Atomic write: temp file → rename to prevent partial captures.
4
+ // If capture fails, caller MUST NOT proceed with close (fail safe).
5
+ import { writeFileSync, renameSync, mkdirSync } from "fs";
6
+ import { join } from "path";
7
+ import { loadBridgeConfig } from "./bridge-config.js";
8
+ export async function captureTaskThreadSnapshot(session, reason, taskStatus = "abandoned") {
9
+ const config = loadBridgeConfig();
10
+ const tabs = [];
11
+ const sidecarThreads = [];
12
+ let windowMetadata = null;
13
+ // Query tabs in this session's tab group via extension messaging
14
+ try {
15
+ const { tabGroupsClient } = await import("./tab-groups.js");
16
+ const allTabs = await tabGroupsClient.listTabs();
17
+ // Filter to tabs in this session's tab group, or by ownership if no group (extension degradation)
18
+ const sessionTabs = session.tabGroupId !== null
19
+ ? allTabs.filter((t) => t.groupId === session.tabGroupId)
20
+ : allTabs.filter((t) => t.id === session.chromeTabId);
21
+ for (const tab of sessionTabs) {
22
+ tabs.push({
23
+ url: tab.url,
24
+ title: tab.title,
25
+ chromeTabId: tab.id,
26
+ targetId: session.targetId,
27
+ active: tab.active,
28
+ });
29
+ // Check if tab has a Sidecar thread (Perplexity assistant panel)
30
+ if (tab.url?.includes("perplexity.ai")) {
31
+ sidecarThreads.push({
32
+ threadUrl: tab.url,
33
+ parentTabUrl: tab.url,
34
+ lastQuery: null,
35
+ });
36
+ }
37
+ }
38
+ // Capture window metadata
39
+ const groups = await tabGroupsClient.listGroups();
40
+ const sessionGroup = groups.find((g) => g.id === session.tabGroupId);
41
+ if (sessionGroup) {
42
+ windowMetadata = {
43
+ windowId: sessionGroup.windowId,
44
+ x: 0,
45
+ y: -1440,
46
+ width: 2560,
47
+ height: 1440,
48
+ displayIndex: 0,
49
+ };
50
+ }
51
+ }
52
+ catch (extErr) {
53
+ // Extension not available — capture what we can from CDP
54
+ console.warn(`[comet-bridge] Snapshot degraded for ${session.sessionKey}: extension unavailable (${extErr instanceof Error ? extErr.message : extErr})`);
55
+ tabs.push({
56
+ url: "unknown (extension unavailable)",
57
+ title: session.taskThreadId,
58
+ chromeTabId: session.chromeTabId ?? 0,
59
+ targetId: session.targetId,
60
+ active: true,
61
+ });
62
+ }
63
+ const snapshot = {
64
+ sessionKey: session.sessionKey,
65
+ agentId: session.agentId,
66
+ taskThreadId: session.taskThreadId,
67
+ capturedAt: new Date().toISOString(),
68
+ reason,
69
+ taskStatus,
70
+ tabs,
71
+ sidecarThreads,
72
+ windowMetadata,
73
+ tabGroupId: session.tabGroupId,
74
+ tabGroupColor: session.tabGroupColor,
75
+ };
76
+ // Atomic write: temp file → rename (T026)
77
+ const snapshotDir = config.cleanup.snapshotDir;
78
+ mkdirSync(snapshotDir, { recursive: true });
79
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
80
+ const safeAgentId = session.agentId.replace(/[^a-zA-Z0-9_-]/g, "_");
81
+ const safeThreadId = session.taskThreadId.replace(/[^a-zA-Z0-9_-]/g, "_");
82
+ const filename = `${safeAgentId}_${safeThreadId}_${timestamp}.json`;
83
+ const finalPath = join(snapshotDir, filename);
84
+ const tempPath = finalPath + ".tmp";
85
+ // Write to temp file first — if this fails, no partial snapshot exists
86
+ writeFileSync(tempPath, JSON.stringify(snapshot, null, 2));
87
+ // Atomic rename — if this fails, temp file exists but final doesn't
88
+ renameSync(tempPath, finalPath);
89
+ return snapshot;
90
+ }
91
+ //# sourceMappingURL=snapshot-capture.js.map
@@ -11,7 +11,9 @@ let writeQueue = Promise.resolve();
11
11
  function serialized(fn) {
12
12
  const prev = writeQueue;
13
13
  let resolve;
14
- writeQueue = new Promise((r) => { resolve = r; });
14
+ writeQueue = new Promise((r) => {
15
+ resolve = r;
16
+ });
15
17
  return prev.then(fn).finally(() => resolve());
16
18
  }
17
19
  function resolveArchivePaths(archiveDir) {
@@ -15,6 +15,11 @@ export interface TabInfo {
15
15
  url: string;
16
16
  active: boolean;
17
17
  }
18
+ export interface WindowTabInfo {
19
+ tabId: number;
20
+ windowId: number;
21
+ url: string;
22
+ }
18
23
  export interface CreateGroupOptions {
19
24
  tabIds: number[];
20
25
  title?: string;
@@ -26,6 +31,22 @@ export interface UpdateGroupOptions {
26
31
  color?: TabGroupColor;
27
32
  collapsed?: boolean;
28
33
  }
34
+ export interface ExtensionMetadataUpdateOptions {
35
+ entityType?: string;
36
+ entityId?: string | number;
37
+ windowId?: string | number;
38
+ activeWindowId?: string | number;
39
+ id?: string | number;
40
+ taskThreadId?: string;
41
+ tabIndex?: number;
42
+ sourceFamily?: string;
43
+ intent?: string;
44
+ policyTier?: string;
45
+ description?: string;
46
+ title?: string;
47
+ label?: string;
48
+ status?: string;
49
+ }
29
50
  export interface TabGroupArchiveEntry {
30
51
  taskThreadId: string;
31
52
  title: string;
@@ -37,7 +58,7 @@ export interface TabGroupArchiveEntry {
37
58
  }[];
38
59
  archivedAt: string;
39
60
  restoredAt?: string;
40
- status: "saved" | "archived";
61
+ status: "pending" | "done" | "trashed" | "saved" | "archived";
41
62
  }
42
63
  export declare class TabGroupsClient {
43
64
  private client;
@@ -68,6 +89,8 @@ export declare class TabGroupsClient {
68
89
  groupId: number;
69
90
  group: TabGroup;
70
91
  }>;
92
+ /** Create a managed browser window locked to the top display fullscreen policy. */
93
+ createTopDisplayFullscreenWindowWithTab(url: string): Promise<WindowTabInfo>;
71
94
  /** Update an existing tab group's title, color, or collapsed state. */
72
95
  updateGroup(options: UpdateGroupOptions): Promise<TabGroup>;
73
96
  /** Move a tab group to a new position. */
@@ -76,6 +99,10 @@ export declare class TabGroupsClient {
76
99
  ungroupTabs(tabIds: number[]): Promise<void>;
77
100
  /** List all tabs with their groupId (−1 = ungrouped). */
78
101
  listTabs(): Promise<TabInfo[]>;
102
+ /** List extension-owned descriptions, window labels, archives, and selected metadata. */
103
+ listExtensionMetadata(): Promise<Record<string, unknown>>;
104
+ /** Update extension-owned metadata without touching native tab state. */
105
+ updateExtensionMetadata(options: ExtensionMetadataUpdateOptions): Promise<Record<string, unknown>>;
79
106
  /** Create a single tab and return its ID. Used by ETT restore_group. */
80
107
  createTab(url: string, active?: boolean): Promise<number>;
81
108
  /** Close multiple tabs by ID. Used by ETT archive_group. */
@@ -116,8 +116,7 @@ export class TabGroupsClient {
116
116
  returnByValue: true,
117
117
  });
118
118
  if (result.exceptionDetails) {
119
- const msg = result.exceptionDetails.exception?.description ||
120
- result.exceptionDetails.text;
119
+ const msg = result.exceptionDetails.exception?.description || result.exceptionDetails.text;
121
120
  throw new Error(`Extension eval error: ${msg}`);
122
121
  }
123
122
  return result.result.value;
@@ -163,7 +162,10 @@ export class TabGroupsClient {
163
162
  if (Object.keys(updateProps).length > 0) {
164
163
  await chrome.tabGroups.update(groupId, updateProps);
165
164
  }
166
- const g = await chrome.tabGroups.get(groupId);
165
+ const enforced = await self.CometWindowPolicy.ensureOneGroupPerWindow(groupId, {
166
+ markChangedWindowManaged: true
167
+ });
168
+ const g = enforced?.group || (await chrome.tabGroups.get(groupId));
167
169
  return {
168
170
  groupId: g.id,
169
171
  group: {
@@ -174,6 +176,22 @@ export class TabGroupsClient {
174
176
  })()
175
177
  `);
176
178
  }
179
+ /** Create a managed browser window locked to the top display fullscreen policy. */
180
+ async createTopDisplayFullscreenWindowWithTab(url) {
181
+ return await this.evaluate(`
182
+ (async () => {
183
+ const win = await self.CometWindowPolicy.createTopDisplayFullscreenWindow({
184
+ url: ${JSON.stringify(url)}
185
+ });
186
+ const tab = win.tabs?.[0] || (await chrome.tabs.query({ windowId: win.id }))[0];
187
+ return {
188
+ tabId: tab.id,
189
+ windowId: win.id,
190
+ url: tab.url || tab.pendingUrl || ${JSON.stringify(url)}
191
+ };
192
+ })()
193
+ `);
194
+ }
177
195
  /** Update an existing tab group's title, color, or collapsed state. */
178
196
  async updateGroup(options) {
179
197
  const props = [];
@@ -225,6 +243,190 @@ export class TabGroupsClient {
225
243
  })()
226
244
  `);
227
245
  }
246
+ /** List extension-owned descriptions, window labels, archives, and selected metadata. */
247
+ async listExtensionMetadata() {
248
+ return await this.evaluate(`
249
+ (async () => {
250
+ const stored = await chrome.storage.local.get([
251
+ "sessionEntityDescriptions",
252
+ "sidebarSettings",
253
+ "archivedGroups",
254
+ "recentlyClosedGroups",
255
+ "fsSelectedGroups",
256
+ "fsSelectedTabs"
257
+ ]);
258
+ const normalizeArchiveWorkflowStatus = (status) => {
259
+ const raw = String(status || "").trim().toLowerCase();
260
+ if (raw === "done") return "done";
261
+ if (raw === "trashed") return "trashed";
262
+ return "pending";
263
+ };
264
+ const settings = stored.sidebarSettings || {};
265
+ const archives = Array.isArray(stored.archivedGroups) ? stored.archivedGroups : [];
266
+ return {
267
+ entityDescriptions: stored.sessionEntityDescriptions || {},
268
+ windowLabels: settings.windowLabels || {},
269
+ archives: archives.map((entry) => ({
270
+ taskThreadId: entry.taskThreadId || entry.id,
271
+ title: entry.title || entry.sessionName || "Untitled",
272
+ sessionName: entry.sessionName || null,
273
+ taskGoal: entry.taskGoal || null,
274
+ description: String(entry.description || entry.note || ""),
275
+ status: normalizeArchiveWorkflowStatus(entry.status),
276
+ rawStatus: entry.status || null,
277
+ tabCount: Array.isArray(entry.urls) ? entry.urls.length : 0,
278
+ urls: Array.isArray(entry.urls)
279
+ ? entry.urls.map((tab) => ({
280
+ url: String(tab && tab.url ? tab.url : ""),
281
+ title: String(tab && tab.title ? tab.title : ""),
282
+ description: String(tab && tab.description ? tab.description : "")
283
+ }))
284
+ : [],
285
+ archivedAt: entry.archivedAt || null,
286
+ closedAt: entry.closedAt || null,
287
+ restoredAt: entry.restoredAt || null,
288
+ orchestratorUrl: entry.orchestratorUrl || null,
289
+ locked: !!entry.locked,
290
+ pinned: !!entry.pinned,
291
+ starred: !!entry.starred
292
+ })),
293
+ recentGroups: Array.isArray(stored.recentlyClosedGroups) ? stored.recentlyClosedGroups : [],
294
+ selectedItems: {
295
+ groups: Array.isArray(stored.fsSelectedGroups) ? stored.fsSelectedGroups : [],
296
+ tabs: Array.isArray(stored.fsSelectedTabs) ? stored.fsSelectedTabs : []
297
+ }
298
+ };
299
+ })()
300
+ `);
301
+ }
302
+ /** Update extension-owned metadata without touching native tab state. */
303
+ async updateExtensionMetadata(options) {
304
+ const payload = JSON.stringify(options || {});
305
+ return await this.evaluate(`
306
+ (async () => {
307
+ const input = ${payload};
308
+ const now = new Date().toISOString();
309
+ const entityType = String(input.entityType || "").toLowerCase();
310
+ const taskThreadId = String(input.taskThreadId || input.entityId || input.id || "");
311
+ const makeMetadataAudit = (target) => ({
312
+ intent: String(input.intent || input.action || "update_metadata"),
313
+ target,
314
+ activeWindow: {
315
+ windowId: String(input.activeWindowId || input.activeWindow?.windowId || "extension_service_worker")
316
+ },
317
+ policyTier: String(input.policyTier || "prime"),
318
+ status: "success",
319
+ createdAt: now
320
+ });
321
+ const coerceArchiveWorkflowStatusForWrite = (status) => {
322
+ const raw = String(status || "").trim().toLowerCase();
323
+ if (raw === "saved" || raw === "archived") return "pending";
324
+ if (raw === "pending" || raw === "done" || raw === "trashed") return raw;
325
+ throw new Error("Invalid status: " + status);
326
+ };
327
+
328
+ if (entityType === "window" || entityType === "window-label") {
329
+ const windowId = String(input.windowId || input.entityId || input.id || "");
330
+ if (!windowId) throw new Error("windowId is required for window metadata updates");
331
+ const stored = await chrome.storage.local.get("sidebarSettings");
332
+ const settings = stored.sidebarSettings || {};
333
+ settings.windowLabels = settings.windowLabels || {};
334
+ const existingWindowLabel = settings.windowLabels[windowId] || {};
335
+ const nextWindowLabel = { ...existingWindowLabel };
336
+ if (input.label !== undefined || input.title !== undefined) {
337
+ nextWindowLabel.label = String(input.label ?? input.title);
338
+ }
339
+ if (typeof nextWindowLabel.label !== "string") {
340
+ nextWindowLabel.label = String(nextWindowLabel.label ?? "");
341
+ }
342
+ settings.windowLabels[windowId] = {
343
+ ...nextWindowLabel,
344
+ source: "mcp_metadata",
345
+ updatedAt: now
346
+ };
347
+ await chrome.storage.local.set({ sidebarSettings: settings });
348
+ return {
349
+ updated: true,
350
+ entityType: "window-label",
351
+ windowId,
352
+ audit: makeMetadataAudit({ entityType: "window-label", windowId })
353
+ };
354
+ }
355
+
356
+ if (entityType === "archive" || entityType === "task-thread") {
357
+ if (!taskThreadId) throw new Error("taskThreadId is required for archive metadata updates");
358
+ const stored = await chrome.storage.local.get("archivedGroups");
359
+ const archive = Array.isArray(stored.archivedGroups) ? stored.archivedGroups : [];
360
+ const entry = archive.find((item) => String(item.taskThreadId || item.id) === taskThreadId);
361
+ if (!entry) throw new Error("Archive entry not found: " + taskThreadId);
362
+ let changed = false;
363
+ if (input.title !== undefined) {
364
+ entry.title = String(input.title);
365
+ changed = true;
366
+ }
367
+ if (input.description !== undefined) {
368
+ entry.description = String(input.description);
369
+ changed = true;
370
+ }
371
+ if (input.status !== undefined) {
372
+ if (entry.locked) throw new Error("Cannot change status of a locked entry");
373
+ entry.status = coerceArchiveWorkflowStatusForWrite(input.status);
374
+ entry.statusUpdatedAt = now;
375
+ changed = true;
376
+ }
377
+ if (changed) entry.updatedAt = now;
378
+ await chrome.storage.local.set({ archivedGroups: archive });
379
+ return {
380
+ updated: true,
381
+ entityType: "archive",
382
+ taskThreadId,
383
+ audit: makeMetadataAudit({ entityType: "archive", taskThreadId })
384
+ };
385
+ }
386
+
387
+ if (entityType === "archive-tab") {
388
+ if (!taskThreadId) throw new Error("taskThreadId is required for archive tab metadata updates");
389
+ const tabIndex = Number(input.tabIndex);
390
+ if (!Number.isInteger(tabIndex) || tabIndex < 0) throw new Error("tabIndex is required");
391
+ const stored = await chrome.storage.local.get("archivedGroups");
392
+ const archive = Array.isArray(stored.archivedGroups) ? stored.archivedGroups : [];
393
+ const entry = archive.find((item) => String(item.taskThreadId || item.id) === taskThreadId);
394
+ if (!entry) throw new Error("Archive entry not found: " + taskThreadId);
395
+ entry.urls = Array.isArray(entry.urls) ? entry.urls : [];
396
+ if (!entry.urls[tabIndex]) throw new Error("Archive tab not found: " + tabIndex);
397
+ if (input.title !== undefined) entry.urls[tabIndex].title = String(input.title);
398
+ if (input.description !== undefined) entry.urls[tabIndex].description = String(input.description);
399
+ await chrome.storage.local.set({ archivedGroups: archive });
400
+ return {
401
+ updated: true,
402
+ entityType: "archive-tab",
403
+ taskThreadId,
404
+ tabIndex,
405
+ audit: makeMetadataAudit({ entityType: "archive-tab", taskThreadId, tabIndex })
406
+ };
407
+ }
408
+
409
+ const entityId = String(input.entityId || input.id || "");
410
+ if (!entityType || !entityId) throw new Error("entityType and entityId are required");
411
+ const stored = await chrome.storage.local.get("sessionEntityDescriptions");
412
+ const descriptions = stored.sessionEntityDescriptions || {};
413
+ const key = entityType + ":" + entityId;
414
+ descriptions[key] = {
415
+ entityType,
416
+ entityId,
417
+ description: String(input.description ?? ""),
418
+ updatedAt: now
419
+ };
420
+ await chrome.storage.local.set({ sessionEntityDescriptions: descriptions });
421
+ return {
422
+ updated: true,
423
+ entityType,
424
+ entityId,
425
+ audit: makeMetadataAudit({ entityType, entityId })
426
+ };
427
+ })()
428
+ `);
429
+ }
228
430
  /** Create a single tab and return its ID. Used by ETT restore_group. */
229
431
  async createTab(url, active = false) {
230
432
  return await this.evaluate(`
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { CodexSessionIdentity, CodexWindowBinding } from "./window-bindings.js";
1
2
  export interface CDPTarget {
2
3
  id: string;
3
4
  type: string;
@@ -41,6 +42,8 @@ export interface CometState {
41
42
  port: number;
42
43
  currentUrl?: string;
43
44
  activeTabId?: string;
45
+ profileId?: string;
46
+ profileOwner?: "agent" | "human" | "shared_legacy" | "unknown";
44
47
  }
45
48
  export interface NetworkIdleResult {
46
49
  idle: boolean;
@@ -67,4 +70,238 @@ export interface TabInfo {
67
70
  url: string;
68
71
  active: boolean;
69
72
  }
73
+ export type AgentActivityStatus = "active" | "idle" | "orphaned";
74
+ export interface ObserverAgentStatus {
75
+ agentId: string;
76
+ agentType: string;
77
+ label: string;
78
+ status: AgentActivityStatus;
79
+ lastHeartbeat: string;
80
+ lastActivity: string | null;
81
+ idleSince: string | null;
82
+ }
83
+ export interface ObserverTabInfo {
84
+ id: string;
85
+ url: string;
86
+ title: string;
87
+ loadState: "complete" | "loading" | "error";
88
+ thumbnail: string | null;
89
+ }
90
+ export interface ObserverBindingInfo {
91
+ bindingId: string;
92
+ codexSessionId: string;
93
+ projectThreadId: string;
94
+ repoSlug: string;
95
+ worktreePath: string;
96
+ branchName: string;
97
+ sessionKey: string;
98
+ role: CodexSessionIdentity["role"];
99
+ sidecarContextKey: string;
100
+ status: CodexWindowBinding["status"];
101
+ windowId: number;
102
+ tabGroupId: number | null;
103
+ targetId: string | null;
104
+ runIds: string[];
105
+ updatedAt: string;
106
+ }
107
+ export interface ObserverWindowBindingInfo {
108
+ windowId: number;
109
+ binding: ObserverBindingInfo | null;
110
+ bindingStatus: CodexWindowBinding["status"] | "unbound";
111
+ }
112
+ export interface ObserverTabGroupInfo {
113
+ id: number;
114
+ title: string;
115
+ color: string;
116
+ collapsed: boolean;
117
+ windowId: number;
118
+ tabs: ObserverTabInfo[];
119
+ tabCount: number;
120
+ agent: ObserverAgentStatus | null;
121
+ binding: ObserverBindingInfo | null;
122
+ bindingStatus: CodexWindowBinding["status"] | "unbound";
123
+ }
124
+ export interface BrowserHealth {
125
+ running: boolean;
126
+ cdpConnected: boolean;
127
+ extensionAvailable: boolean;
128
+ tabCount: number;
129
+ groupCount: number;
130
+ }
131
+ export interface BrowserSnapshot {
132
+ timestamp: string;
133
+ browser: BrowserHealth;
134
+ groups: ObserverTabGroupInfo[];
135
+ ungroupedTabs: ObserverTabInfo[];
136
+ windows: ObserverWindowBindingInfo[];
137
+ bindings: ObserverBindingInfo[];
138
+ totalTabs: number;
139
+ totalGroups: number;
140
+ }
141
+ export interface ObserverFilters {
142
+ group?: string;
143
+ agentId?: string;
144
+ urlPattern?: string;
145
+ thumbnails?: boolean;
146
+ codexIdentity?: CodexSessionIdentity;
147
+ }
148
+ export interface AgentRegistryEntry {
149
+ agentId: string;
150
+ agentType: string;
151
+ label: string;
152
+ lane: number;
153
+ status: string;
154
+ taskGroup: string | null;
155
+ tabGroupId: number | null;
156
+ tabGroupColor: string | null;
157
+ currentUrl: string | null;
158
+ taskDescription: string | null;
159
+ startedAt: string | null;
160
+ lastHeartbeat: string;
161
+ completedAt: string | null;
162
+ errorMessage: string | null;
163
+ capabilities?: string[];
164
+ }
165
+ export interface AgentRegistry {
166
+ schema: string;
167
+ lastUpdated: string;
168
+ agents: Record<string, AgentRegistryEntry>;
169
+ }
170
+ export type ConsumerRole = "orchestrator" | "subagent";
171
+ export type ErrorEventType = "PERPLEXITY_VOICE_MODE" | "PERPLEXITY_IDLE_NO_NAV" | "PERPLEXITY_CONTEXT_BLEED" | "BROWSER_CRASH" | "CRASH_LOOP_CAP" | "CDP_DISCONNECT" | "ORPHAN_DETECTED" | "ORPHAN_REAPED" | "DUPLICATE_PROCESS" | "PROTOCOL_VIOLATION";
172
+ export type ErrorSeverity = "critical" | "operational" | "warning";
173
+ export type AlertRoutingTarget = "operator-direct" | "orchestrator-first" | "orchestrator-only";
174
+ export interface DeliveryStatus {
175
+ macosNotification: boolean;
176
+ logFile: boolean;
177
+ mcpQueue: boolean;
178
+ }
179
+ export interface ErrorEvent {
180
+ id: string;
181
+ timestamp: string;
182
+ type: ErrorEventType;
183
+ severity: ErrorSeverity;
184
+ consumerId: string | null;
185
+ sessionKey: string | null;
186
+ message: string;
187
+ context: Record<string, unknown>;
188
+ suggestedAction: string;
189
+ delivered: DeliveryStatus;
190
+ }
191
+ export interface AlertRouting {
192
+ critical: AlertRoutingTarget;
193
+ operational: AlertRoutingTarget;
194
+ warning: AlertRoutingTarget;
195
+ }
196
+ export interface RoleConfig {
197
+ operatorId: string;
198
+ orchestratorAgentId: string | null;
199
+ }
200
+ export interface BridgeConfig {
201
+ version: string;
202
+ cleanup: {
203
+ orphanThresholdMinutes: number;
204
+ snapshotBeforeClose: boolean;
205
+ snapshotDir: string;
206
+ };
207
+ crashRecovery: {
208
+ maxRestarts: number;
209
+ windowMinutes: number;
210
+ autoRestart: boolean;
211
+ crashHistoryPath: string;
212
+ };
213
+ alerts: {
214
+ logPath: string;
215
+ macosNotifications: boolean;
216
+ mcpErrorQueue: boolean;
217
+ routing: AlertRouting;
218
+ };
219
+ roles: RoleConfig;
220
+ protocolEnforcement: {
221
+ requireConnectBeforeBrowse: boolean;
222
+ warnMissingTabGroup: boolean;
223
+ };
224
+ }
225
+ export interface TabSnapshot {
226
+ url: string;
227
+ title: string;
228
+ chromeTabId: number;
229
+ targetId: string;
230
+ active: boolean;
231
+ }
232
+ export interface SidecarSnapshot {
233
+ threadUrl: string;
234
+ parentTabUrl: string;
235
+ lastQuery: string | null;
236
+ }
237
+ export interface WindowMetadata {
238
+ windowId: number;
239
+ x: number;
240
+ y: number;
241
+ width: number;
242
+ height: number;
243
+ displayIndex: number;
244
+ }
245
+ export type SnapshotReason = "orphan-cleanup" | "completion" | "crash" | "manual";
246
+ export type TaskThreadStatus = "success" | "failed" | "abandoned" | "in-progress";
247
+ export interface TaskThreadSnapshot {
248
+ sessionKey: string;
249
+ agentId: string;
250
+ taskThreadId: string;
251
+ capturedAt: string;
252
+ reason: SnapshotReason;
253
+ taskStatus: TaskThreadStatus;
254
+ tabs: TabSnapshot[];
255
+ sidecarThreads: SidecarSnapshot[];
256
+ windowMetadata: WindowMetadata | null;
257
+ tabGroupId: number | null;
258
+ tabGroupColor: TabGroupColor;
259
+ }
260
+ export type SessionStatus = "active" | "disconnecting" | "orphaned";
261
+ export interface AgentSession {
262
+ agentId: string;
263
+ taskThreadId: string;
264
+ sessionKey: string;
265
+ targetId: string;
266
+ chromeTabId: number | null;
267
+ tabGroupId: number | null;
268
+ tabGroupColor: TabGroupColor;
269
+ createdAt: number;
270
+ lastActivity: number;
271
+ status: SessionStatus;
272
+ role: ConsumerRole | null;
273
+ sessionName?: string;
274
+ taskGoal?: string;
275
+ orchestratorUrl?: string;
276
+ parentSessionId?: string | null;
277
+ codexIdentity?: CodexSessionIdentity;
278
+ codexBinding?: CodexWindowBinding;
279
+ profileId?: string;
280
+ profileOwner?: "agent" | "human" | "shared_legacy" | "unknown";
281
+ profileAlias?: string | null;
282
+ }
283
+ export interface ManifestEntry {
284
+ sessionKey: string;
285
+ agentId: string;
286
+ taskThreadId: string;
287
+ targetId: string;
288
+ tabGroupId: number | null;
289
+ createdAt: number;
290
+ lastActivity: number;
291
+ pid: number;
292
+ sessionName?: string;
293
+ taskGoal?: string;
294
+ orchestratorUrl?: string;
295
+ role?: "orchestrator" | "subagent";
296
+ parentSessionId?: string | null;
297
+ codexIdentity?: CodexSessionIdentity;
298
+ codexBinding?: CodexWindowBinding;
299
+ profileId?: string;
300
+ profileOwner?: "agent" | "human" | "shared_legacy" | "unknown";
301
+ profileAlias?: string | null;
302
+ }
303
+ export interface SessionManifest {
304
+ sessions: ManifestEntry[];
305
+ lastUpdated: number;
306
+ }
70
307
  //# sourceMappingURL=types.d.ts.map