@shawnowen/comet-mcp 2.4.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 +12 -1
- package/dist/binding-reaper.d.ts +46 -0
- package/dist/binding-reaper.js +73 -0
- package/dist/http-server.js +121 -0
- package/dist/index.js +310 -6
- package/dist/project-config.d.ts +46 -0
- package/dist/project-config.js +166 -0
- package/dist/tab-groups.d.ts +21 -1
- package/dist/tab-groups.js +184 -0
- package/dist/window-bindings.d.ts +48 -0
- package/dist/window-bindings.js +85 -0
- package/extension/background.js +38 -17
- package/extension/manifest.json +16 -1
- package/extension/perplexity-capability-manifest.json +1181 -0
- package/extension/perplexity-capability-manifest.schema.json +142 -0
- package/extension/session-logic.js +696 -25
- package/extension/session-manager.html +13 -1
- package/extension/sidepanel.css +21 -6
- package/extension/sidepanel.js +598 -68
- package/package.json +1 -1
- package/dist/discovery/capability-entry.d.ts +0 -215
- package/dist/discovery/capability-entry.js +0 -13
- package/dist/discovery/description-template.d.ts +0 -40
- package/dist/discovery/description-template.js +0 -61
- package/dist/discovery/golden-queries.fixture.d.ts +0 -22
- package/dist/discovery/golden-queries.fixture.js +0 -137
- package/dist/discovery/mcp-source.d.ts +0 -38
- package/dist/discovery/mcp-source.js +0 -70
- package/dist/discovery/metadata-completeness.d.ts +0 -48
- package/dist/discovery/metadata-completeness.js +0 -83
- package/dist/discovery/registry.d.ts +0 -35
- package/dist/discovery/registry.js +0 -35
- package/dist/discovery/safety.d.ts +0 -44
- package/dist/discovery/safety.js +0 -59
- package/dist/discovery/schema-validator.d.ts +0 -36
- package/dist/discovery/schema-validator.js +0 -257
- package/dist/discovery/source-error.d.ts +0 -47
- package/dist/discovery/source-error.js +0 -95
- package/dist/discovery/tool-meta.d.ts +0 -41
- package/dist/discovery/tool-meta.js +0 -229
- package/dist/discovery/virtual-tools.d.ts +0 -20
- package/dist/discovery/virtual-tools.js +0 -69
- package/dist/task-thread-aggregator.d.ts +0 -34
- package/dist/task-thread-aggregator.js +0 -480
- package/dist/task-thread-canonical.d.ts +0 -142
- package/dist/task-thread-canonical.js +0 -116
package/extension/sidepanel.js
CHANGED
|
@@ -591,10 +591,6 @@ function buildWindowScopedEquanautContext({ windowId, groups = [], tabs = [] })
|
|
|
591
591
|
"When the user switches windows, discard this window context and bind only to the new active window.",
|
|
592
592
|
"Use only the tabs, tab groups, task threads, browser-session settings, and window instructions listed in this window context.",
|
|
593
593
|
"Do not infer, inspect, summarize, or act on tabs or groups from any other browser window.",
|
|
594
|
-
"For focus, display, fullscreen, or lock questions, treat host-supplied Comet diagnostics as in-scope local context.",
|
|
595
|
-
"Relevant local diagnostics include CDP session status, Comet launch/profile configuration, top-display window policy, macOS Accessibility window bounds, AXFullScreen state, launch agents, and Comet-related processes.",
|
|
596
|
-
"If those diagnostics are absent, name the exact missing diagnostic surface instead of saying the issue is outside browser scope.",
|
|
597
|
-
"Never claim that local focus or display locking is unknowable when Comet-Bridge, host shell, or macOS Accessibility diagnostics can be requested by the owning agent.",
|
|
598
594
|
"Keep responses synchronized with this active window's task threads.",
|
|
599
595
|
"",
|
|
600
596
|
`Parent window folder id: ${key || "unknown"}`,
|
|
@@ -3660,6 +3656,31 @@ function buildSidepanelGlobalContextCatalog() {
|
|
|
3660
3656
|
status: agentState.status || "unknown",
|
|
3661
3657
|
taskThreadId: taskGroup,
|
|
3662
3658
|
}));
|
|
3659
|
+
const descriptionRecords = Object.entries(state.entityDescriptions || {}).map(([id, entry]) => ({
|
|
3660
|
+
id,
|
|
3661
|
+
title: id,
|
|
3662
|
+
entityType: entry?.entityType || id.split(":")[0],
|
|
3663
|
+
entityId: entry?.entityId || id.split(":").slice(1).join(":"),
|
|
3664
|
+
description: normalizeDescriptionText(typeof entry === "string" ? entry : entry?.description),
|
|
3665
|
+
extensionOwned: true,
|
|
3666
|
+
}));
|
|
3667
|
+
const windowLabelRecords = Object.entries(state.settings?.windowLabels || {}).map(
|
|
3668
|
+
([windowId, entry]) => ({
|
|
3669
|
+
id: windowId,
|
|
3670
|
+
windowId,
|
|
3671
|
+
title: typeof entry === "string" ? entry : entry?.label || windowId,
|
|
3672
|
+
label: typeof entry === "string" ? entry : entry?.label || "",
|
|
3673
|
+
source: typeof entry === "string" ? "manual" : entry?.source || "manual",
|
|
3674
|
+
extensionOwned: true,
|
|
3675
|
+
})
|
|
3676
|
+
);
|
|
3677
|
+
const lifecycleBindings = Array.from(state.bindingWindows.entries()).map(([windowId, info]) => ({
|
|
3678
|
+
id: info?.binding?.bindingId || windowId,
|
|
3679
|
+
windowId,
|
|
3680
|
+
title: info?.binding?.codexSessionId || info?.binding?.projectThreadId || windowId,
|
|
3681
|
+
status: info?.bindingStatus || "unbound",
|
|
3682
|
+
extensionOwned: true,
|
|
3683
|
+
}));
|
|
3663
3684
|
return SessionLogic.buildGlobalContextCatalog({
|
|
3664
3685
|
liveSessions: (state.groups || []).map(buildSidepanelCatalogRecordFromGroup),
|
|
3665
3686
|
archivedSessions: state.archived || [],
|
|
@@ -3668,9 +3689,26 @@ function buildSidepanelGlobalContextCatalog() {
|
|
|
3668
3689
|
gatewayTools: [
|
|
3669
3690
|
{ id: "inspect", title: "Inspect task thread", status: "available" },
|
|
3670
3691
|
{ id: "search", title: "Search task threads", status: "available" },
|
|
3692
|
+
{ id: "update_description", title: "Update metadata description", status: "available" },
|
|
3693
|
+
{ id: "bulk_update_descriptions", title: "Bulk update descriptions", status: "available" },
|
|
3694
|
+
{ id: "rename_window", title: "Rename window label", status: "available" },
|
|
3695
|
+
{ id: "rename_group", title: "Rename tab group", status: "available" },
|
|
3696
|
+
{ id: "rename_tab", title: "Rename archived tab row", status: "available" },
|
|
3697
|
+
{ id: "rename_archive", title: "Rename archived task thread", status: "available" },
|
|
3698
|
+
{ id: "update_task_status", title: "Update task-thread status", status: "available" },
|
|
3699
|
+
{ id: "save_snapshot", title: "Save group snapshot", status: "available" },
|
|
3700
|
+
{
|
|
3701
|
+
id: "manage_selected_item",
|
|
3702
|
+
title: "Manage full-screen selected items",
|
|
3703
|
+
status: "available",
|
|
3704
|
+
},
|
|
3671
3705
|
{ id: "dispatch_to_fleet", title: "Dispatch to fleet", status: "gateway_required" },
|
|
3672
3706
|
{ id: "get_fleet_status", title: "Get fleet status", status: "available" },
|
|
3673
3707
|
],
|
|
3708
|
+
selectedItems: getPrimeSelectedItemCatalog(),
|
|
3709
|
+
lifecycleBindings,
|
|
3710
|
+
windowLabels: windowLabelRecords,
|
|
3711
|
+
descriptions: descriptionRecords,
|
|
3674
3712
|
slashCommands: getEquanautSlashCommands(),
|
|
3675
3713
|
plugins: getEquanautPlugins(),
|
|
3676
3714
|
});
|
|
@@ -3744,7 +3782,7 @@ function formatRouterCatalogForPrompt(envelope) {
|
|
|
3744
3782
|
"[Equanaut Global Read-Only Catalog]",
|
|
3745
3783
|
"Use this catalog for search and summaries across saved task threads, archived sessions, recently closed sessions, and fleet state.",
|
|
3746
3784
|
"Do not mutate catalog records unless a router intent validates the target against active-window and profile policy.",
|
|
3747
|
-
`Counts: live=${counts.live || 0}, archived=${counts.archived || 0}, recent=${counts.recent || 0}, unified=${counts.unified || 0}, fleet=${counts.fleet || 0}, gatewayTools=${counts.gatewayTools || 0}, slashCommands=${counts.slashCommands || 0}, plugins=${counts.plugins || 0}`,
|
|
3785
|
+
`Counts: live=${counts.live || 0}, archived=${counts.archived || 0}, recent=${counts.recent || 0}, unified=${counts.unified || 0}, fleet=${counts.fleet || 0}, gatewayTools=${counts.gatewayTools || 0}, selectedItems=${counts.selectedItems || 0}, lifecycleBindings=${counts.lifecycleBindings || 0}, windowLabels=${counts.windowLabels || 0}, descriptions=${counts.descriptions || 0}, slashCommands=${counts.slashCommands || 0}, plugins=${counts.plugins || 0}`,
|
|
3748
3786
|
];
|
|
3749
3787
|
const sections = [
|
|
3750
3788
|
["Live", catalog.liveSessions || []],
|
|
@@ -3752,6 +3790,11 @@ function formatRouterCatalogForPrompt(envelope) {
|
|
|
3752
3790
|
["Recently Closed", catalog.recentlyClosed || []],
|
|
3753
3791
|
["Unified Threads", catalog.unifiedThreads || []],
|
|
3754
3792
|
["Fleet", catalog.fleetMembers || []],
|
|
3793
|
+
["Gateway Tools", catalog.gatewayTools || []],
|
|
3794
|
+
["Selected Items", catalog.selectedItems || []],
|
|
3795
|
+
["Lifecycle Bindings", catalog.lifecycleBindings || []],
|
|
3796
|
+
["Window Labels", catalog.windowLabels || []],
|
|
3797
|
+
["Descriptions", catalog.descriptions || []],
|
|
3755
3798
|
["Slash Commands", catalog.slashCommands || []],
|
|
3756
3799
|
["Plugins", catalog.plugins || []],
|
|
3757
3800
|
];
|
|
@@ -4249,12 +4292,13 @@ function createArchiveEntryNode(entry) {
|
|
|
4249
4292
|
const isExpanded = state.expandedGroups.has(archiveKey);
|
|
4250
4293
|
const urls = entry.urls || [];
|
|
4251
4294
|
const archiveDescription = normalizeRecordDescription(entry);
|
|
4295
|
+
const status = fsWorkflowStatus(entry);
|
|
4252
4296
|
|
|
4253
4297
|
const stateClasses = [
|
|
4254
4298
|
entry.starred ? "starred" : "",
|
|
4255
4299
|
entry.locked ? "locked" : "",
|
|
4256
4300
|
entry.pinned ? "pinned" : "",
|
|
4257
|
-
|
|
4301
|
+
status === "pending" ? "status-pending" : "",
|
|
4258
4302
|
]
|
|
4259
4303
|
.filter(Boolean)
|
|
4260
4304
|
.join(" ");
|
|
@@ -4329,7 +4373,7 @@ function createArchiveEntryNode(entry) {
|
|
|
4329
4373
|
indicators.push(el("span", { className: "archive-indicator", title: "Locked" }, "🔒"));
|
|
4330
4374
|
if (entry.pinned)
|
|
4331
4375
|
indicators.push(el("span", { className: "archive-indicator", title: "Pinned" }, "📌"));
|
|
4332
|
-
if (
|
|
4376
|
+
if (status === "pending")
|
|
4333
4377
|
indicators.push(el("span", { className: "archive-indicator status-badge pending" }, "Pending"));
|
|
4334
4378
|
if (archiveDescription)
|
|
4335
4379
|
indicators.push(
|
|
@@ -5075,9 +5119,9 @@ function showArchiveContextMenu(e, entry) {
|
|
|
5075
5119
|
action: () => updateStatusAction(entry.taskThreadId, "pending"),
|
|
5076
5120
|
},
|
|
5077
5121
|
{
|
|
5078
|
-
label: "Mark as
|
|
5079
|
-
icon: "
|
|
5080
|
-
action: () => updateStatusAction(entry.taskThreadId, "
|
|
5122
|
+
label: "Mark as done",
|
|
5123
|
+
icon: "✅",
|
|
5124
|
+
action: () => updateStatusAction(entry.taskThreadId, "done"),
|
|
5081
5125
|
},
|
|
5082
5126
|
{
|
|
5083
5127
|
label: entry.pinned ? "Unpin" : "Pin to top",
|
|
@@ -5975,6 +6019,213 @@ function isFullscreenMode() {
|
|
|
5975
6019
|
);
|
|
5976
6020
|
}
|
|
5977
6021
|
|
|
6022
|
+
// Figma node 5:27 "Header Menu" mapped into the full-page extension shell.
|
|
6023
|
+
const FS_HOME_ROUTE = "home";
|
|
6024
|
+
const FS_DASHBOARD_BASE_URL = "http://localhost:3847/";
|
|
6025
|
+
const FS_NAVIGATION_ITEMS = Object.freeze([
|
|
6026
|
+
{
|
|
6027
|
+
route: "home",
|
|
6028
|
+
label: "Home",
|
|
6029
|
+
icon: "⌂",
|
|
6030
|
+
kind: "internal",
|
|
6031
|
+
description: "Return to the full-page session manager home.",
|
|
6032
|
+
},
|
|
6033
|
+
{
|
|
6034
|
+
route: "capabilities",
|
|
6035
|
+
label: "Capabilities",
|
|
6036
|
+
icon: "⚙",
|
|
6037
|
+
kind: "dashboard",
|
|
6038
|
+
dashboardHash: "capabilities",
|
|
6039
|
+
description: "Open the unified capabilities dashboard.",
|
|
6040
|
+
},
|
|
6041
|
+
{
|
|
6042
|
+
route: "live-browser",
|
|
6043
|
+
label: "Live Browser",
|
|
6044
|
+
icon: "⚡",
|
|
6045
|
+
kind: "dashboard",
|
|
6046
|
+
dashboardHash: "live",
|
|
6047
|
+
description: "Open the live browser dashboard.",
|
|
6048
|
+
},
|
|
6049
|
+
{
|
|
6050
|
+
route: "sessions",
|
|
6051
|
+
label: "Sessions",
|
|
6052
|
+
icon: "📡",
|
|
6053
|
+
kind: "dashboard",
|
|
6054
|
+
dashboardHash: "sessions",
|
|
6055
|
+
description: "Open the unified sessions dashboard.",
|
|
6056
|
+
},
|
|
6057
|
+
{
|
|
6058
|
+
route: "dispatch",
|
|
6059
|
+
label: "Dispatch",
|
|
6060
|
+
icon: "↗",
|
|
6061
|
+
kind: "dashboard",
|
|
6062
|
+
dashboardHash: "dispatch",
|
|
6063
|
+
description: "Open the dispatch view.",
|
|
6064
|
+
},
|
|
6065
|
+
{
|
|
6066
|
+
route: "test-suite",
|
|
6067
|
+
label: "Test Suite",
|
|
6068
|
+
icon: "⌬",
|
|
6069
|
+
kind: "dashboard",
|
|
6070
|
+
dashboardHash: "tests",
|
|
6071
|
+
description: "Open the test suite dashboard.",
|
|
6072
|
+
},
|
|
6073
|
+
{
|
|
6074
|
+
route: "window-map",
|
|
6075
|
+
label: "Window Map",
|
|
6076
|
+
icon: "▦",
|
|
6077
|
+
kind: "dashboard",
|
|
6078
|
+
dashboardHash: "mapping",
|
|
6079
|
+
description: "Open the managed window map.",
|
|
6080
|
+
},
|
|
6081
|
+
{
|
|
6082
|
+
route: "task-threads",
|
|
6083
|
+
label: "Task Threads",
|
|
6084
|
+
icon: "▤",
|
|
6085
|
+
kind: "dashboard",
|
|
6086
|
+
dashboardHash: "threads",
|
|
6087
|
+
description: "Open the unified taskthreads dashboard.",
|
|
6088
|
+
},
|
|
6089
|
+
{
|
|
6090
|
+
route: "skill-reference",
|
|
6091
|
+
label: "Skill Reference",
|
|
6092
|
+
icon: "☰",
|
|
6093
|
+
kind: "docs",
|
|
6094
|
+
href: "https://github.com/EQUAStart/equa-comet-browser-control/blob/main/docs/SKILL-ULTIMATE.md",
|
|
6095
|
+
description: "Open the skill reference documentation.",
|
|
6096
|
+
},
|
|
6097
|
+
]);
|
|
6098
|
+
|
|
6099
|
+
function fsGetNavigationItem(route) {
|
|
6100
|
+
return FS_NAVIGATION_ITEMS.find((item) => item.route === route);
|
|
6101
|
+
}
|
|
6102
|
+
|
|
6103
|
+
function fsReadFullscreenRoute() {
|
|
6104
|
+
const params = new URLSearchParams(window.location.search);
|
|
6105
|
+
const route = params.get("route") || window.location.hash.replace("#", "");
|
|
6106
|
+
return fsGetNavigationItem(route) ? route : FS_HOME_ROUTE;
|
|
6107
|
+
}
|
|
6108
|
+
|
|
6109
|
+
function fsBuildNavigationUrl(item) {
|
|
6110
|
+
if (item.kind === "dashboard") {
|
|
6111
|
+
const url = new URL(FS_DASHBOARD_BASE_URL);
|
|
6112
|
+
url.searchParams.set("returnUrl", fsBuildNavigationUrl(fsGetNavigationItem(FS_HOME_ROUTE)));
|
|
6113
|
+
url.hash = item.dashboardHash;
|
|
6114
|
+
return url.toString();
|
|
6115
|
+
}
|
|
6116
|
+
if (item.kind === "docs") return item.href;
|
|
6117
|
+
|
|
6118
|
+
const url = new URL(window.location.href);
|
|
6119
|
+
url.searchParams.set("mode", "fullscreen");
|
|
6120
|
+
url.searchParams.set("route", item.route);
|
|
6121
|
+
url.hash = "";
|
|
6122
|
+
return url.toString();
|
|
6123
|
+
}
|
|
6124
|
+
|
|
6125
|
+
function fsOpenNavigationUrl(url) {
|
|
6126
|
+
if (globalThis.chrome?.tabs?.create) {
|
|
6127
|
+
globalThis.chrome.tabs.create({ url });
|
|
6128
|
+
return;
|
|
6129
|
+
}
|
|
6130
|
+
window.open(url, "_blank", "noopener");
|
|
6131
|
+
}
|
|
6132
|
+
|
|
6133
|
+
function fsScrollAndFocus(element) {
|
|
6134
|
+
if (!element) return;
|
|
6135
|
+
element.scrollIntoView({ block: "start", behavior: "smooth" });
|
|
6136
|
+
if (!element.hasAttribute("tabindex")) element.setAttribute("tabindex", "-1");
|
|
6137
|
+
element.focus({ preventScroll: true });
|
|
6138
|
+
}
|
|
6139
|
+
|
|
6140
|
+
function fsSetFullscreenRoute(route) {
|
|
6141
|
+
const item = fsGetNavigationItem(route) || fsGetNavigationItem(FS_HOME_ROUTE);
|
|
6142
|
+
if (!item || item.kind !== "internal") return;
|
|
6143
|
+
|
|
6144
|
+
const url = new URL(window.location.href);
|
|
6145
|
+
url.searchParams.set("mode", "fullscreen");
|
|
6146
|
+
url.searchParams.set("route", item.route);
|
|
6147
|
+
url.hash = "";
|
|
6148
|
+
window.history.replaceState({ route: item.route }, "", url);
|
|
6149
|
+
fsApplyFullscreenRoute(item.route);
|
|
6150
|
+
}
|
|
6151
|
+
|
|
6152
|
+
function fsApplyFullscreenRoute(route = fsReadFullscreenRoute()) {
|
|
6153
|
+
const item = fsGetNavigationItem(route) || fsGetNavigationItem(FS_HOME_ROUTE);
|
|
6154
|
+
if (!item || item.kind !== "internal") return;
|
|
6155
|
+
|
|
6156
|
+
document.body.dataset.fullscreenRoute = item.route;
|
|
6157
|
+
|
|
6158
|
+
if (item.route === "home") {
|
|
6159
|
+
const scroll = document.getElementById("fs-main-scroll");
|
|
6160
|
+
const search = document.getElementById("fs-search-input");
|
|
6161
|
+
if (scroll) scroll.scrollTo({ top: 0, behavior: "smooth" });
|
|
6162
|
+
if (search) search.focus({ preventScroll: true });
|
|
6163
|
+
return;
|
|
6164
|
+
}
|
|
6165
|
+
|
|
6166
|
+
if (item.route === "sessions") {
|
|
6167
|
+
fsScrollAndFocus(document.getElementById("fs-groups-list"));
|
|
6168
|
+
return;
|
|
6169
|
+
}
|
|
6170
|
+
|
|
6171
|
+
if (item.route === "task-threads") {
|
|
6172
|
+
fsScrollAndFocus(document.getElementById("fs-tasks-panel"));
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
|
|
6176
|
+
function fsNavigateFromMenu(item) {
|
|
6177
|
+
if (!item) return;
|
|
6178
|
+
if (item.kind === "internal") {
|
|
6179
|
+
fsSetFullscreenRoute(item.route);
|
|
6180
|
+
return;
|
|
6181
|
+
}
|
|
6182
|
+
fsOpenNavigationUrl(fsBuildNavigationUrl(item));
|
|
6183
|
+
}
|
|
6184
|
+
|
|
6185
|
+
function fsInitNavigationMenu() {
|
|
6186
|
+
const navBtn = document.getElementById("fs-btn-navigation");
|
|
6187
|
+
if (!navBtn) return;
|
|
6188
|
+
|
|
6189
|
+
navBtn.addEventListener("click", (event) => {
|
|
6190
|
+
event.stopPropagation();
|
|
6191
|
+
const activeRoute = fsReadFullscreenRoute();
|
|
6192
|
+
navBtn.setAttribute("aria-expanded", "true");
|
|
6193
|
+
|
|
6194
|
+
showContextMenu(
|
|
6195
|
+
navBtn,
|
|
6196
|
+
FS_NAVIGATION_ITEMS.map((item, index) => ({
|
|
6197
|
+
icon: item.icon,
|
|
6198
|
+
label: item.label,
|
|
6199
|
+
active: item.kind === "internal" && item.route === activeRoute,
|
|
6200
|
+
action: () => {
|
|
6201
|
+
navBtn.setAttribute("aria-expanded", "false");
|
|
6202
|
+
fsNavigateFromMenu(item);
|
|
6203
|
+
},
|
|
6204
|
+
divider: false,
|
|
6205
|
+
}))
|
|
6206
|
+
);
|
|
6207
|
+
|
|
6208
|
+
requestAnimationFrame(() => {
|
|
6209
|
+
const menu = document.getElementById("fs-context-menu");
|
|
6210
|
+
if (!menu) return;
|
|
6211
|
+
menu.setAttribute("aria-label", "Full-screen navigation");
|
|
6212
|
+
menu.querySelectorAll(".fs-context-menu-item").forEach((el, index) => {
|
|
6213
|
+
const item = FS_NAVIGATION_ITEMS[index];
|
|
6214
|
+
if (!item) return;
|
|
6215
|
+
el.dataset.route = item.route;
|
|
6216
|
+
el.title = item.description || item.label;
|
|
6217
|
+
if (index === 0) el.classList.add("fs-nav-home-link");
|
|
6218
|
+
});
|
|
6219
|
+
});
|
|
6220
|
+
|
|
6221
|
+
const resetExpanded = () => navBtn.setAttribute("aria-expanded", "false");
|
|
6222
|
+
setTimeout(() => {
|
|
6223
|
+
document.addEventListener("click", resetExpanded, { once: true });
|
|
6224
|
+
document.addEventListener("keydown", resetExpanded, { once: true });
|
|
6225
|
+
}, 0);
|
|
6226
|
+
});
|
|
6227
|
+
}
|
|
6228
|
+
|
|
5978
6229
|
// ─── Reusable Components (T006-T008) ────────────────────────────────────────
|
|
5979
6230
|
|
|
5980
6231
|
// T007: Toast notification
|
|
@@ -6147,7 +6398,7 @@ function fsRenderSidebar(groups) {
|
|
|
6147
6398
|
totalTabs += tabCount;
|
|
6148
6399
|
const groupId = group.taskThreadId || group.id;
|
|
6149
6400
|
const isExpanded = fsSidebarExpanded.has(groupId);
|
|
6150
|
-
const status = group
|
|
6401
|
+
const status = fsWorkflowStatus(group);
|
|
6151
6402
|
|
|
6152
6403
|
const li = document.createElement("li");
|
|
6153
6404
|
li.className = "fs-sidebar-item";
|
|
@@ -6333,7 +6584,7 @@ function fsSidebarContextMenu(e, groupId, group) {
|
|
|
6333
6584
|
const isLocked = fsLockedGroups.has(groupId);
|
|
6334
6585
|
const isStarred = group.starred;
|
|
6335
6586
|
const isPinned = group.pinned;
|
|
6336
|
-
const status = group
|
|
6587
|
+
const status = fsWorkflowStatus(group);
|
|
6337
6588
|
|
|
6338
6589
|
// Create a temporary trigger at mouse position
|
|
6339
6590
|
const trigger = document.createElement("div");
|
|
@@ -6398,12 +6649,6 @@ function fsSidebarContextMenu(e, groupId, group) {
|
|
|
6398
6649
|
action: () => fsUpdateStatus(groupId, "done"),
|
|
6399
6650
|
active: status === "done",
|
|
6400
6651
|
},
|
|
6401
|
-
{
|
|
6402
|
-
icon: "\uD83D\uDCE6",
|
|
6403
|
-
label: "Mark as archived",
|
|
6404
|
-
action: () => fsUpdateStatus(groupId, "archived"),
|
|
6405
|
-
active: status === "archived",
|
|
6406
|
-
},
|
|
6407
6652
|
{ divider: true },
|
|
6408
6653
|
{ icon: "\uD83D\uDD17", label: "Copy URLs", action: () => fsShareAsUrl(group) },
|
|
6409
6654
|
{ icon: "\uD83D\uDCCB", label: "Export to text", action: () => fsExportToText(group) },
|
|
@@ -6461,8 +6706,9 @@ function fsRenderGroups(groups) {
|
|
|
6461
6706
|
card.className = "fs-group-card";
|
|
6462
6707
|
if (fsSelectedGroups.has(String(groupId))) card.classList.add("selected");
|
|
6463
6708
|
if (fsIsGroupStale && fsIsGroupStale(group)) card.classList.add("stale");
|
|
6464
|
-
|
|
6465
|
-
if (
|
|
6709
|
+
const status = fsWorkflowStatus(group);
|
|
6710
|
+
if (status === "pending") card.classList.add("status-pending");
|
|
6711
|
+
if (status === "done") card.classList.add("status-done");
|
|
6466
6712
|
card.id = "fs-group-" + groupId;
|
|
6467
6713
|
|
|
6468
6714
|
const tabs = group.urls || group.tabs || [];
|
|
@@ -6645,6 +6891,10 @@ async function fsSaveLockedGroups() {
|
|
|
6645
6891
|
await chrome.storage.local.set({ lockedGroups: [...fsLockedGroups] });
|
|
6646
6892
|
}
|
|
6647
6893
|
|
|
6894
|
+
function fsWorkflowStatus(group) {
|
|
6895
|
+
return SessionLogic.normalizeArchiveWorkflowStatus(group?.status);
|
|
6896
|
+
}
|
|
6897
|
+
|
|
6648
6898
|
// ─── Full-Screen Data Loading ───────────────────────────────────────────────
|
|
6649
6899
|
|
|
6650
6900
|
async function fsLoadAndRender() {
|
|
@@ -6960,14 +7210,9 @@ async function fsRenderTasksPanel(groups) {
|
|
|
6960
7210
|
const agents = await fsFetchAgentRegistryForPanel();
|
|
6961
7211
|
fsTaskthreadsState.agentRegistry = agents;
|
|
6962
7212
|
|
|
6963
|
-
const
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
);
|
|
6967
|
-
const done = fsSortGroups(
|
|
6968
|
-
groups.filter((g) => g.status === "done"),
|
|
6969
|
-
fsTaskthreadsState.sortDone
|
|
6970
|
-
);
|
|
7213
|
+
const taskGroups = SessionLogic.fsPartitionTaskthreadGroups(groups);
|
|
7214
|
+
const pending = fsSortGroups(taskGroups.pending, fsTaskthreadsState.sortPending);
|
|
7215
|
+
const done = fsSortGroups(taskGroups.done, fsTaskthreadsState.sortDone);
|
|
6971
7216
|
|
|
6972
7217
|
if (countEl) countEl.textContent = String(pending.length + done.length);
|
|
6973
7218
|
if (pendingCountEl) pendingCountEl.textContent = String(pending.length);
|
|
@@ -7263,7 +7508,8 @@ function fsUpdateBulkBar() {
|
|
|
7263
7508
|
);
|
|
7264
7509
|
fsAddBulkButton("Restore", () => fsRestoreGroup(groupId));
|
|
7265
7510
|
fsAddBulkButton("Copy URLs", () => fsShareAsUrl(group));
|
|
7266
|
-
fsAddBulkButton("Mark as
|
|
7511
|
+
fsAddBulkButton("Mark as pending", () => fsUpdateStatus(groupId, "pending"));
|
|
7512
|
+
fsAddBulkButton("Mark as done", () => fsUpdateStatus(groupId, "done"));
|
|
7267
7513
|
fsAddBulkButton("Move to trash", () => fsDeleteGroup(groupId), { danger: true });
|
|
7268
7514
|
} else if (tabCount === 1 && groupCount === 0 && selectedTabs[0]) {
|
|
7269
7515
|
const selected = selectedTabs[0];
|
|
@@ -7278,13 +7524,13 @@ function fsUpdateBulkBar() {
|
|
|
7278
7524
|
if (groupCount) {
|
|
7279
7525
|
fsAddBulkButton("Restore groups", fsBulkRestoreGroups);
|
|
7280
7526
|
fsAddBulkButton("Copy URLs", fsBulkCopyGroups);
|
|
7281
|
-
fsAddBulkButton("Mark as
|
|
7527
|
+
fsAddBulkButton("Mark as pending", () => fsBulkUpdateGroupStatus("pending"));
|
|
7528
|
+
fsAddBulkButton("Mark as done", () => fsBulkUpdateGroupStatus("done"));
|
|
7282
7529
|
fsAddBulkButton("Move groups to trash", fsBulkTrashGroups, { danger: true });
|
|
7283
7530
|
}
|
|
7284
7531
|
if (tabCount) {
|
|
7285
7532
|
fsAddBulkButton("Restore tabs", fsBulkRestore);
|
|
7286
7533
|
fsAddBulkButton("Copy URLs", fsBulkCopy);
|
|
7287
|
-
fsAddBulkButton("Mark groups archived", fsBulkArchive);
|
|
7288
7534
|
fsAddBulkButton("Move tabs to trash", fsBulkTrash, { danger: true });
|
|
7289
7535
|
}
|
|
7290
7536
|
}
|
|
@@ -7358,6 +7604,301 @@ function fsResolveSelectedTabs() {
|
|
|
7358
7604
|
return SessionLogic.fsResolveSelectedTabs(fsSelectedTabs, fsCachedGroups);
|
|
7359
7605
|
}
|
|
7360
7606
|
|
|
7607
|
+
function getPrimeSelectedItemCatalog() {
|
|
7608
|
+
const groups = fsResolveSelectedGroups().map((group) => ({
|
|
7609
|
+
id: String(group.taskThreadId || group.id),
|
|
7610
|
+
entityType: "archive",
|
|
7611
|
+
taskThreadId: String(group.taskThreadId || group.id),
|
|
7612
|
+
title: group.sessionName || group.title || "Untitled",
|
|
7613
|
+
description: normalizeRecordDescription(group),
|
|
7614
|
+
status: group.status || "unknown",
|
|
7615
|
+
sourceFamily: "selected",
|
|
7616
|
+
extensionOwned: true,
|
|
7617
|
+
}));
|
|
7618
|
+
const tabs = fsResolveSelectedTabs().map((selected) => ({
|
|
7619
|
+
id: `${selected.groupId}-${selected.tabIdx}`,
|
|
7620
|
+
entityType: "archive-tab",
|
|
7621
|
+
taskThreadId: String(selected.groupId),
|
|
7622
|
+
tabIndex: selected.tabIdx,
|
|
7623
|
+
title: selected.tab?.title || selected.tab?.url || "Untitled",
|
|
7624
|
+
url: selected.tab?.url || "",
|
|
7625
|
+
description: normalizeRecordDescription(selected.tab),
|
|
7626
|
+
sourceFamily: "selected",
|
|
7627
|
+
extensionOwned: true,
|
|
7628
|
+
}));
|
|
7629
|
+
return [...groups, ...tabs];
|
|
7630
|
+
}
|
|
7631
|
+
|
|
7632
|
+
function primeCommandText(command, ...keys) {
|
|
7633
|
+
for (const key of keys) {
|
|
7634
|
+
if (command?.[key] !== undefined && command?.[key] !== null) {
|
|
7635
|
+
return normalizeDescriptionText(command[key]);
|
|
7636
|
+
}
|
|
7637
|
+
}
|
|
7638
|
+
return "";
|
|
7639
|
+
}
|
|
7640
|
+
|
|
7641
|
+
function primeTargetTaskThreadId(target) {
|
|
7642
|
+
return String(target?.taskThreadId || target?.threadId || target?.entityId || target?.id || "");
|
|
7643
|
+
}
|
|
7644
|
+
|
|
7645
|
+
function primeTargetTabIndex(target) {
|
|
7646
|
+
const index = Number(target?.tabIndex ?? target?.tabIdx ?? target?.index);
|
|
7647
|
+
return Number.isInteger(index) && index >= 0 ? index : -1;
|
|
7648
|
+
}
|
|
7649
|
+
|
|
7650
|
+
function buildPrimeValidationScope(command) {
|
|
7651
|
+
if (command?.activeWindowScope) return command.activeWindowScope;
|
|
7652
|
+
const windowScope = {
|
|
7653
|
+
windowId: state.activeWindowId || command?.target?.windowId || "unknown",
|
|
7654
|
+
};
|
|
7655
|
+
return buildActiveWindowRouterScope(
|
|
7656
|
+
windowScope,
|
|
7657
|
+
getWindowEquanautInstructions(windowScope.windowId)
|
|
7658
|
+
);
|
|
7659
|
+
}
|
|
7660
|
+
|
|
7661
|
+
function validatePrimeCommand(command) {
|
|
7662
|
+
const payload = {
|
|
7663
|
+
...command,
|
|
7664
|
+
actorRole: command?.actorRole || "equanaut_prime",
|
|
7665
|
+
activeWindowScope: buildPrimeValidationScope(command),
|
|
7666
|
+
};
|
|
7667
|
+
return SessionLogic.validateRouterIntent(payload);
|
|
7668
|
+
}
|
|
7669
|
+
|
|
7670
|
+
async function loadPrimeArchiveGroup(taskThreadId) {
|
|
7671
|
+
const groups = await sendMessage("getArchivedGroups");
|
|
7672
|
+
return (groups || []).find((group) => String(group.taskThreadId || group.id) === taskThreadId);
|
|
7673
|
+
}
|
|
7674
|
+
|
|
7675
|
+
async function updatePrimeArchiveTabMetadata(target, updates) {
|
|
7676
|
+
const taskThreadId = primeTargetTaskThreadId(target);
|
|
7677
|
+
const tabIndex = primeTargetTabIndex(target);
|
|
7678
|
+
if (!taskThreadId) throw new Error("taskThreadId is required for archive tab updates");
|
|
7679
|
+
if (tabIndex < 0) throw new Error("tabIndex is required for archive tab updates");
|
|
7680
|
+
const group = await loadPrimeArchiveGroup(taskThreadId);
|
|
7681
|
+
if (!group) throw new Error("Archive group not found: " + taskThreadId);
|
|
7682
|
+
const urls = [...(group.urls || group.tabs || [])];
|
|
7683
|
+
if (!urls[tabIndex]) throw new Error("Archive tab not found: " + tabIndex);
|
|
7684
|
+
urls[tabIndex] = { ...urls[tabIndex], ...updates };
|
|
7685
|
+
await sendMessage("updateArchiveEntry", { taskThreadId, updates: { urls } });
|
|
7686
|
+
return { taskThreadId, tabIndex, updates };
|
|
7687
|
+
}
|
|
7688
|
+
|
|
7689
|
+
async function executePrimeDescriptionUpdate(command) {
|
|
7690
|
+
const target = command.target || {};
|
|
7691
|
+
const entityType = String(target.entityType || target.type || "");
|
|
7692
|
+
const description = primeCommandText(command, "description", "value", "text");
|
|
7693
|
+
if (entityType === "archive-tab" || target.tabIndex !== undefined) {
|
|
7694
|
+
return await updatePrimeArchiveTabMetadata(target, { description });
|
|
7695
|
+
}
|
|
7696
|
+
if (entityType === "archive" || target.sourceFamily === "archived") {
|
|
7697
|
+
const taskThreadId = primeTargetTaskThreadId(target);
|
|
7698
|
+
if (!taskThreadId) throw new Error("taskThreadId is required for archive descriptions");
|
|
7699
|
+
await sendMessage("updateArchiveDescription", { taskThreadId, description });
|
|
7700
|
+
return { taskThreadId, description };
|
|
7701
|
+
}
|
|
7702
|
+
const entityId =
|
|
7703
|
+
target.entityId || target.id || target.windowId || target.groupId || target.tabId;
|
|
7704
|
+
if (!entityType || entityId === undefined || entityId === null) {
|
|
7705
|
+
throw new Error("entityType and entityId are required for description updates");
|
|
7706
|
+
}
|
|
7707
|
+
await persistEntityDescription(entityType, entityId, description);
|
|
7708
|
+
return { entityType, entityId: String(entityId), description };
|
|
7709
|
+
}
|
|
7710
|
+
|
|
7711
|
+
async function executePrimeRename(command) {
|
|
7712
|
+
const target = command.target || {};
|
|
7713
|
+
const intent = String(command.intent || "");
|
|
7714
|
+
const entityType = String(target.entityType || target.type || "");
|
|
7715
|
+
const title = primeCommandText(command, "newTitle", "title", "label", "name");
|
|
7716
|
+
if (!title.trim()) throw new Error("New title or label is required");
|
|
7717
|
+
|
|
7718
|
+
if (intent === "rename_window" || entityType === "window" || entityType === "window-label") {
|
|
7719
|
+
const windowId = target.windowId || target.entityId || target.id;
|
|
7720
|
+
if (windowId === undefined || windowId === null) throw new Error("windowId is required");
|
|
7721
|
+
await persistWindowLabel(windowId, title, "manual");
|
|
7722
|
+
return { windowId: String(windowId), label: title };
|
|
7723
|
+
}
|
|
7724
|
+
|
|
7725
|
+
if (intent === "rename_archive" || entityType === "archive") {
|
|
7726
|
+
const taskThreadId = primeTargetTaskThreadId(target);
|
|
7727
|
+
if (!taskThreadId) throw new Error("taskThreadId is required for archive rename");
|
|
7728
|
+
await sendMessage("renameArchived", { taskThreadId, newTitle: title });
|
|
7729
|
+
return { taskThreadId, title };
|
|
7730
|
+
}
|
|
7731
|
+
|
|
7732
|
+
if (intent === "rename_tab" || entityType === "archive-tab") {
|
|
7733
|
+
if (target.sourceFamily === "live" && entityType !== "archive-tab") {
|
|
7734
|
+
throw new Error("Live tab titles are browser-owned; update the tab description instead");
|
|
7735
|
+
}
|
|
7736
|
+
return await updatePrimeArchiveTabMetadata(target, { title });
|
|
7737
|
+
}
|
|
7738
|
+
|
|
7739
|
+
const groupId = target.groupId || target.entityId || target.id;
|
|
7740
|
+
if (groupId === undefined || groupId === null) throw new Error("groupId is required");
|
|
7741
|
+
if (!chrome.tabGroups?.update) throw new Error("tabGroups API unavailable");
|
|
7742
|
+
await chrome.tabGroups.update(Number(groupId), { title });
|
|
7743
|
+
return { groupId: String(groupId), title };
|
|
7744
|
+
}
|
|
7745
|
+
|
|
7746
|
+
async function executePrimeTaskStatus(command) {
|
|
7747
|
+
const target = command.target || {};
|
|
7748
|
+
const taskThreadId = primeTargetTaskThreadId(target);
|
|
7749
|
+
const status = primeCommandText(command, "status", "taskStatus", "value");
|
|
7750
|
+
if (!taskThreadId) throw new Error("taskThreadId is required for status update");
|
|
7751
|
+
if (!status) throw new Error("status is required");
|
|
7752
|
+
await sendMessage("updateArchiveStatus", { taskThreadId, status });
|
|
7753
|
+
return { taskThreadId, status };
|
|
7754
|
+
}
|
|
7755
|
+
|
|
7756
|
+
async function executePrimeSaveSnapshot(command) {
|
|
7757
|
+
const target = command.target || {};
|
|
7758
|
+
const groupId = target.groupId || target.entityId || target.id;
|
|
7759
|
+
if (groupId === undefined || groupId === null) throw new Error("groupId is required");
|
|
7760
|
+
await saveGroupSnapshotAction(Number(groupId), target.title || command.title || "");
|
|
7761
|
+
return { groupId: String(groupId), saved: true };
|
|
7762
|
+
}
|
|
7763
|
+
|
|
7764
|
+
async function executePrimeSelectedItemCommand(command) {
|
|
7765
|
+
const action = String(command.action || command.selectedAction || "list").toLowerCase();
|
|
7766
|
+
const selected = getPrimeSelectedItemCatalog();
|
|
7767
|
+
if (action === "clear_selection" || action === "clear") {
|
|
7768
|
+
fsClearSelections();
|
|
7769
|
+
return { selectedItems: [], cleared: true };
|
|
7770
|
+
}
|
|
7771
|
+
if (action === "list" || selected.length === 0) {
|
|
7772
|
+
return { selectedItems: selected };
|
|
7773
|
+
}
|
|
7774
|
+
|
|
7775
|
+
const results = [];
|
|
7776
|
+
for (const item of selected) {
|
|
7777
|
+
const target = { ...item, sourceFamily: "selected", extensionOwned: true };
|
|
7778
|
+
if (action === "update_description" || action === "describe") {
|
|
7779
|
+
results.push(
|
|
7780
|
+
await executePrimeRouterIntent({
|
|
7781
|
+
...command,
|
|
7782
|
+
intent: "update_description",
|
|
7783
|
+
target,
|
|
7784
|
+
description: command.description,
|
|
7785
|
+
})
|
|
7786
|
+
);
|
|
7787
|
+
} else if (action === "rename") {
|
|
7788
|
+
results.push(
|
|
7789
|
+
await executePrimeRouterIntent({
|
|
7790
|
+
...command,
|
|
7791
|
+
intent: item.entityType === "archive-tab" ? "rename_tab" : "rename_archive",
|
|
7792
|
+
target,
|
|
7793
|
+
})
|
|
7794
|
+
);
|
|
7795
|
+
} else if (action === "update_task_status" || action === "status") {
|
|
7796
|
+
if (item.entityType === "archive") {
|
|
7797
|
+
results.push(
|
|
7798
|
+
await executePrimeRouterIntent({
|
|
7799
|
+
...command,
|
|
7800
|
+
intent: "update_task_status",
|
|
7801
|
+
target,
|
|
7802
|
+
})
|
|
7803
|
+
);
|
|
7804
|
+
}
|
|
7805
|
+
} else {
|
|
7806
|
+
throw new Error("Unsupported selected-item action: " + action);
|
|
7807
|
+
}
|
|
7808
|
+
}
|
|
7809
|
+
return { selectedItems: selected, results };
|
|
7810
|
+
}
|
|
7811
|
+
|
|
7812
|
+
async function executePrimeRouterIntent(command = {}) {
|
|
7813
|
+
if (command.intent === "bulk_update_descriptions") {
|
|
7814
|
+
const targets = Array.isArray(command.targets) ? command.targets : [];
|
|
7815
|
+
const results = [];
|
|
7816
|
+
for (const item of targets) {
|
|
7817
|
+
const target = item.target || item;
|
|
7818
|
+
results.push(
|
|
7819
|
+
await executePrimeRouterIntent({
|
|
7820
|
+
...command,
|
|
7821
|
+
intent: "update_description",
|
|
7822
|
+
target,
|
|
7823
|
+
description: item.description !== undefined ? item.description : command.description,
|
|
7824
|
+
})
|
|
7825
|
+
);
|
|
7826
|
+
}
|
|
7827
|
+
return {
|
|
7828
|
+
intent: "bulk_update_descriptions",
|
|
7829
|
+
success: results.every((result) => result.success),
|
|
7830
|
+
results,
|
|
7831
|
+
audit: {
|
|
7832
|
+
policyTier: "prime",
|
|
7833
|
+
activeWindowId: state.activeWindowId || "unknown",
|
|
7834
|
+
createdAt: new Date().toISOString(),
|
|
7835
|
+
},
|
|
7836
|
+
};
|
|
7837
|
+
}
|
|
7838
|
+
|
|
7839
|
+
const validation = validatePrimeCommand(command);
|
|
7840
|
+
if (!validation.allowed) {
|
|
7841
|
+
return {
|
|
7842
|
+
intent: command.intent || "unknown",
|
|
7843
|
+
target: command.target || {},
|
|
7844
|
+
success: false,
|
|
7845
|
+
allowed: false,
|
|
7846
|
+
denial: validation.denial,
|
|
7847
|
+
audit: validation.audit,
|
|
7848
|
+
};
|
|
7849
|
+
}
|
|
7850
|
+
|
|
7851
|
+
try {
|
|
7852
|
+
let result;
|
|
7853
|
+
switch (command.intent) {
|
|
7854
|
+
case "update_description":
|
|
7855
|
+
result = await executePrimeDescriptionUpdate(command);
|
|
7856
|
+
break;
|
|
7857
|
+
case "rename_window":
|
|
7858
|
+
case "rename_group":
|
|
7859
|
+
case "rename_tab":
|
|
7860
|
+
case "rename_archive":
|
|
7861
|
+
result = await executePrimeRename(command);
|
|
7862
|
+
break;
|
|
7863
|
+
case "update_task_status":
|
|
7864
|
+
result = await executePrimeTaskStatus(command);
|
|
7865
|
+
break;
|
|
7866
|
+
case "save_snapshot":
|
|
7867
|
+
result = await executePrimeSaveSnapshot(command);
|
|
7868
|
+
break;
|
|
7869
|
+
case "manage_selected_item":
|
|
7870
|
+
result = await executePrimeSelectedItemCommand(command);
|
|
7871
|
+
break;
|
|
7872
|
+
default:
|
|
7873
|
+
throw new Error("Unsupported Prime command intent: " + command.intent);
|
|
7874
|
+
}
|
|
7875
|
+
await Promise.allSettled([
|
|
7876
|
+
fetchAndRenderLiveTree(),
|
|
7877
|
+
fetchAndRenderArchived(),
|
|
7878
|
+
fetchAndRenderRecent(),
|
|
7879
|
+
]);
|
|
7880
|
+
return {
|
|
7881
|
+
intent: command.intent,
|
|
7882
|
+
target: command.target || {},
|
|
7883
|
+
scope: validation.scope,
|
|
7884
|
+
success: true,
|
|
7885
|
+
allowed: true,
|
|
7886
|
+
result,
|
|
7887
|
+
audit: validation.audit,
|
|
7888
|
+
};
|
|
7889
|
+
} catch (err) {
|
|
7890
|
+
return {
|
|
7891
|
+
intent: command.intent || "unknown",
|
|
7892
|
+
target: command.target || {},
|
|
7893
|
+
scope: validation.scope,
|
|
7894
|
+
success: false,
|
|
7895
|
+
allowed: true,
|
|
7896
|
+
error: err?.message || String(err),
|
|
7897
|
+
audit: validation.audit,
|
|
7898
|
+
};
|
|
7899
|
+
}
|
|
7900
|
+
}
|
|
7901
|
+
|
|
7361
7902
|
// T038: Bulk restore — open selected tabs in a new window
|
|
7362
7903
|
async function fsBulkRestore() {
|
|
7363
7904
|
const selected = fsResolveSelectedTabs();
|
|
@@ -7394,23 +7935,6 @@ async function fsBulkCopy() {
|
|
|
7394
7935
|
}
|
|
7395
7936
|
}
|
|
7396
7937
|
|
|
7397
|
-
// T038: Bulk archive — mark selected tabs' groups as archived
|
|
7398
|
-
async function fsBulkArchive() {
|
|
7399
|
-
const selected = fsResolveSelectedTabs();
|
|
7400
|
-
if (!selected.length) return;
|
|
7401
|
-
const groupIds = [...new Set(selected.map((s) => s.groupId))];
|
|
7402
|
-
for (const gid of groupIds) {
|
|
7403
|
-
try {
|
|
7404
|
-
await sendMessage("updateArchiveStatus", { taskThreadId: gid, status: "archived" });
|
|
7405
|
-
} catch {
|
|
7406
|
-
// continue
|
|
7407
|
-
}
|
|
7408
|
-
}
|
|
7409
|
-
showToast(groupIds.length + " group" + (groupIds.length !== 1 ? "s" : "") + " archived");
|
|
7410
|
-
fsSelectedTabs.clear();
|
|
7411
|
-
await fsLoadAndRender();
|
|
7412
|
-
}
|
|
7413
|
-
|
|
7414
7938
|
// T038: Bulk trash — remove selected tabs from their groups
|
|
7415
7939
|
async function fsBulkTrash() {
|
|
7416
7940
|
const selected = fsResolveSelectedTabs();
|
|
@@ -7594,7 +8118,7 @@ function fsShowGroupContextMenu(trigger, groupId, group) {
|
|
|
7594
8118
|
const isLocked = fsLockedGroups.has(groupId);
|
|
7595
8119
|
const isStarred = group.starred;
|
|
7596
8120
|
const isPinned = group.pinned;
|
|
7597
|
-
const status = group
|
|
8121
|
+
const status = fsWorkflowStatus(group);
|
|
7598
8122
|
const color = group.color || "grey";
|
|
7599
8123
|
|
|
7600
8124
|
const COLORS = ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"];
|
|
@@ -7664,12 +8188,6 @@ function fsShowGroupContextMenu(trigger, groupId, group) {
|
|
|
7664
8188
|
action: () => fsUpdateStatus(groupId, "done"),
|
|
7665
8189
|
active: status === "done",
|
|
7666
8190
|
},
|
|
7667
|
-
{
|
|
7668
|
-
icon: "📦",
|
|
7669
|
-
label: "Mark as archived",
|
|
7670
|
-
action: () => fsUpdateStatus(groupId, "archived"),
|
|
7671
|
-
active: status === "archived",
|
|
7672
|
-
},
|
|
7673
8191
|
{ divider: true },
|
|
7674
8192
|
// Clipboard / sharing
|
|
7675
8193
|
{ icon: "🔗", label: "Copy URLs to clipboard", action: () => fsShareAsUrl(group) },
|
|
@@ -7863,7 +8381,7 @@ let fsHideArchived = false;
|
|
|
7863
8381
|
|
|
7864
8382
|
// Advanced filter bar state
|
|
7865
8383
|
const fsAdvancedFilter = {
|
|
7866
|
-
status: "all", // "all" | "
|
|
8384
|
+
status: "all", // "all" | "pending" | "done"
|
|
7867
8385
|
colors: [], // [] = all, ["blue","red"] = only those
|
|
7868
8386
|
starred: false,
|
|
7869
8387
|
locked: false,
|
|
@@ -7886,6 +8404,11 @@ async function fsLoadFilterState() {
|
|
|
7886
8404
|
if (stored.advancedFilter) {
|
|
7887
8405
|
Object.assign(fsAdvancedFilter, stored.advancedFilter);
|
|
7888
8406
|
if (!Array.isArray(fsAdvancedFilter.colors)) fsAdvancedFilter.colors = [];
|
|
8407
|
+
if (fsAdvancedFilter.status === "archived" || fsAdvancedFilter.status === "saved") {
|
|
8408
|
+
fsAdvancedFilter.status = "pending";
|
|
8409
|
+
} else if (!["all", "pending", "done"].includes(fsAdvancedFilter.status)) {
|
|
8410
|
+
fsAdvancedFilter.status = "all";
|
|
8411
|
+
}
|
|
7889
8412
|
}
|
|
7890
8413
|
} catch {
|
|
7891
8414
|
// ignore
|
|
@@ -8419,7 +8942,6 @@ function fsGetViewLabel() {
|
|
|
8419
8942
|
if (fsAdvancedFilter.stale) return "Stale";
|
|
8420
8943
|
if (fsAdvancedFilter.status === "pending") return "Pending Tasks";
|
|
8421
8944
|
if (fsAdvancedFilter.status === "done") return "Completed";
|
|
8422
|
-
if (fsAdvancedFilter.status === "archived") return "Archived";
|
|
8423
8945
|
if (fsAdvancedFilter.colors.length === 1) {
|
|
8424
8946
|
const c = fsAdvancedFilter.colors[0];
|
|
8425
8947
|
return c.charAt(0).toUpperCase() + c.slice(1) + " Groups";
|
|
@@ -8655,8 +9177,9 @@ function fsCreateGroupCard(group) {
|
|
|
8655
9177
|
card.className = "fs-group-card";
|
|
8656
9178
|
if (fsSelectedGroups.has(String(groupId))) card.classList.add("selected");
|
|
8657
9179
|
if (typeof fsIsGroupStale === "function" && fsIsGroupStale(group)) card.classList.add("stale");
|
|
8658
|
-
|
|
8659
|
-
if (
|
|
9180
|
+
const status = fsWorkflowStatus(group);
|
|
9181
|
+
if (status === "pending") card.classList.add("status-pending");
|
|
9182
|
+
if (status === "done") card.classList.add("status-done");
|
|
8660
9183
|
card.id = "fs-group-" + groupId;
|
|
8661
9184
|
|
|
8662
9185
|
const tabs = group.urls || group.tabs || [];
|
|
@@ -8983,7 +9506,7 @@ async function fsImportFromText(text) {
|
|
|
8983
9506
|
urls: urls.map((u) => ({ url: u, title: u })),
|
|
8984
9507
|
archivedAt: new Date().toISOString(),
|
|
8985
9508
|
restoredAt: null,
|
|
8986
|
-
status: "
|
|
9509
|
+
status: "pending",
|
|
8987
9510
|
};
|
|
8988
9511
|
// Save via the archive system — prepend to existing
|
|
8989
9512
|
const current = Array.isArray(archive) ? archive : [];
|
|
@@ -9091,7 +9614,7 @@ async function fsImportBookmarkFolder(folderNode) {
|
|
|
9091
9614
|
urls,
|
|
9092
9615
|
archivedAt: new Date().toISOString(),
|
|
9093
9616
|
restoredAt: null,
|
|
9094
|
-
status: "
|
|
9617
|
+
status: "pending",
|
|
9095
9618
|
};
|
|
9096
9619
|
const current = Array.isArray(archive) ? archive : [];
|
|
9097
9620
|
current.unshift(entry);
|
|
@@ -9672,7 +10195,7 @@ function fsIsGroupStale(group) {
|
|
|
9672
10195
|
return Date.now() - new Date(ts).getTime() > thresholdMs;
|
|
9673
10196
|
}
|
|
9674
10197
|
|
|
9675
|
-
async function
|
|
10198
|
+
async function fsMarkAllStaleDone() {
|
|
9676
10199
|
const staleGroups = fsCachedGroups.filter(fsIsGroupStale);
|
|
9677
10200
|
if (!staleGroups.length) {
|
|
9678
10201
|
showToast("No stale sessions found");
|
|
@@ -9681,13 +10204,13 @@ async function fsArchiveAllStale() {
|
|
|
9681
10204
|
for (const group of staleGroups) {
|
|
9682
10205
|
const gid = group.taskThreadId || group.id;
|
|
9683
10206
|
try {
|
|
9684
|
-
await sendMessage("updateArchiveStatus", { taskThreadId: gid, status: "
|
|
10207
|
+
await sendMessage("updateArchiveStatus", { taskThreadId: gid, status: "done" });
|
|
9685
10208
|
} catch {
|
|
9686
10209
|
// continue
|
|
9687
10210
|
}
|
|
9688
10211
|
}
|
|
9689
10212
|
showToast(
|
|
9690
|
-
staleGroups.length + " stale session" + (staleGroups.length !== 1 ? "s" : "") + "
|
|
10213
|
+
staleGroups.length + " stale session" + (staleGroups.length !== 1 ? "s" : "") + " marked done"
|
|
9691
10214
|
);
|
|
9692
10215
|
await fsLoadAndRender();
|
|
9693
10216
|
}
|
|
@@ -9977,11 +10500,8 @@ function fsInitTopBarActions() {
|
|
|
9977
10500
|
{ label: "Tab Group Templates", action: fsShowTemplatesModal },
|
|
9978
10501
|
{ label: "Rename current window", action: fsRenameCurrentWindowLabel },
|
|
9979
10502
|
{ divider: true },
|
|
10503
|
+
{ label: "Mark stale sessions done", action: fsMarkAllStaleDone },
|
|
9980
10504
|
{ label: "Export workspace (JSON)", action: fsExportWorkspace },
|
|
9981
|
-
{
|
|
9982
|
-
label: "Archive stale sessions",
|
|
9983
|
-
action: fsArchiveAllStale,
|
|
9984
|
-
},
|
|
9985
10505
|
]);
|
|
9986
10506
|
});
|
|
9987
10507
|
}
|
|
@@ -10027,8 +10547,10 @@ async function initFullscreenUI() {
|
|
|
10027
10547
|
fsInitCommandPalette();
|
|
10028
10548
|
fsInitKeyboardNav();
|
|
10029
10549
|
fsInitDragAndDrop();
|
|
10550
|
+
fsInitNavigationMenu();
|
|
10030
10551
|
fsInitTopBarActions();
|
|
10031
10552
|
await fsLoadAndRender();
|
|
10553
|
+
fsApplyFullscreenRoute();
|
|
10032
10554
|
fsStartAgentPoll();
|
|
10033
10555
|
await fsRenderTimeline();
|
|
10034
10556
|
}
|
|
@@ -10306,5 +10828,13 @@ async function init() {
|
|
|
10306
10828
|
}
|
|
10307
10829
|
}
|
|
10308
10830
|
|
|
10831
|
+
if (typeof globalThis !== "undefined") {
|
|
10832
|
+
globalThis.EquanautPrimeControlPlane = {
|
|
10833
|
+
executePrimeRouterIntent,
|
|
10834
|
+
getPrimeSelectedItemCatalog,
|
|
10835
|
+
buildSidepanelGlobalContextCatalog,
|
|
10836
|
+
};
|
|
10837
|
+
}
|
|
10838
|
+
|
|
10309
10839
|
// Start when DOM is ready
|
|
10310
10840
|
document.addEventListener("DOMContentLoaded", init);
|