@openscout/protocol 0.2.98 → 0.2.102
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/dist/broker-routes.d.ts +7 -0
- package/dist/broker-routes.d.ts.map +1 -1
- package/dist/broker-routes.js +14 -0
- package/dist/conversation-projection.d.ts +1 -1
- package/dist/conversation-projection.js +1 -1
- package/dist/herdr-workspace-digest.d.ts +151 -0
- package/dist/herdr-workspace-digest.d.ts.map +1 -0
- package/dist/herdr-workspace-digest.js +343 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/machines.d.ts +231 -0
- package/dist/machines.d.ts.map +1 -0
- package/dist/machines.js +550 -0
- package/dist/runtime-catalog.generated.d.ts +6 -15
- package/dist/runtime-catalog.generated.d.ts.map +1 -1
- package/dist/runtime-catalog.generated.js +13 -19
- package/dist/scout-delivery.d.ts +1 -0
- package/dist/scout-delivery.d.ts.map +1 -1
- package/dist/terminal-host-topology.d.ts +20 -1
- package/dist/terminal-host-topology.d.ts.map +1 -1
- package/dist/terminal-host-topology.js +8 -2
- package/package.json +2 -2
- package/src/broker-routes.ts +16 -0
- package/src/conversation-projection.ts +1 -1
- package/src/herdr-workspace-digest.ts +458 -0
- package/src/index.ts +2 -0
- package/src/machines.ts +741 -0
- package/src/runtime-catalog.generated.ts +13 -19
- package/src/runtime-catalog.v1.json +6 -7
- package/src/scout-delivery.ts +1 -0
- package/src/terminal-host-topology.ts +32 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scout-delivery.d.ts","sourceRoot":"","sources":["../src/scout-delivery.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scout-delivery.d.ts","sourceRoot":"","sources":["../src/scout-delivery.ts"],"names":[],"mappings":"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"}
|
|
@@ -109,6 +109,20 @@ export type HerdrWorkspaceProjection = {
|
|
|
109
109
|
agentStatus: HerdrAgentStatus;
|
|
110
110
|
tabs: HerdrTabProjection[];
|
|
111
111
|
};
|
|
112
|
+
/**
|
|
113
|
+
* Why a projection is not live.
|
|
114
|
+
*
|
|
115
|
+
* `not_running` is the ordinary case: no herdr server answers for this session,
|
|
116
|
+
* so its persisted layout is the honest thing to show.
|
|
117
|
+
*
|
|
118
|
+
* `unreadable` is the case worth naming. A herdr server for this session IS
|
|
119
|
+
* running, but it did not answer the topology commands — a protocol skew
|
|
120
|
+
* between the installed client and a long-lived server is the common cause,
|
|
121
|
+
* and a busy server or a timeout look the same from here. Collapsing that into
|
|
122
|
+
* `not_running` would state, of live work, that it is not running. The
|
|
123
|
+
* projection says which it is so a reader never has to guess.
|
|
124
|
+
*/
|
|
125
|
+
export type HerdrTopologyUnavailableReason = "not_running" | "unreadable";
|
|
112
126
|
export type HerdrSessionTopology = {
|
|
113
127
|
/** Herdr session name, e.g. "openscout". */
|
|
114
128
|
session: string;
|
|
@@ -123,6 +137,11 @@ export type HerdrSessionTopology = {
|
|
|
123
137
|
* projection is live.
|
|
124
138
|
*/
|
|
125
139
|
savedAt?: number | null;
|
|
140
|
+
/**
|
|
141
|
+
* Why this projection is not live. Set only when `running` is false; absent
|
|
142
|
+
* on a live read. See {@link HerdrTopologyUnavailableReason}.
|
|
143
|
+
*/
|
|
144
|
+
unavailable?: HerdrTopologyUnavailableReason | null;
|
|
126
145
|
};
|
|
127
|
-
export declare function emptyHerdrSessionTopology(session: string, running: boolean): HerdrSessionTopology;
|
|
146
|
+
export declare function emptyHerdrSessionTopology(session: string, running: boolean, unavailable?: HerdrTopologyUnavailableReason | null): HerdrSessionTopology;
|
|
128
147
|
//# sourceMappingURL=terminal-host-topology.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-host-topology.d.ts","sourceRoot":"","sources":["../src/terminal-host-topology.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"terminal-host-topology.d.ts","sourceRoot":"","sources":["../src/terminal-host-topology.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
* persisted state projects as `{ running: false, workspaces: [] }` — an
|
|
20
20
|
* ordinary state, not an error.
|
|
21
21
|
*/
|
|
22
|
-
export function emptyHerdrSessionTopology(session, running) {
|
|
23
|
-
return {
|
|
22
|
+
export function emptyHerdrSessionTopology(session, running, unavailable) {
|
|
23
|
+
return {
|
|
24
|
+
session,
|
|
25
|
+
running,
|
|
26
|
+
workspaces: [],
|
|
27
|
+
observedAt: Date.now(),
|
|
28
|
+
...(running ? {} : { unavailable: unavailable ?? "not_running" }),
|
|
29
|
+
};
|
|
24
30
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openscout/protocol",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.102",
|
|
4
4
|
"description": "Typed protocol for the OpenScout local control plane",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"typescript": "^5"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "c50900700bff66c1b1947d3413e54a7b1279d581"
|
|
61
61
|
}
|
package/src/broker-routes.ts
CHANGED
|
@@ -12,9 +12,16 @@ export const scoutBrokerPaths = {
|
|
|
12
12
|
capabilities: "/v1/capabilities",
|
|
13
13
|
runtimeCatalog: "/v1/runtime-catalog",
|
|
14
14
|
topologySnapshot: "/v1/topology/snapshot",
|
|
15
|
+
meshNodes: "/v1/mesh/nodes",
|
|
16
|
+
// Observe-tier compact node state. Read against a PEER broker's base URL
|
|
17
|
+
// through the signed/pinned mesh client; the local home feed is never
|
|
18
|
+
// widened for remote callers (mesh trust cone §4).
|
|
19
|
+
meshNodeState: "/v1/mesh/node-state",
|
|
15
20
|
tailDiscover: "/v1/tail/discover",
|
|
16
21
|
tailRecent: "/v1/tail/recent",
|
|
17
22
|
repoWatchSnapshot: "/v1/repo-watch/snapshot",
|
|
23
|
+
machines: "/v1/machines",
|
|
24
|
+
machinesScan: "/v1/machines/scan",
|
|
18
25
|
messages: "/v1/messages",
|
|
19
26
|
brokerMessages: "/v1/broker/messages",
|
|
20
27
|
eventsStream: "/v1/events/stream",
|
|
@@ -50,6 +57,11 @@ export function scoutBrokerMessagesPath(search: URLSearchParams): string {
|
|
|
50
57
|
: scoutBrokerPaths.v1.brokerMessages;
|
|
51
58
|
}
|
|
52
59
|
|
|
60
|
+
/** Name a conversation. An empty title hands it back to automatic naming. */
|
|
61
|
+
export function scoutBrokerConversationTitlePath(conversationId: string): string {
|
|
62
|
+
return `${scoutBrokerPaths.v1.conversations}/${encodeURIComponent(conversationId)}/title`;
|
|
63
|
+
}
|
|
64
|
+
|
|
53
65
|
export function scoutBrokerInvocationPath(invocationId: string): string {
|
|
54
66
|
return `${scoutBrokerPaths.v1.invocations}/${encodeURIComponent(invocationId)}`;
|
|
55
67
|
}
|
|
@@ -61,3 +73,7 @@ export function scoutBrokerInvocationStreamPath(invocationId: string): string {
|
|
|
61
73
|
export function scoutBrokerInvocationLifecyclePath(invocationId: string): string {
|
|
62
74
|
return `${scoutBrokerInvocationPath(invocationId)}/lifecycle`;
|
|
63
75
|
}
|
|
76
|
+
|
|
77
|
+
export function scoutBrokerMachinePath(machineRef: string): string {
|
|
78
|
+
return `${scoutBrokerPaths.v1.machines}/${encodeURIComponent(machineRef)}`;
|
|
79
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ConversationKind } from "./conversations.js";
|
|
2
2
|
import type { ObservedActivity } from "./observed-status.js";
|
|
3
3
|
|
|
4
|
-
export const CONVERSATION_PROJECTION_VERSION =
|
|
4
|
+
export const CONVERSATION_PROJECTION_VERSION = 2;
|
|
5
5
|
|
|
6
6
|
export type ConversationProjectionEntityKind =
|
|
7
7
|
| "scout_conversation"
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Herdr workspace digest — the presentation layer over {@link HerdrSessionTopology}.
|
|
3
|
+
*
|
|
4
|
+
* The raw projection is faithful but shapeless: a complex session is several
|
|
5
|
+
* workspaces × tabs × panes, and handing that whole tree to an operator (or to
|
|
6
|
+
* an assistant that has to answer for it) buries the one thing they came for.
|
|
7
|
+
* A digest ranks, groups, bounds, and describes it.
|
|
8
|
+
*
|
|
9
|
+
* Four commitments, in order of how much they matter:
|
|
10
|
+
*
|
|
11
|
+
* 1. **Attention leads.** A `blocked` pane is the only thing in a herdr session
|
|
12
|
+
* that is actually waiting on a person. It comes first, always, before any
|
|
13
|
+
* inventory. Everything else is context for it.
|
|
14
|
+
* 2. **Directory is the grouping axis, not the tab.** Operators think "which
|
|
15
|
+
* repo", herdr thinks "which tab". Grouping is by the pane's literal working
|
|
16
|
+
* directory — no repo-root inference, because a guessed grouping presented as
|
|
17
|
+
* a fact is worse than an honest fragmented one.
|
|
18
|
+
* 3. **Shape is described, not dumped.** A tab's layout becomes one readable
|
|
19
|
+
* phrase ("2×2 grid", "3 columns (2 · 1 · 1)") derived from the pane rects.
|
|
20
|
+
* 4. **Bounded and honest.** Panes are capped and truncation is declared;
|
|
21
|
+
* `unknown` never collapses into `done`; a persisted projection is labeled
|
|
22
|
+
* last-known rather than passed off as live.
|
|
23
|
+
*
|
|
24
|
+
* There are no mutation verbs here, for the same reason there are none in the
|
|
25
|
+
* topology projection: herdr owns workspaces, tabs and panes, and Scout's layer
|
|
26
|
+
* is coordination over whatever host is present. A digest pane carries the
|
|
27
|
+
* herdr `target` string so a caller can construct a handoff; performing it is
|
|
28
|
+
* the herdr client's job.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import type {
|
|
32
|
+
HerdrAgentStatus,
|
|
33
|
+
HerdrPaneProjection,
|
|
34
|
+
HerdrSessionTopology,
|
|
35
|
+
HerdrTabLayout,
|
|
36
|
+
HerdrTopologyUnavailableReason,
|
|
37
|
+
} from "./terminal-host-topology.js";
|
|
38
|
+
|
|
39
|
+
/** Total emitted panes across every group before the digest declares truncation. */
|
|
40
|
+
export const HERDR_DIGEST_PANE_CAP = 40;
|
|
41
|
+
/** Ceiling on each of the two lead lists; attention is a shortlist, not an inventory. */
|
|
42
|
+
export const HERDR_DIGEST_LEAD_CAP = 12;
|
|
43
|
+
|
|
44
|
+
export type HerdrStatusCounts = Record<HerdrAgentStatus, number>;
|
|
45
|
+
|
|
46
|
+
export type HerdrDigestPane = {
|
|
47
|
+
/**
|
|
48
|
+
* What `herdr agent <verb>` accepts as a target for this pane: the stable
|
|
49
|
+
* terminal id when herdr reported one, else the pane id. Null on a persisted
|
|
50
|
+
* projection, where neither is live — a pane you cannot address yet.
|
|
51
|
+
*/
|
|
52
|
+
target: string | null;
|
|
53
|
+
paneId: string;
|
|
54
|
+
workspaceId: string;
|
|
55
|
+
tabId: string;
|
|
56
|
+
label: string | null;
|
|
57
|
+
/** Detected agent label ("claude", "codex"), or null for a plain shell. */
|
|
58
|
+
agent: string | null;
|
|
59
|
+
status: HerdrAgentStatus;
|
|
60
|
+
/** Working directory: the foreground process's when known, else the pane's. */
|
|
61
|
+
directory: string | null;
|
|
62
|
+
focused: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Scrollback backlog depth in lines. The cheapest activity signal a
|
|
65
|
+
* projection carries — a deep backlog on an idle pane is work that already
|
|
66
|
+
* happened, not work happening. Null when herdr did not report scroll state.
|
|
67
|
+
*/
|
|
68
|
+
backlog: number | null;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export type HerdrDigestGroup = {
|
|
72
|
+
/** The panes' literal shared working directory; null when they report none. */
|
|
73
|
+
directory: string | null;
|
|
74
|
+
/** Last path segment — how an operator names the place. */
|
|
75
|
+
name: string;
|
|
76
|
+
counts: HerdrStatusCounts;
|
|
77
|
+
panes: HerdrDigestPane[];
|
|
78
|
+
/** Panes in this group dropped by the pane cap. */
|
|
79
|
+
omitted: number;
|
|
80
|
+
/**
|
|
81
|
+
* What was dropped, by status. A collapsed label has to carry its own scent:
|
|
82
|
+
* a reader decides whether the hidden rows matter from the label alone, so
|
|
83
|
+
* "2 idle not shown" is a usable label and "2 not shown" is not.
|
|
84
|
+
*/
|
|
85
|
+
omittedCounts: HerdrStatusCounts;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export type HerdrDigestShape = {
|
|
89
|
+
workspaceId: string;
|
|
90
|
+
workspaceLabel: string;
|
|
91
|
+
tabId: string;
|
|
92
|
+
tabLabel: string;
|
|
93
|
+
paneCount: number;
|
|
94
|
+
/** One readable phrase for the arrangement, e.g. "2×2 grid". */
|
|
95
|
+
arrangement: string;
|
|
96
|
+
zoomed: boolean;
|
|
97
|
+
focused: boolean;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type HerdrWorkspaceDigest = {
|
|
101
|
+
session: string;
|
|
102
|
+
/** True only for a projection read from a running herdr server. */
|
|
103
|
+
live: boolean;
|
|
104
|
+
observedAt: number;
|
|
105
|
+
/** Set when the projection is the persisted last-known state, not a live read. */
|
|
106
|
+
savedAt: number | null;
|
|
107
|
+
/**
|
|
108
|
+
* Why a non-live projection is not live. `unreadable` means a server for the
|
|
109
|
+
* session IS running and did not answer — the digest must not present that as
|
|
110
|
+
* a stopped session. Null on a live read.
|
|
111
|
+
*/
|
|
112
|
+
unavailable: HerdrTopologyUnavailableReason | null;
|
|
113
|
+
totals: {
|
|
114
|
+
workspaces: number;
|
|
115
|
+
tabs: number;
|
|
116
|
+
panes: number;
|
|
117
|
+
/** Panes herdr recognized an agent in. The rest are plain shells. */
|
|
118
|
+
agents: number;
|
|
119
|
+
};
|
|
120
|
+
counts: HerdrStatusCounts;
|
|
121
|
+
/** Blocked panes — the only entries that represent something waiting on a person. */
|
|
122
|
+
needsYou: HerdrDigestPane[];
|
|
123
|
+
/** Panes herdr observed as working, bounded. */
|
|
124
|
+
working: HerdrDigestPane[];
|
|
125
|
+
groups: HerdrDigestGroup[];
|
|
126
|
+
shapes: HerdrDigestShape[];
|
|
127
|
+
truncated: boolean;
|
|
128
|
+
/** Caveats the reader must carry with the numbers. Never decorative. */
|
|
129
|
+
notes: string[];
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const EMPTY_COUNTS: HerdrStatusCounts = { blocked: 0, working: 0, idle: 0, done: 0, unknown: 0 };
|
|
133
|
+
|
|
134
|
+
/** Attention order. Blocked outranks working because it is the only state that waits on a person. */
|
|
135
|
+
const STATUS_RANK: Record<HerdrAgentStatus, number> = {
|
|
136
|
+
blocked: 5,
|
|
137
|
+
working: 4,
|
|
138
|
+
idle: 3,
|
|
139
|
+
done: 2,
|
|
140
|
+
unknown: 1,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export function herdrStatusRank(status: HerdrAgentStatus): number {
|
|
144
|
+
return STATUS_RANK[status];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function emptyCounts(): HerdrStatusCounts {
|
|
148
|
+
return { ...EMPTY_COUNTS };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function totalCount(counts: HerdrStatusCounts): number {
|
|
152
|
+
return counts.blocked + counts.working + counts.idle + counts.done + counts.unknown;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function paneDirectory(pane: HerdrPaneProjection): string | null {
|
|
156
|
+
return pane.foregroundCwd ?? pane.cwd ?? null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function directoryName(directory: string | null): string {
|
|
160
|
+
if (!directory) return "no directory";
|
|
161
|
+
const segments = directory.replace(/\/+$/, "").split("/").filter(Boolean);
|
|
162
|
+
return segments[segments.length - 1] ?? directory;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function toDigestPane(pane: HerdrPaneProjection): HerdrDigestPane {
|
|
166
|
+
return {
|
|
167
|
+
target: pane.terminalId ?? pane.paneId ?? null,
|
|
168
|
+
paneId: pane.paneId,
|
|
169
|
+
workspaceId: pane.workspaceId,
|
|
170
|
+
tabId: pane.tabId,
|
|
171
|
+
label: pane.label,
|
|
172
|
+
agent: pane.agent,
|
|
173
|
+
status: pane.agentStatus,
|
|
174
|
+
directory: paneDirectory(pane),
|
|
175
|
+
focused: pane.focused,
|
|
176
|
+
backlog: pane.scroll?.maxOffsetFromBottom ?? null,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Rank panes for a lead list: attention state, then the focused pane (where the
|
|
182
|
+
* operator's eyes already are), then the deepest backlog, then pane id so the
|
|
183
|
+
* order never wobbles between polls.
|
|
184
|
+
*/
|
|
185
|
+
function compareLeadPanes(left: HerdrDigestPane, right: HerdrDigestPane): number {
|
|
186
|
+
const byStatus = STATUS_RANK[right.status] - STATUS_RANK[left.status];
|
|
187
|
+
if (byStatus !== 0) return byStatus;
|
|
188
|
+
if (left.focused !== right.focused) return left.focused ? -1 : 1;
|
|
189
|
+
const byBacklog = (right.backlog ?? -1) - (left.backlog ?? -1);
|
|
190
|
+
if (byBacklog !== 0) return byBacklog;
|
|
191
|
+
return left.paneId.localeCompare(right.paneId, undefined, { numeric: true });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Describe a tab's arrangement from its pane rects.
|
|
196
|
+
*
|
|
197
|
+
* Panes sharing an x start a column; panes sharing a y start a row. A layout
|
|
198
|
+
* whose columns all hold the same number of panes is a grid and is named as
|
|
199
|
+
* one; anything else is named by its columns and their depths, which is both
|
|
200
|
+
* shorter and more truthful than inventing a name for an irregular split.
|
|
201
|
+
*/
|
|
202
|
+
export function describeHerdrArrangement(
|
|
203
|
+
layout: HerdrTabLayout | null,
|
|
204
|
+
paneCount: number,
|
|
205
|
+
): string {
|
|
206
|
+
if (paneCount <= 0) return "no panes";
|
|
207
|
+
if (paneCount === 1) return "single pane";
|
|
208
|
+
const panes = layout?.panes ?? [];
|
|
209
|
+
if (panes.length !== paneCount) return `${paneCount} panes (layout unavailable)`;
|
|
210
|
+
|
|
211
|
+
const columns = new Map<number, number>();
|
|
212
|
+
const rows = new Map<number, number>();
|
|
213
|
+
for (const pane of panes) {
|
|
214
|
+
columns.set(pane.rect.x, (columns.get(pane.rect.x) ?? 0) + 1);
|
|
215
|
+
rows.set(pane.rect.y, (rows.get(pane.rect.y) ?? 0) + 1);
|
|
216
|
+
}
|
|
217
|
+
if (columns.size === 1) return `${paneCount} stacked rows`;
|
|
218
|
+
if (rows.size === 1) return `${paneCount} side-by-side columns`;
|
|
219
|
+
|
|
220
|
+
const depths = [...columns.entries()]
|
|
221
|
+
.sort((left, right) => left[0] - right[0])
|
|
222
|
+
.map(([, count]) => count);
|
|
223
|
+
const first = depths[0] ?? 0;
|
|
224
|
+
if (depths.every((depth) => depth === first) && first * columns.size === paneCount) {
|
|
225
|
+
return `${first}×${columns.size} grid`;
|
|
226
|
+
}
|
|
227
|
+
return `${columns.size} columns (${depths.join(" · ")})`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Build the digest for one herdr session topology.
|
|
232
|
+
*
|
|
233
|
+
* Pure: the same topology always digests to the same value, so the whole
|
|
234
|
+
* presentation is testable without a herdr server.
|
|
235
|
+
*/
|
|
236
|
+
export function digestHerdrTopology(
|
|
237
|
+
topology: HerdrSessionTopology,
|
|
238
|
+
options: { paneCap?: number; leadCap?: number } = {},
|
|
239
|
+
): HerdrWorkspaceDigest {
|
|
240
|
+
const paneCap = options.paneCap ?? HERDR_DIGEST_PANE_CAP;
|
|
241
|
+
const leadCap = options.leadCap ?? HERDR_DIGEST_LEAD_CAP;
|
|
242
|
+
|
|
243
|
+
const counts = emptyCounts();
|
|
244
|
+
const shapes: HerdrDigestShape[] = [];
|
|
245
|
+
const all: HerdrDigestPane[] = [];
|
|
246
|
+
let tabTotal = 0;
|
|
247
|
+
let agentTotal = 0;
|
|
248
|
+
|
|
249
|
+
for (const workspace of topology.workspaces) {
|
|
250
|
+
const workspaceLabel = workspace.label ?? (workspace.number != null ? `workspace ${workspace.number}` : workspace.workspaceId);
|
|
251
|
+
for (const tab of workspace.tabs) {
|
|
252
|
+
tabTotal += 1;
|
|
253
|
+
shapes.push({
|
|
254
|
+
workspaceId: workspace.workspaceId,
|
|
255
|
+
workspaceLabel,
|
|
256
|
+
tabId: tab.tabId,
|
|
257
|
+
tabLabel: tab.label ?? (tab.number != null ? `tab ${tab.number}` : tab.tabId),
|
|
258
|
+
paneCount: tab.panes.length,
|
|
259
|
+
arrangement: describeHerdrArrangement(tab.layout, tab.panes.length),
|
|
260
|
+
zoomed: tab.layout?.zoomed ?? false,
|
|
261
|
+
focused: workspace.focused && tab.focused,
|
|
262
|
+
});
|
|
263
|
+
for (const pane of tab.panes) {
|
|
264
|
+
const digestPane = toDigestPane(pane);
|
|
265
|
+
counts[digestPane.status] += 1;
|
|
266
|
+
if (digestPane.agent) agentTotal += 1;
|
|
267
|
+
all.push(digestPane);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Groups are filled in rank order so the pane cap spends its budget on the
|
|
273
|
+
// panes that matter, not on whichever directory herdr happened to list first.
|
|
274
|
+
const ranked = [...all].sort(compareLeadPanes);
|
|
275
|
+
const kept = ranked.slice(0, paneCap);
|
|
276
|
+
const droppedByDirectory = new Map<string, HerdrStatusCounts>();
|
|
277
|
+
for (const pane of ranked.slice(paneCap)) {
|
|
278
|
+
const key = pane.directory ?? "";
|
|
279
|
+
let dropped = droppedByDirectory.get(key);
|
|
280
|
+
if (!dropped) {
|
|
281
|
+
dropped = emptyCounts();
|
|
282
|
+
droppedByDirectory.set(key, dropped);
|
|
283
|
+
}
|
|
284
|
+
dropped[pane.status] += 1;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const groupsByDirectory = new Map<string, HerdrDigestGroup>();
|
|
288
|
+
for (const pane of all) {
|
|
289
|
+
const key = pane.directory ?? "";
|
|
290
|
+
let group = groupsByDirectory.get(key);
|
|
291
|
+
if (!group) {
|
|
292
|
+
group = {
|
|
293
|
+
directory: pane.directory,
|
|
294
|
+
name: directoryName(pane.directory),
|
|
295
|
+
counts: emptyCounts(),
|
|
296
|
+
panes: [],
|
|
297
|
+
omitted: totalCount(droppedByDirectory.get(key) ?? EMPTY_COUNTS),
|
|
298
|
+
omittedCounts: { ...(droppedByDirectory.get(key) ?? EMPTY_COUNTS) },
|
|
299
|
+
};
|
|
300
|
+
groupsByDirectory.set(key, group);
|
|
301
|
+
}
|
|
302
|
+
group.counts[pane.status] += 1;
|
|
303
|
+
}
|
|
304
|
+
const keptIds = new Set(kept.map((pane) => `${pane.workspaceId}/${pane.paneId}`));
|
|
305
|
+
for (const pane of all) {
|
|
306
|
+
if (!keptIds.has(`${pane.workspaceId}/${pane.paneId}`)) continue;
|
|
307
|
+
groupsByDirectory.get(pane.directory ?? "")?.panes.push(pane);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const groups = [...groupsByDirectory.values()];
|
|
311
|
+
for (const group of groups) group.panes.sort(compareLeadPanes);
|
|
312
|
+
// Groups sort by what is waiting, then what is moving, then size — the same
|
|
313
|
+
// precedence the lead lists use, applied one level up.
|
|
314
|
+
groups.sort((left, right) =>
|
|
315
|
+
right.counts.blocked - left.counts.blocked
|
|
316
|
+
|| right.counts.working - left.counts.working
|
|
317
|
+
|| (right.panes.length + right.omitted) - (left.panes.length + left.omitted)
|
|
318
|
+
|| left.name.localeCompare(right.name, undefined, { numeric: true }));
|
|
319
|
+
|
|
320
|
+
const notes: string[] = [];
|
|
321
|
+
if (!topology.running) {
|
|
322
|
+
// "Last known" is only useful with a when: a layout saved an hour ago and
|
|
323
|
+
// one saved in March are the same claim with very different weight.
|
|
324
|
+
const saved = topology.savedAt ? ` Last saved ${new Date(topology.savedAt).toISOString()}.` : "";
|
|
325
|
+
notes.push(topology.unavailable === "unreadable"
|
|
326
|
+
// The session IS running; herdr just would not answer. Saying "stopped"
|
|
327
|
+
// here would be the one thing a digest must never do — state, of live
|
|
328
|
+
// work, that it is not live.
|
|
329
|
+
? `A herdr server for this session is running but did not answer, so this is the persisted last-known layout, not what is on screen. The usual cause is a protocol skew between the installed herdr client and a long-lived server; restarting that session's server (which exits its pane processes) is what clears it.${saved}`
|
|
330
|
+
: `Not live: this is the persisted last-known layout. Agent status, terminal ids and geometry are absent from it — no pane here is claimed to be running.${saved}`);
|
|
331
|
+
}
|
|
332
|
+
if (counts.unknown > 0) {
|
|
333
|
+
notes.push(
|
|
334
|
+
`${counts.unknown} ${counts.unknown === 1 ? "pane reports" : "panes report"} unknown: herdr sees an agent it cannot classify. Unknown is the absence of a signal, not completion.`,
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
session: topology.session,
|
|
340
|
+
live: topology.running,
|
|
341
|
+
observedAt: topology.observedAt,
|
|
342
|
+
savedAt: topology.savedAt ?? null,
|
|
343
|
+
unavailable: topology.running ? null : topology.unavailable ?? "not_running",
|
|
344
|
+
totals: {
|
|
345
|
+
workspaces: topology.workspaces.length,
|
|
346
|
+
tabs: tabTotal,
|
|
347
|
+
panes: all.length,
|
|
348
|
+
agents: agentTotal,
|
|
349
|
+
},
|
|
350
|
+
counts,
|
|
351
|
+
needsYou: ranked.filter((pane) => pane.status === "blocked").slice(0, leadCap),
|
|
352
|
+
working: ranked.filter((pane) => pane.status === "working").slice(0, leadCap),
|
|
353
|
+
groups,
|
|
354
|
+
shapes,
|
|
355
|
+
truncated: all.length > paneCap,
|
|
356
|
+
notes,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* One row, led by whatever is most specific to this pane.
|
|
362
|
+
*
|
|
363
|
+
* Rows that all begin `term-w1:p1`, `term-w1:p2` share a long prefix, and a
|
|
364
|
+
* scanning reader learns to skip the opening of every line — so the identity
|
|
365
|
+
* column becomes the one column nobody reads. The lead is therefore the pane's
|
|
366
|
+
* label when it has one, and its pane id when it does not, since a short id
|
|
367
|
+
* differs within the first couple of characters where a terminal id does not.
|
|
368
|
+
*
|
|
369
|
+
* The pane id is enough to act on: `herdr agent <verb>` accepts the pane
|
|
370
|
+
* currently hosting an agent as a target. The directory is the basename only —
|
|
371
|
+
* the full path is carried once, in the grouped section, and repeating it on
|
|
372
|
+
* every row buys nothing.
|
|
373
|
+
*/
|
|
374
|
+
function paneLine(pane: HerdrDigestPane): string {
|
|
375
|
+
const parts = pane.label
|
|
376
|
+
? [pane.label, pane.agent ?? "shell", pane.paneId]
|
|
377
|
+
: [pane.paneId, pane.agent ?? "shell"];
|
|
378
|
+
if (pane.directory) parts.push(directoryName(pane.directory));
|
|
379
|
+
if (pane.focused) parts.push("focused");
|
|
380
|
+
return ` ${parts.join(" ")}`;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** "2 idle not shown" — a collapsed label a reader can decide from. */
|
|
384
|
+
function omittedLabel(counts: HerdrStatusCounts): string | null {
|
|
385
|
+
const total = totalCount(counts);
|
|
386
|
+
if (total === 0) return null;
|
|
387
|
+
const order: HerdrAgentStatus[] = ["blocked", "working", "idle", "done", "unknown"];
|
|
388
|
+
const present = order.filter((status) => counts[status] > 0);
|
|
389
|
+
if (present.length === 1) return `+${total} ${present[0]} not shown`;
|
|
390
|
+
return `+${total} not shown (${present.map((status) => `${counts[status]} ${status}`).join(" · ")})`;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function countsLine(counts: HerdrStatusCounts): string {
|
|
394
|
+
const order: HerdrAgentStatus[] = ["blocked", "working", "idle", "done", "unknown"];
|
|
395
|
+
const parts = order.filter((status) => counts[status] > 0).map((status) => `${counts[status]} ${status}`);
|
|
396
|
+
return parts.length ? parts.join(" · ") : "no panes";
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Render a digest as the text an operator should actually read.
|
|
401
|
+
*
|
|
402
|
+
* Ordering is the whole design: what is waiting, then what is moving, then
|
|
403
|
+
* where things live, then how the screen is arranged. A reader who stops after
|
|
404
|
+
* the first section has still got the answer that mattered.
|
|
405
|
+
*
|
|
406
|
+
* Section heads are verb phrases rather than bare nouns, and there is no
|
|
407
|
+
* trailing caveat block: a qualifier that changes how a count should be read
|
|
408
|
+
* sits directly under that count, because a scanning reader reaches the top of
|
|
409
|
+
* the output and not the bottom of it.
|
|
410
|
+
*/
|
|
411
|
+
export function renderHerdrWorkspaceDigest(digest: HerdrWorkspaceDigest): string {
|
|
412
|
+
const lines: string[] = [];
|
|
413
|
+
const state = digest.live
|
|
414
|
+
? "live"
|
|
415
|
+
: digest.unavailable === "unreadable" ? "running, not readable" : "not running";
|
|
416
|
+
const { workspaces, tabs, panes } = digest.totals;
|
|
417
|
+
lines.push(
|
|
418
|
+
`herdr · ${digest.session} — ${state} · ${workspaces} workspace${workspaces === 1 ? "" : "s"}, ${tabs} tab${tabs === 1 ? "" : "s"}, ${panes} pane${panes === 1 ? "" : "s"} (${countsLine(digest.counts)})`,
|
|
419
|
+
);
|
|
420
|
+
// Caveats sit under the counts they qualify, not in a block at the bottom.
|
|
421
|
+
// A qualifier that changes how a number should be read is body text; a
|
|
422
|
+
// scanning reader never reaches a footnote, and these are the lines that stop
|
|
423
|
+
// someone believing `unknown` meant done or that a stale layout was current.
|
|
424
|
+
for (const note of digest.notes) lines.push(` ${note}`);
|
|
425
|
+
|
|
426
|
+
if (digest.needsYou.length) {
|
|
427
|
+
lines.push("", "Waiting on you");
|
|
428
|
+
for (const pane of digest.needsYou) lines.push(paneLine(pane));
|
|
429
|
+
}
|
|
430
|
+
if (digest.working.length) {
|
|
431
|
+
lines.push("", "Working now");
|
|
432
|
+
for (const pane of digest.working) lines.push(paneLine(pane));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (digest.groups.length) {
|
|
436
|
+
lines.push("", "Grouped by directory");
|
|
437
|
+
for (const group of digest.groups) {
|
|
438
|
+
const total = totalCount(group.counts);
|
|
439
|
+
const where = group.directory ? ` (${group.directory})` : "";
|
|
440
|
+
const hidden = omittedLabel(group.omittedCounts);
|
|
441
|
+
lines.push(` ${group.name}${where} — ${total} pane${total === 1 ? "" : "s"} · ${countsLine(group.counts)}${hidden ? ` · ${hidden}` : ""}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (digest.shapes.length) {
|
|
446
|
+
lines.push("", "Arranged like this");
|
|
447
|
+
for (const shape of digest.shapes) {
|
|
448
|
+
const marks = [shape.arrangement];
|
|
449
|
+
if (shape.zoomed) marks.push("zoomed");
|
|
450
|
+
if (shape.focused) marks.push("focused");
|
|
451
|
+
// The tab id leads because two workspaces can carry the same label, and
|
|
452
|
+
// a label can itself contain the separator — the id cannot do either.
|
|
453
|
+
lines.push(` ${shape.tabId} ${shape.workspaceLabel} / ${shape.tabLabel} — ${marks.join(" · ")}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return lines.join("\n");
|
|
458
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./a2a.js";
|
|
|
7
7
|
export * from "./scout-agent-card.js";
|
|
8
8
|
export * from "./relay-agent-card.js";
|
|
9
9
|
export * from "./mesh.js";
|
|
10
|
+
export * from "./machines.js";
|
|
10
11
|
export * from "./conversations.js";
|
|
11
12
|
export * from "./conversation-projection.js";
|
|
12
13
|
export * from "./channel-identity.js";
|
|
@@ -53,5 +54,6 @@ export * from "./terminal-surface-id.js";
|
|
|
53
54
|
export * from "./terminal-sessions.js";
|
|
54
55
|
export * from "./terminal-workspaces.js";
|
|
55
56
|
export * from "./terminal-host-topology.js";
|
|
57
|
+
export * from "./herdr-workspace-digest.js";
|
|
56
58
|
export * from "./broker-routes.js";
|
|
57
59
|
export * from "./rendezvous.js";
|