@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.
- package/README.md +97 -19
- package/dist/alert-dispatcher.d.ts +23 -0
- package/dist/alert-dispatcher.js +101 -0
- package/dist/binding-reaper.d.ts +46 -0
- package/dist/binding-reaper.js +73 -0
- package/dist/bound-session.d.ts +23 -0
- package/dist/bound-session.js +119 -0
- package/dist/bridge-config.d.ts +6 -0
- package/dist/bridge-config.js +78 -0
- package/dist/cdp-client.d.ts +40 -4
- package/dist/cdp-client.js +502 -155
- package/dist/comet-ai.d.ts +15 -0
- package/dist/comet-ai.js +114 -38
- package/dist/delegate-binding.d.ts +19 -0
- package/dist/delegate-binding.js +73 -0
- package/dist/http-server.js +2188 -47
- package/dist/index.js +3545 -788
- package/dist/observer.d.ts +47 -0
- package/dist/observer.js +516 -0
- package/dist/project-config.d.ts +46 -0
- package/dist/project-config.js +166 -0
- package/dist/session-registry.d.ts +57 -0
- package/dist/session-registry.js +500 -0
- package/dist/sidecar-artifacts.d.ts +49 -0
- package/dist/sidecar-artifacts.js +146 -0
- package/dist/snapshot-capture.d.ts +3 -0
- package/dist/snapshot-capture.js +91 -0
- package/dist/tab-group-archive.js +3 -1
- package/dist/tab-groups.d.ts +28 -1
- package/dist/tab-groups.js +205 -3
- package/dist/types.d.ts +237 -0
- package/dist/window-bindings.d.ts +160 -0
- package/dist/window-bindings.js +561 -0
- package/extension/background.js +1577 -300
- package/extension/icons/icon.svg +9 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/manifest.json +34 -4
- package/extension/perplexity-capability-manifest.json +1181 -0
- package/extension/perplexity-capability-manifest.schema.json +142 -0
- package/extension/session-logic.js +3054 -0
- package/extension/session-manager.html +311 -0
- package/extension/sidepanel.css +5338 -528
- package/extension/sidepanel.html +282 -2
- package/extension/sidepanel.js +10604 -950
- package/extension/window-policy.js +162 -0
- package/package.json +10 -7
- package/vendor/lifecycle-mcp-adapter.mjs +103 -0
- package/vendor/lifecycle-metadata.mjs +252 -0
- package/vendor/readiness-report.mjs +742 -0
- package/dist/cdp-client.d.ts.map +0 -1
- package/dist/cdp-client.js.map +0 -1
- package/dist/comet-ai.d.ts.map +0 -1
- package/dist/comet-ai.js.map +0 -1
- package/dist/http-server.d.ts.map +0 -1
- package/dist/http-server.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/tab-group-archive.d.ts.map +0 -1
- package/dist/tab-group-archive.js.map +0 -1
- package/dist/tab-groups.d.ts.map +0 -1
- package/dist/tab-groups.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- 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) => {
|
|
14
|
+
writeQueue = new Promise((r) => {
|
|
15
|
+
resolve = r;
|
|
16
|
+
});
|
|
15
17
|
return prev.then(fn).finally(() => resolve());
|
|
16
18
|
}
|
|
17
19
|
function resolveArchivePaths(archiveDir) {
|
package/dist/tab-groups.d.ts
CHANGED
|
@@ -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. */
|
package/dist/tab-groups.js
CHANGED
|
@@ -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
|
|
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
|