aimux-cli 0.1.13 → 0.1.14
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 +54 -0
- package/dist/agent-prompt-delivery.d.ts +31 -0
- package/dist/agent-prompt-delivery.js +119 -0
- package/dist/agent-prompt-delivery.js.map +1 -0
- package/dist/agent-tracker.js +4 -2
- package/dist/agent-tracker.js.map +1 -1
- package/dist/atomic-write.d.ts +1 -0
- package/dist/atomic-write.js +9 -0
- package/dist/atomic-write.js.map +1 -0
- package/dist/builtin-metadata-watchers.js +10 -6
- package/dist/builtin-metadata-watchers.js.map +1 -1
- package/dist/daemon.js +3 -6
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/index.d.ts +26 -15
- package/dist/dashboard/index.js +26 -56
- package/dist/dashboard/index.js.map +1 -1
- package/dist/dashboard/operation-failures.d.ts +30 -0
- package/dist/dashboard/operation-failures.js +86 -0
- package/dist/dashboard/operation-failures.js.map +1 -0
- package/dist/dashboard/pending-actions.d.ts +9 -2
- package/dist/dashboard/pending-actions.js +66 -7
- package/dist/dashboard/pending-actions.js.map +1 -1
- package/dist/dashboard/quick-jump.d.ts +3 -1
- package/dist/dashboard/quick-jump.js +27 -8
- package/dist/dashboard/quick-jump.js.map +1 -1
- package/dist/dashboard/runtime-evidence.d.ts +4 -0
- package/dist/dashboard/runtime-evidence.js +22 -0
- package/dist/dashboard/runtime-evidence.js.map +1 -0
- package/dist/dashboard/session-actions.d.ts +1 -1
- package/dist/dashboard/session-actions.js +3 -1
- package/dist/dashboard/session-actions.js.map +1 -1
- package/dist/dashboard/session-registry.d.ts +1 -0
- package/dist/dashboard/session-registry.js +47 -30
- package/dist/dashboard/session-registry.js.map +1 -1
- package/dist/dashboard/state.d.ts +9 -0
- package/dist/dashboard/state.js +33 -1
- package/dist/dashboard/state.js.map +1 -1
- package/dist/dashboard/targets.js +11 -3
- package/dist/dashboard/targets.js.map +1 -1
- package/dist/dashboard/ui-state-store.d.ts +3 -3
- package/dist/dashboard/ui-state-store.js +23 -14
- package/dist/dashboard/ui-state-store.js.map +1 -1
- package/dist/default-plugins/gh-pr-context.d.ts +8 -0
- package/dist/default-plugins/gh-pr-context.js +256 -0
- package/dist/default-plugins/gh-pr-context.js.map +1 -0
- package/dist/default-plugins/transcript-length.d.ts +1 -0
- package/dist/default-plugins/transcript-length.js +10 -0
- package/dist/default-plugins/transcript-length.js.map +1 -1
- package/dist/fast-control.d.ts +8 -3
- package/dist/fast-control.js +13 -14
- package/dist/fast-control.js.map +1 -1
- package/dist/main.js +22 -35
- package/dist/main.js.map +1 -1
- package/dist/managed-launch-env.d.ts +10 -0
- package/dist/managed-launch-env.js +51 -0
- package/dist/managed-launch-env.js.map +1 -0
- package/dist/metadata-server.d.ts +41 -0
- package/dist/metadata-server.js +272 -15
- package/dist/metadata-server.js.map +1 -1
- package/dist/metadata-store.d.ts +1 -0
- package/dist/metadata-store.js +3 -5
- package/dist/metadata-store.js.map +1 -1
- package/dist/multiplexer/agent-io-methods.d.ts +1 -1
- package/dist/multiplexer/agent-io-methods.js +36 -29
- package/dist/multiplexer/agent-io-methods.js.map +1 -1
- package/dist/multiplexer/archives.d.ts +1 -0
- package/dist/multiplexer/archives.js +143 -25
- package/dist/multiplexer/archives.js.map +1 -1
- package/dist/multiplexer/dashboard-actions-methods.d.ts +10 -1
- package/dist/multiplexer/dashboard-actions-methods.js +16 -4
- package/dist/multiplexer/dashboard-actions-methods.js.map +1 -1
- package/dist/multiplexer/dashboard-control.d.ts +13 -1
- package/dist/multiplexer/dashboard-control.js +150 -121
- package/dist/multiplexer/dashboard-control.js.map +1 -1
- package/dist/multiplexer/dashboard-interaction.d.ts +2 -1
- package/dist/multiplexer/dashboard-interaction.js +175 -113
- package/dist/multiplexer/dashboard-interaction.js.map +1 -1
- package/dist/multiplexer/dashboard-model.d.ts +5 -1
- package/dist/multiplexer/dashboard-model.js +83 -8
- package/dist/multiplexer/dashboard-model.js.map +1 -1
- package/dist/multiplexer/dashboard-ops.d.ts +32 -1
- package/dist/multiplexer/dashboard-ops.js +517 -15
- package/dist/multiplexer/dashboard-ops.js.map +1 -1
- package/dist/multiplexer/dashboard-state-methods.d.ts +7 -0
- package/dist/multiplexer/dashboard-state-methods.js +79 -4
- package/dist/multiplexer/dashboard-state-methods.js.map +1 -1
- package/dist/multiplexer/dashboard-tail-methods.d.ts +12 -2
- package/dist/multiplexer/dashboard-tail-methods.js +18 -6
- package/dist/multiplexer/dashboard-tail-methods.js.map +1 -1
- package/dist/multiplexer/dashboard-view-methods.d.ts +3 -1
- package/dist/multiplexer/dashboard-view-methods.js +53 -38
- package/dist/multiplexer/dashboard-view-methods.js.map +1 -1
- package/dist/multiplexer/graveyard-view-model.d.ts +68 -0
- package/dist/multiplexer/graveyard-view-model.js +185 -0
- package/dist/multiplexer/graveyard-view-model.js.map +1 -0
- package/dist/multiplexer/index.d.ts +18 -11
- package/dist/multiplexer/index.js +30 -18
- package/dist/multiplexer/index.js.map +1 -1
- package/dist/multiplexer/navigation.d.ts +1 -1
- package/dist/multiplexer/navigation.js +31 -11
- package/dist/multiplexer/navigation.js.map +1 -1
- package/dist/multiplexer/notifications.d.ts +1 -0
- package/dist/multiplexer/notifications.js +27 -19
- package/dist/multiplexer/notifications.js.map +1 -1
- package/dist/multiplexer/persistence-methods.d.ts +16 -0
- package/dist/multiplexer/persistence-methods.js +349 -28
- package/dist/multiplexer/persistence-methods.js.map +1 -1
- package/dist/multiplexer/runtime-lifecycle-methods.js +81 -12
- package/dist/multiplexer/runtime-lifecycle-methods.js.map +1 -1
- package/dist/multiplexer/runtime-state.d.ts +6 -2
- package/dist/multiplexer/runtime-state.js +144 -23
- package/dist/multiplexer/runtime-state.js.map +1 -1
- package/dist/multiplexer/service-state-snapshot.d.ts +14 -0
- package/dist/multiplexer/service-state-snapshot.js +121 -0
- package/dist/multiplexer/service-state-snapshot.js.map +1 -0
- package/dist/multiplexer/services.d.ts +19 -1
- package/dist/multiplexer/services.js +114 -16
- package/dist/multiplexer/services.js.map +1 -1
- package/dist/multiplexer/session-actions.d.ts +3 -0
- package/dist/multiplexer/session-actions.js +10 -8
- package/dist/multiplexer/session-actions.js.map +1 -1
- package/dist/multiplexer/session-launch.d.ts +1 -1
- package/dist/multiplexer/session-launch.js +63 -22
- package/dist/multiplexer/session-launch.js.map +1 -1
- package/dist/multiplexer/session-runtime-core.d.ts +2 -1
- package/dist/multiplexer/session-runtime-core.js +107 -69
- package/dist/multiplexer/session-runtime-core.js.map +1 -1
- package/dist/multiplexer/subscreens.d.ts +1 -0
- package/dist/multiplexer/subscreens.js +17 -12
- package/dist/multiplexer/subscreens.js.map +1 -1
- package/dist/multiplexer/tool-picker.d.ts +6 -2
- package/dist/multiplexer/tool-picker.js +231 -81
- package/dist/multiplexer/tool-picker.js.map +1 -1
- package/dist/multiplexer/worktree-graveyard.d.ts +13 -0
- package/dist/multiplexer/worktree-graveyard.js +21 -0
- package/dist/multiplexer/worktree-graveyard.js.map +1 -0
- package/dist/multiplexer/worktrees.d.ts +1 -0
- package/dist/multiplexer/worktrees.js +151 -69
- package/dist/multiplexer/worktrees.js.map +1 -1
- package/dist/notification-context.js +9 -15
- package/dist/notification-context.js.map +1 -1
- package/dist/notifications.d.ts +22 -0
- package/dist/notifications.js +66 -11
- package/dist/notifications.js.map +1 -1
- package/dist/orchestration-actions.js +3 -3
- package/dist/orchestration-actions.js.map +1 -1
- package/dist/orchestration-dispatcher.d.ts +4 -3
- package/dist/orchestration-dispatcher.js +9 -7
- package/dist/orchestration-dispatcher.js.map +1 -1
- package/dist/orchestration-routing.d.ts +2 -2
- package/dist/orchestration-routing.js +3 -7
- package/dist/orchestration-routing.js.map +1 -1
- package/dist/paths.d.ts +3 -0
- package/dist/paths.js +9 -0
- package/dist/paths.js.map +1 -1
- package/dist/plugin-runtime.d.ts +5 -0
- package/dist/plugin-runtime.js +98 -34
- package/dist/plugin-runtime.js.map +1 -1
- package/dist/project-events.js +4 -2
- package/dist/project-events.js.map +1 -1
- package/dist/session-bootstrap.d.ts +0 -3
- package/dist/session-bootstrap.js +12 -104
- package/dist/session-bootstrap.js.map +1 -1
- package/dist/session-semantics.d.ts +44 -7
- package/dist/session-semantics.js +164 -69
- package/dist/session-semantics.js.map +1 -1
- package/dist/shell-args.d.ts +1 -0
- package/dist/shell-args.js +57 -0
- package/dist/shell-args.js.map +1 -0
- package/dist/shell-hooks.d.ts +3 -0
- package/dist/shell-hooks.js +19 -2
- package/dist/shell-hooks.js.map +1 -1
- package/dist/statusline-model.d.ts +5 -1
- package/dist/statusline-model.js +18 -29
- package/dist/statusline-model.js.map +1 -1
- package/dist/task-dispatcher.d.ts +3 -3
- package/dist/task-dispatcher.js +6 -5
- package/dist/task-dispatcher.js.map +1 -1
- package/dist/task-workflow.d.ts +2 -0
- package/dist/task-workflow.js +15 -6
- package/dist/task-workflow.js.map +1 -1
- package/dist/tasks.d.ts +4 -1
- package/dist/tasks.js +19 -1
- package/dist/tasks.js.map +1 -1
- package/dist/tmux/doctor.d.ts +1 -0
- package/dist/tmux/doctor.js +4 -1
- package/dist/tmux/doctor.js.map +1 -1
- package/dist/tmux/runtime-manager.d.ts +6 -0
- package/dist/tmux/runtime-manager.js +56 -18
- package/dist/tmux/runtime-manager.js.map +1 -1
- package/dist/tmux/statusline.js +17 -16
- package/dist/tmux/statusline.js.map +1 -1
- package/dist/tmux/window-open.d.ts +1 -0
- package/dist/tmux/window-open.js +10 -10
- package/dist/tmux/window-open.js.map +1 -1
- package/dist/tool-output-watchers.d.ts +2 -0
- package/dist/tool-output-watchers.js +123 -12
- package/dist/tool-output-watchers.js.map +1 -1
- package/dist/tui/screens/dashboard-renderers.d.ts +2 -21
- package/dist/tui/screens/dashboard-renderers.js +126 -32
- package/dist/tui/screens/dashboard-renderers.js.map +1 -1
- package/dist/tui/screens/overlay-renderers.d.ts +11 -1
- package/dist/tui/screens/overlay-renderers.js +68 -28
- package/dist/tui/screens/overlay-renderers.js.map +1 -1
- package/dist/tui/screens/subscreen-renderers.js +156 -33
- package/dist/tui/screens/subscreen-renderers.js.map +1 -1
- package/dist/workflow.js +2 -2
- package/dist/workflow.js.map +1 -1
- package/dist/worktree.d.ts +3 -0
- package/dist/worktree.js +27 -2
- package/dist/worktree.js.map +1 -1
- package/package.json +5 -1
package/dist/metadata-server.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
4
|
-
import {
|
|
4
|
+
import { basename } from "node:path";
|
|
5
|
+
import { getDashboardClientUiStatePath, getProjectId, getProjectStateDir } from "./paths.js";
|
|
5
6
|
import { updateSessionMetadata, clearSessionLogs, saveMetadataEndpoint, loadMetadataState, } from "./metadata-store.js";
|
|
6
7
|
import { notifyAlert } from "./notify.js";
|
|
7
8
|
import { clearNotifications, listNotifications, markNotificationsRead, unreadNotificationCount, } from "./notifications.js";
|
|
@@ -21,17 +22,129 @@ import { listSwitchableAgentItems, resolveAttentionAgent, resolveNextAgent, reso
|
|
|
21
22
|
import { TmuxRuntimeManager } from "./tmux/runtime-manager.js";
|
|
22
23
|
import { openTargetForClient } from "./tmux/window-open.js";
|
|
23
24
|
import { getDashboardCommandSpec } from "./dashboard/command-spec.js";
|
|
24
|
-
function
|
|
25
|
+
function compactSessionId(sessionId) {
|
|
26
|
+
const compact = sessionId.replace(/-[a-z0-9]{4,}$/i, "");
|
|
27
|
+
return compact || sessionId;
|
|
28
|
+
}
|
|
29
|
+
function metadataDisplayContext(metadata) {
|
|
30
|
+
return {
|
|
31
|
+
label: metadata?.label,
|
|
32
|
+
worktreePath: metadata?.context?.worktreePath,
|
|
33
|
+
worktreeName: metadata?.context?.worktreeName,
|
|
34
|
+
branch: metadata?.context?.branch,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function mergeDisplayContext(base, override) {
|
|
38
|
+
return {
|
|
39
|
+
label: override.label ?? base.label,
|
|
40
|
+
command: override.command ?? base.command,
|
|
41
|
+
worktreePath: override.worktreePath ?? base.worktreePath,
|
|
42
|
+
worktreeName: override.worktreeName ?? base.worktreeName,
|
|
43
|
+
branch: override.branch ?? base.branch,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function displayWorktreeLabel(context) {
|
|
47
|
+
const worktreeName = context.worktreeName?.trim();
|
|
48
|
+
const branch = context.branch?.trim();
|
|
49
|
+
if (worktreeName)
|
|
50
|
+
return worktreeName;
|
|
51
|
+
if (branch)
|
|
52
|
+
return branch;
|
|
53
|
+
const path = context.worktreePath?.trim();
|
|
54
|
+
return path ? basename(path) : undefined;
|
|
55
|
+
}
|
|
56
|
+
function sessionAlertSubject(sessionId, context) {
|
|
57
|
+
if (!sessionId)
|
|
58
|
+
return undefined;
|
|
59
|
+
const label = context?.label?.trim() || context?.command?.trim() || compactSessionId(sessionId);
|
|
60
|
+
const worktree = context ? displayWorktreeLabel(context) : undefined;
|
|
61
|
+
return worktree ? `${label} @ ${worktree}` : label;
|
|
62
|
+
}
|
|
63
|
+
function sessionAlertTitle(kind, sessionId, fallback, context) {
|
|
64
|
+
const title = fallback?.trim();
|
|
65
|
+
const subject = sessionAlertSubject(sessionId, context);
|
|
66
|
+
if (!subject)
|
|
67
|
+
return title || "aimux";
|
|
68
|
+
if (kind === "needs_input")
|
|
69
|
+
return `${subject} needs input`;
|
|
70
|
+
if (kind === "blocked") {
|
|
71
|
+
if (!title || (sessionId && title === `${sessionId} is blocked`))
|
|
72
|
+
return `${subject} is blocked`;
|
|
73
|
+
return title;
|
|
74
|
+
}
|
|
75
|
+
if (kind === "task_failed") {
|
|
76
|
+
if (!title || (sessionId && title === `${sessionId} errored`))
|
|
77
|
+
return `${subject} errored`;
|
|
78
|
+
return title;
|
|
79
|
+
}
|
|
80
|
+
if (kind === "task_done") {
|
|
81
|
+
if (!title || (sessionId && title === `${sessionId} finished`))
|
|
82
|
+
return `${subject} finished`;
|
|
83
|
+
return title;
|
|
84
|
+
}
|
|
85
|
+
if (!title)
|
|
86
|
+
return subject;
|
|
87
|
+
if (title.includes(subject))
|
|
88
|
+
return title;
|
|
89
|
+
if (sessionId && title.includes(sessionId))
|
|
90
|
+
return title.replace(sessionId, subject);
|
|
91
|
+
return `${subject}: ${title}`;
|
|
92
|
+
}
|
|
93
|
+
function dashboardClientKeyFromSession(sessionName) {
|
|
94
|
+
return sessionName.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
95
|
+
}
|
|
96
|
+
function persistDashboardClientPreference(clientSession, update) {
|
|
97
|
+
const path = getDashboardClientUiStatePath(dashboardClientKeyFromSession(clientSession));
|
|
98
|
+
let snapshot = {};
|
|
25
99
|
try {
|
|
26
|
-
|
|
27
|
-
const raw = readFileSync(path, "utf-8");
|
|
28
|
-
const snapshot = JSON.parse(raw);
|
|
29
|
-
snapshot.screen = screen;
|
|
30
|
-
writeFileSync(path, JSON.stringify(snapshot, null, 2) + "\n");
|
|
100
|
+
snapshot = JSON.parse(readFileSync(path, "utf-8"));
|
|
31
101
|
}
|
|
32
|
-
catch {
|
|
33
|
-
|
|
102
|
+
catch { }
|
|
103
|
+
update(snapshot);
|
|
104
|
+
writeFileSync(path, JSON.stringify(snapshot, null, 2) + "\n");
|
|
105
|
+
}
|
|
106
|
+
function persistDashboardReturnSelection(tmux, projectRoot, currentClientSession, currentWindowId) {
|
|
107
|
+
persistDashboardClientPreference(currentClientSession, (snapshot) => {
|
|
108
|
+
snapshot.screen = "dashboard";
|
|
109
|
+
if (!currentWindowId)
|
|
110
|
+
return;
|
|
111
|
+
const match = tmux
|
|
112
|
+
.listProjectManagedWindows(projectRoot)
|
|
113
|
+
.find((entry) => entry.target.windowId === currentWindowId);
|
|
114
|
+
if (!match)
|
|
115
|
+
return;
|
|
116
|
+
snapshot.focusedWorktreePath = match.metadata.worktreePath;
|
|
117
|
+
snapshot.level = "sessions";
|
|
118
|
+
snapshot.selectedEntryKind = match.metadata.kind === "service" ? "service" : "session";
|
|
119
|
+
snapshot.selectedEntryId = match.metadata.sessionId;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function markActiveWindowFocused(tmux, projectRoot, currentClientSession, currentWindow, currentWindowId, tracker) {
|
|
123
|
+
if (currentWindow && /^dashboard/.test(currentWindow)) {
|
|
124
|
+
updateNotificationContext("tui", {
|
|
125
|
+
focused: true,
|
|
126
|
+
screen: "dashboard",
|
|
127
|
+
panelOpen: false,
|
|
128
|
+
sessionId: undefined,
|
|
129
|
+
});
|
|
130
|
+
return true;
|
|
34
131
|
}
|
|
132
|
+
if (!currentWindowId)
|
|
133
|
+
return false;
|
|
134
|
+
const match = tmux.listProjectManagedWindows(projectRoot).find((entry) => entry.target.windowId === currentWindowId);
|
|
135
|
+
if (!match)
|
|
136
|
+
return false;
|
|
137
|
+
updateNotificationContext("tui", {
|
|
138
|
+
focused: true,
|
|
139
|
+
sessionId: match.metadata.sessionId,
|
|
140
|
+
panelOpen: false,
|
|
141
|
+
});
|
|
142
|
+
if (match.metadata.kind === "agent") {
|
|
143
|
+
tracker.markSeen(match.metadata.sessionId);
|
|
144
|
+
markNotificationsRead({ sessionId: match.metadata.sessionId });
|
|
145
|
+
}
|
|
146
|
+
markTargetUsed(tmux, projectRoot, match.target, currentClientSession, match.metadata.sessionId);
|
|
147
|
+
return true;
|
|
35
148
|
}
|
|
36
149
|
function markTargetUsed(tmux, projectRoot, target, currentClientSession, itemId) {
|
|
37
150
|
const resolvedItemId = itemId ||
|
|
@@ -144,7 +257,26 @@ export class MetadataServer {
|
|
|
144
257
|
});
|
|
145
258
|
}
|
|
146
259
|
emitAlert(input) {
|
|
147
|
-
this.
|
|
260
|
+
const displayContext = this.resolveSessionAlertDisplayContext(input.sessionId, input.worktreePath);
|
|
261
|
+
this.eventBus.publishAlert({
|
|
262
|
+
...input,
|
|
263
|
+
title: sessionAlertTitle(input.kind, input.sessionId, input.title, displayContext),
|
|
264
|
+
worktreePath: input.worktreePath ?? displayContext?.worktreePath,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
resolveSessionAlertDisplayContext(sessionId, worktreePath) {
|
|
268
|
+
if (!sessionId)
|
|
269
|
+
return worktreePath ? { worktreePath } : undefined;
|
|
270
|
+
let context = {};
|
|
271
|
+
try {
|
|
272
|
+
context = metadataDisplayContext(loadMetadataState().sessions[sessionId]);
|
|
273
|
+
}
|
|
274
|
+
catch { }
|
|
275
|
+
const liveContext = this.options.desktop?.getSessionDisplayContext?.(sessionId);
|
|
276
|
+
context = mergeDisplayContext(context, liveContext ?? {});
|
|
277
|
+
if (worktreePath)
|
|
278
|
+
context.worktreePath = worktreePath;
|
|
279
|
+
return Object.values(context).some((value) => value !== undefined) ? context : undefined;
|
|
148
280
|
}
|
|
149
281
|
emitThreadWaitingAlert(input) {
|
|
150
282
|
for (const recipient of [...new Set((input.recipients ?? []).map((value) => value?.trim()).filter(Boolean))]) {
|
|
@@ -371,7 +503,11 @@ export class MetadataServer {
|
|
|
371
503
|
send(res, 501, { ok: false, error: "graveyard listing not supported by this service" });
|
|
372
504
|
return;
|
|
373
505
|
}
|
|
374
|
-
send(res, 200, {
|
|
506
|
+
send(res, 200, {
|
|
507
|
+
ok: true,
|
|
508
|
+
entries: this.options.desktop.listGraveyard(),
|
|
509
|
+
worktrees: this.options.desktop.listWorktreeGraveyard?.() ?? [],
|
|
510
|
+
});
|
|
375
511
|
return;
|
|
376
512
|
}
|
|
377
513
|
if (req.method === "GET" && url.pathname === "/threads") {
|
|
@@ -525,10 +661,12 @@ export class MetadataServer {
|
|
|
525
661
|
: {};
|
|
526
662
|
const currentClientSession = body.currentClientSession?.trim() || url.searchParams.get("currentClientSession")?.trim() || undefined;
|
|
527
663
|
const clientTty = body.clientTty?.trim() || url.searchParams.get("clientTty")?.trim() || undefined;
|
|
664
|
+
const currentWindowId = body.currentWindowId?.trim() || url.searchParams.get("currentWindowId")?.trim() || undefined;
|
|
528
665
|
if (!currentClientSession) {
|
|
529
666
|
send(res, 400, { ok: false, error: "currentClientSession is required" });
|
|
530
667
|
return;
|
|
531
668
|
}
|
|
669
|
+
persistDashboardReturnSelection(new TmuxRuntimeManager(), process.cwd(), currentClientSession, currentWindowId);
|
|
532
670
|
const tmux = new TmuxRuntimeManager();
|
|
533
671
|
const { dashboardCommand, dashboardBuildStamp } = getDashboardCommandSpec(process.cwd());
|
|
534
672
|
const dashboardSession = tmux.ensureProjectSession(process.cwd(), dashboardCommand);
|
|
@@ -555,7 +693,9 @@ export class MetadataServer {
|
|
|
555
693
|
send(res, 400, { ok: false, error: "currentClientSession is required" });
|
|
556
694
|
return;
|
|
557
695
|
}
|
|
558
|
-
|
|
696
|
+
persistDashboardClientPreference(currentClientSession, (snapshot) => {
|
|
697
|
+
snapshot.screen = "notifications";
|
|
698
|
+
});
|
|
559
699
|
const tmux = new TmuxRuntimeManager();
|
|
560
700
|
const { dashboardCommand, dashboardBuildStamp } = getDashboardCommandSpec(process.cwd());
|
|
561
701
|
const dashboardSession = tmux.ensureProjectSession(process.cwd(), dashboardCommand);
|
|
@@ -674,6 +814,23 @@ export class MetadataServer {
|
|
|
674
814
|
send(res, 200, { ok: true });
|
|
675
815
|
return;
|
|
676
816
|
}
|
|
817
|
+
if ((req.method === "GET" || req.method === "POST") && url.pathname === "/control/active-window") {
|
|
818
|
+
const body = req.method === "POST"
|
|
819
|
+
? (await readJson(req))
|
|
820
|
+
: {};
|
|
821
|
+
const currentClientSession = body.currentClientSession?.trim() || url.searchParams.get("currentClientSession")?.trim() || undefined;
|
|
822
|
+
const currentWindow = body.currentWindow?.trim() || url.searchParams.get("currentWindow")?.trim() || undefined;
|
|
823
|
+
const currentWindowId = body.currentWindowId?.trim() || url.searchParams.get("currentWindowId")?.trim() || undefined;
|
|
824
|
+
const tmux = new TmuxRuntimeManager();
|
|
825
|
+
const ok = markActiveWindowFocused(tmux, process.cwd(), currentClientSession, currentWindow, currentWindowId, this.tracker);
|
|
826
|
+
if (!ok) {
|
|
827
|
+
send(res, 404, { ok: false, error: "window not found" });
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
this.options.onChange?.();
|
|
831
|
+
send(res, 200, { ok: true });
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
677
834
|
if ((req.method === "GET" || req.method === "POST") && url.pathname === "/control/switch-next") {
|
|
678
835
|
const body = req.method === "POST"
|
|
679
836
|
? (await readJson(req))
|
|
@@ -798,6 +955,46 @@ export class MetadataServer {
|
|
|
798
955
|
if (req.method === "POST" && url.pathname === "/event") {
|
|
799
956
|
const body = (await readJson(req));
|
|
800
957
|
this.tracker.emit(body.session, body.event);
|
|
958
|
+
if (body.event.kind === "needs_input") {
|
|
959
|
+
this.emitAlert({
|
|
960
|
+
kind: "needs_input",
|
|
961
|
+
sessionId: body.session,
|
|
962
|
+
title: `${body.session} needs input`,
|
|
963
|
+
message: body.event.message || "Agent is waiting for input.",
|
|
964
|
+
dedupeKey: `needs_input:${body.session}`,
|
|
965
|
+
cooldownMs: 15_000,
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
else if (body.event.kind === "blocked") {
|
|
969
|
+
this.emitAlert({
|
|
970
|
+
kind: "blocked",
|
|
971
|
+
sessionId: body.session,
|
|
972
|
+
title: `${body.session} is blocked`,
|
|
973
|
+
message: body.event.message || "Agent reported a blocked state.",
|
|
974
|
+
dedupeKey: `blocked:${body.session}`,
|
|
975
|
+
cooldownMs: 15_000,
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
else if (body.event.kind === "task_failed" || body.event.tone === "error") {
|
|
979
|
+
this.emitAlert({
|
|
980
|
+
kind: "task_failed",
|
|
981
|
+
sessionId: body.session,
|
|
982
|
+
title: `${body.session} errored`,
|
|
983
|
+
message: body.event.message || "Agent reported an error state.",
|
|
984
|
+
dedupeKey: `error:${body.session}`,
|
|
985
|
+
cooldownMs: 15_000,
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
else if (body.event.kind === "notify") {
|
|
989
|
+
this.emitAlert({
|
|
990
|
+
kind: "notification",
|
|
991
|
+
sessionId: body.session,
|
|
992
|
+
title: sessionAlertTitle("notification", body.session, body.event.source),
|
|
993
|
+
message: body.event.message || "Agent notification.",
|
|
994
|
+
dedupeKey: body.event.message ? `notify:${body.session}:${body.event.message}` : undefined,
|
|
995
|
+
cooldownMs: 15_000,
|
|
996
|
+
});
|
|
997
|
+
}
|
|
801
998
|
this.options.onChange?.();
|
|
802
999
|
send(res, 200, { ok: true });
|
|
803
1000
|
return;
|
|
@@ -880,14 +1077,26 @@ export class MetadataServer {
|
|
|
880
1077
|
: requestedKind === "review_waiting"
|
|
881
1078
|
? "review_waiting"
|
|
882
1079
|
: "needs_input";
|
|
1080
|
+
const sessionId = body.sessionId?.trim() || undefined;
|
|
1081
|
+
const dedupeKey = body.force
|
|
1082
|
+
? undefined
|
|
1083
|
+
: kind === "needs_input" && sessionId
|
|
1084
|
+
? `needs_input:${sessionId}`
|
|
1085
|
+
: kind === "blocked" && sessionId
|
|
1086
|
+
? `blocked:${sessionId}`
|
|
1087
|
+
: kind === "task_failed" && sessionId
|
|
1088
|
+
? `error:${sessionId}`
|
|
1089
|
+
: kind === "task_done"
|
|
1090
|
+
? `notify:complete:${body.title ?? body.message ?? "aimux"}`
|
|
1091
|
+
: undefined;
|
|
883
1092
|
this.emitAlert({
|
|
884
1093
|
kind,
|
|
885
|
-
sessionId
|
|
886
|
-
title: body.title
|
|
1094
|
+
sessionId,
|
|
1095
|
+
title: sessionAlertTitle(kind, sessionId, body.title),
|
|
887
1096
|
message: [body.subtitle?.trim(), body.message?.trim() || body.title?.trim() || "aimux"]
|
|
888
1097
|
.filter(Boolean)
|
|
889
1098
|
.join(" — "),
|
|
890
|
-
dedupeKey
|
|
1099
|
+
dedupeKey,
|
|
891
1100
|
forceNotify: Boolean(body.force),
|
|
892
1101
|
});
|
|
893
1102
|
send(res, 200, { ok: true });
|
|
@@ -1186,6 +1395,17 @@ export class MetadataServer {
|
|
|
1186
1395
|
send(res, 200, { ok: true, ...result });
|
|
1187
1396
|
return;
|
|
1188
1397
|
}
|
|
1398
|
+
if (req.method === "POST" && url.pathname === "/agents/resume") {
|
|
1399
|
+
const body = (await readJson(req));
|
|
1400
|
+
if (!this.options.desktop?.resumeAgent) {
|
|
1401
|
+
send(res, 501, { ok: false, error: "agent resume not supported by this service" });
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
const result = await this.options.desktop.resumeAgent(body);
|
|
1405
|
+
this.options.onChange?.();
|
|
1406
|
+
send(res, 200, { ok: true, ...result });
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1189
1409
|
if (req.method === "POST" && url.pathname === "/agents/interrupt") {
|
|
1190
1410
|
const body = (await readJson(req));
|
|
1191
1411
|
if (!this.options.lifecycle?.interruptAgent) {
|
|
@@ -1204,6 +1424,10 @@ export class MetadataServer {
|
|
|
1204
1424
|
return;
|
|
1205
1425
|
}
|
|
1206
1426
|
const result = await this.options.lifecycle.renameAgent(body);
|
|
1427
|
+
updateSessionMetadata(body.sessionId, (current) => ({
|
|
1428
|
+
...current,
|
|
1429
|
+
label: body.label?.trim() || undefined,
|
|
1430
|
+
}));
|
|
1207
1431
|
this.options.onChange?.();
|
|
1208
1432
|
send(res, 200, { ok: true, ...result });
|
|
1209
1433
|
return;
|
|
@@ -1382,6 +1606,17 @@ export class MetadataServer {
|
|
|
1382
1606
|
send(res, 202, { ok: true, path: body.path, status: "removing" });
|
|
1383
1607
|
return;
|
|
1384
1608
|
}
|
|
1609
|
+
if (req.method === "POST" && url.pathname === "/worktrees/graveyard") {
|
|
1610
|
+
const body = (await readJson(req));
|
|
1611
|
+
if (!this.options.desktop?.graveyardWorktree) {
|
|
1612
|
+
send(res, 501, { ok: false, error: "worktree graveyard not supported by this service" });
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
const result = await this.options.desktop.graveyardWorktree(body);
|
|
1616
|
+
this.options.onChange?.();
|
|
1617
|
+
send(res, 200, { ok: true, ...result });
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1385
1620
|
if (req.method === "POST" && url.pathname === "/services/create") {
|
|
1386
1621
|
const body = (await readJson(req));
|
|
1387
1622
|
if (!this.options.desktop?.createService) {
|
|
@@ -1437,6 +1672,28 @@ export class MetadataServer {
|
|
|
1437
1672
|
send(res, 200, { ok: true, ...result });
|
|
1438
1673
|
return;
|
|
1439
1674
|
}
|
|
1675
|
+
if (req.method === "POST" && url.pathname === "/graveyard/worktrees/resurrect") {
|
|
1676
|
+
const body = (await readJson(req));
|
|
1677
|
+
if (!this.options.desktop?.resurrectGraveyardWorktree) {
|
|
1678
|
+
send(res, 501, { ok: false, error: "worktree graveyard resurrect not supported by this service" });
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
const result = await this.options.desktop.resurrectGraveyardWorktree(body);
|
|
1682
|
+
this.options.onChange?.();
|
|
1683
|
+
send(res, 200, { ok: true, ...result });
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
if (req.method === "POST" && url.pathname === "/graveyard/worktrees/delete") {
|
|
1687
|
+
const body = (await readJson(req));
|
|
1688
|
+
if (!this.options.desktop?.deleteGraveyardWorktree) {
|
|
1689
|
+
send(res, 501, { ok: false, error: "worktree graveyard delete not supported by this service" });
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
const result = await this.options.desktop.deleteGraveyardWorktree(body);
|
|
1693
|
+
this.options.onChange?.();
|
|
1694
|
+
send(res, 200, { ok: true, ...result });
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1440
1697
|
if (req.method === "POST" && url.pathname === "/reviews/approve") {
|
|
1441
1698
|
const body = (await readJson(req));
|
|
1442
1699
|
const result = this.options.actions?.approveReview
|