@haywoodmarx/agent-pager 0.1.2 → 0.1.3
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 +1 -1
- package/dist/src/launch/broker-lock.d.ts +6 -0
- package/dist/src/launch/broker-lock.js +9 -0
- package/dist/src/launch/cli.js +5 -2
- package/dist/src/operator/protocol.d.ts +2 -0
- package/dist/src/operator/updates.js +2 -0
- package/dist/src/operator/validation.js +11 -1
- package/dist/src/tui/app.js +3 -2
- package/dist/src/tui/model.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ Pager does not change a harness's permission settings. A session that has to wai
|
|
|
62
62
|
|
|
63
63
|
## Service operations
|
|
64
64
|
|
|
65
|
-
The broker starts on demand: `pager` with no command, and every harness connecting over MCP, adopts the running broker or starts one. `pager status` reports whether the broker is running, its pid, its MCP endpoint,
|
|
65
|
+
The broker starts on demand: `pager` with no command, and every harness connecting over MCP, adopts the running broker or starts one. `pager status` reports whether the broker is running, its pid, its MCP endpoint, the database it serves, the installed pager version, and the version of the running broker. `pager exit` stops it, and `pager restart` stops it and starts a fresh one. `pager serve` runs the broker in the foreground.
|
|
66
66
|
|
|
67
67
|
Callers adopt a running service only when its database-adjacent lock and `/health` agree on the service identity; concurrent starters race for that lock and every caller adopts the winner. The adoption rules are declared in [`src/launch/on-demand.ts`](src/launch/on-demand.ts) and [`src/launch/broker-lock.ts`](src/launch/broker-lock.ts).
|
|
68
68
|
|
|
@@ -83,6 +83,12 @@ export declare function brokerLockPath(databasePath: string): string;
|
|
|
83
83
|
* @returns - Valid parsed lock data when the file is well-formed.
|
|
84
84
|
*/
|
|
85
85
|
export declare function readBrokerLock(lockPath: string): BrokerLock | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Name the package version of the build that wrote a lock record.
|
|
88
|
+
* @param lock - Lock record, possibly written by a foreign build.
|
|
89
|
+
* @returns - The version after the build id's last `@`, or the whole build id when it names no version.
|
|
90
|
+
*/
|
|
91
|
+
export declare function brokerLockVersion(lock: Pick<BrokerLock, "buildId">): string;
|
|
86
92
|
/** Bounds how many times acquisition re-attempts after losing a lock-file race. */
|
|
87
93
|
export declare const BROKER_LOCK_ACQUISITION_ATTEMPTS = 5;
|
|
88
94
|
/** Reports the start time of a process, or that this platform cannot supply one. */
|
|
@@ -117,6 +117,15 @@ export function readBrokerLock(lockPath) {
|
|
|
117
117
|
return undefined;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Name the package version of the build that wrote a lock record.
|
|
122
|
+
* @param lock - Lock record, possibly written by a foreign build.
|
|
123
|
+
* @returns - The version after the build id's last `@`, or the whole build id when it names no version.
|
|
124
|
+
*/
|
|
125
|
+
export function brokerLockVersion(lock) {
|
|
126
|
+
const separator = lock.buildId.lastIndexOf("@");
|
|
127
|
+
return separator > 0 ? lock.buildId.slice(separator + 1) : lock.buildId;
|
|
128
|
+
}
|
|
120
129
|
/**
|
|
121
130
|
* Structural gate, not a currency check: `serviceProtocolVersion`, `runtime`, and `protocolVersion`
|
|
122
131
|
* must equal the current build's because a mismatch there means the rest of the shape cannot be
|
package/dist/src/launch/cli.js
CHANGED
|
@@ -10,8 +10,9 @@ import { packagedSkills } from "./skill.js";
|
|
|
10
10
|
import { createOperatorTuiClient, fileBackedTuiLayout, DEFAULT_OPERATOR_TUI_THEME, startOperatorTui, } from "../tui/production.js";
|
|
11
11
|
import { assertSupportedBunRuntime } from "./runtime.js";
|
|
12
12
|
import { BrokerService } from "./service.js";
|
|
13
|
+
import { PAGER_VERSION } from "../version.js";
|
|
13
14
|
import { systemBrokerServiceFilesystem, systemBrokerServiceScheduler } from "./service-system.js";
|
|
14
|
-
import { BrokerLockGuard, brokerLockPath, brokerLockHasCurrentIdentity, brokerLockIsLive, processIsAlive, readBrokerLock, readBrokerOwnerPid, } from "./broker-lock.js";
|
|
15
|
+
import { BrokerLockGuard, brokerLockPath, brokerLockHasCurrentIdentity, brokerLockIsLive, brokerLockVersion, processIsAlive, readBrokerLock, readBrokerOwnerPid, } from "./broker-lock.js";
|
|
15
16
|
/**
|
|
16
17
|
* Run the operational CLI.
|
|
17
18
|
*
|
|
@@ -55,6 +56,8 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
55
56
|
state: running ? "running" : starting ? "starting" : ownerAlive ? "incompatible" : "stopped",
|
|
56
57
|
...(ownerAlive ? { pid: ownerPid } : {}),
|
|
57
58
|
...(running && lock.mcpUrl ? { mcpUrl: lock.mcpUrl } : {}),
|
|
59
|
+
version: PAGER_VERSION,
|
|
60
|
+
...(ownerAlive && lock ? { brokerVersion: brokerLockVersion(lock) } : {}),
|
|
58
61
|
dbPath,
|
|
59
62
|
}));
|
|
60
63
|
break;
|
|
@@ -278,7 +281,7 @@ function usage(implementations) {
|
|
|
278
281
|
"pager open the operator TUI",
|
|
279
282
|
"pager <harness> [--session-name <name>] [-- <harness arguments>]",
|
|
280
283
|
` start a harness attached to the broker (installed: ${harnesses || "none"})`,
|
|
281
|
-
"pager status broker state, pid, MCP endpoint and
|
|
284
|
+
"pager status broker state, pid, MCP endpoint, database and versions",
|
|
282
285
|
"pager exit stop the broker",
|
|
283
286
|
"pager restart stop the broker and start a fresh one",
|
|
284
287
|
"pager serve run the broker in the foreground",
|
|
@@ -250,6 +250,8 @@ export type OperatorSnapshot = {
|
|
|
250
250
|
protocolVersion: typeof OPERATOR_PROTOCOL_VERSION;
|
|
251
251
|
/** Identifies the broker generation that produced the snapshot. */
|
|
252
252
|
epoch: string;
|
|
253
|
+
/** Names the published package version of the broker that produced the snapshot. */
|
|
254
|
+
brokerVersion: string;
|
|
253
255
|
/** Identifies the last event already reflected by this snapshot. */
|
|
254
256
|
cursor: OperatorCursor;
|
|
255
257
|
/** Records when the projection was generated. */
|
|
@@ -2,6 +2,7 @@ import { projectOperatorAgents } from "./agent-projection.js";
|
|
|
2
2
|
import { OPERATOR_PROTOCOL_VERSION, } from "./protocol.js";
|
|
3
3
|
import { operatorDiagnostic } from "./diagnostics.js";
|
|
4
4
|
import { projectOperatorTraffic } from "./traffic-projection.js";
|
|
5
|
+
import { PAGER_VERSION } from "../version.js";
|
|
5
6
|
/** Bounds retained stream-integrity diagnostics. */
|
|
6
7
|
const UPDATE_DIAGNOSTIC_LIMIT = 64;
|
|
7
8
|
/** Broker tables whose rows feed the repository and agent halves of an operator snapshot. */
|
|
@@ -181,6 +182,7 @@ export class OperatorUpdateService {
|
|
|
181
182
|
return {
|
|
182
183
|
protocolVersion: OPERATOR_PROTOCOL_VERSION,
|
|
183
184
|
epoch: cursor.epoch,
|
|
185
|
+
brokerVersion: PAGER_VERSION,
|
|
184
186
|
cursor,
|
|
185
187
|
generatedAt: this.#now(),
|
|
186
188
|
repositories: projected.repositories,
|
|
@@ -173,7 +173,16 @@ export function validateOperatorSnapshot(value) {
|
|
|
173
173
|
const record = readRecord(value, "snapshot");
|
|
174
174
|
exactFields({
|
|
175
175
|
record,
|
|
176
|
-
required: [
|
|
176
|
+
required: [
|
|
177
|
+
"protocolVersion",
|
|
178
|
+
"epoch",
|
|
179
|
+
"brokerVersion",
|
|
180
|
+
"cursor",
|
|
181
|
+
"generatedAt",
|
|
182
|
+
"repositories",
|
|
183
|
+
"agents",
|
|
184
|
+
"traffic",
|
|
185
|
+
],
|
|
177
186
|
optional: [],
|
|
178
187
|
path: "snapshot",
|
|
179
188
|
});
|
|
@@ -207,6 +216,7 @@ export function validateOperatorSnapshot(value) {
|
|
|
207
216
|
const snapshot = {
|
|
208
217
|
protocolVersion,
|
|
209
218
|
epoch,
|
|
219
|
+
brokerVersion: readNonemptyString(record.brokerVersion, "snapshot.brokerVersion"),
|
|
210
220
|
cursor,
|
|
211
221
|
generatedAt: readTimestamp(record.generatedAt, "snapshot.generatedAt"),
|
|
212
222
|
repositories,
|
package/dist/src/tui/app.js
CHANGED
|
@@ -8,6 +8,7 @@ import { Line, Overlay, PaneBar, Rule, VERTICAL_SCROLL_STYLE, useTuiModel } from
|
|
|
8
8
|
import { AgentTrafficLog, MessageDetail, MessageOverlays, selectNewestTuiTraffic, stepTuiTrafficSelection, } from "./message-views.js";
|
|
9
9
|
import { AGENT_TREE_COLUMNS, sidePaneWidths, MESSAGE_LOG_COLUMNS, PORTRAIT_BREAKPOINT_COLUMNS, agentColor, activeAgentCount, agentLogScope, brokerIndicator, COLLAPSED_LOG_ROWS, describeCommandOutcome, diagnosticColor, fitBarLeading, formatClock, formatDiagnostic, selectedMessage, selectMessageBodyRows, selectTuiTraffic, } from "./presentation.js";
|
|
10
10
|
import { dimTuiTheme } from "./theme.js";
|
|
11
|
+
import { PAGER_VERSION } from "../version.js";
|
|
11
12
|
/* eslint-enable jsdoc/require-jsdoc -- The collaborator declarations end here. */
|
|
12
13
|
/** Rows the wide layout spends on fixed chrome rather than pane content. */
|
|
13
14
|
const WIDE_PANE_CHROME_ROWS = 7;
|
|
@@ -274,11 +275,11 @@ function FollowFooter({ state, theme }) {
|
|
|
274
275
|
}
|
|
275
276
|
function StatusBar({ state, theme }) {
|
|
276
277
|
const broker = brokerIndicator(state.connection, theme);
|
|
277
|
-
return (_jsxs("box", { height: 1, flexShrink: 0, flexDirection: "row", paddingLeft: 1, paddingRight: 1, justifyContent: "space-between", children: [_jsx("text", { fg: theme.structure.emphasis, children: "pager" }), _jsx("box", { flexDirection: "row", children: _jsxs("text", { fg: theme.structure.rulesAndLabels, children: [_jsx("span", { fg: broker.color, children: broker.glyph }), " broker ", `${String(state.unreadMessageIds.size)} unread ${String(activeAgentCount(state))} agents`] }) })] }));
|
|
278
|
+
return (_jsxs("box", { height: 1, flexShrink: 0, flexDirection: "row", paddingLeft: 1, paddingRight: 1, justifyContent: "space-between", children: [_jsx("text", { fg: theme.structure.emphasis, children: "pager" }), _jsx("box", { flexDirection: "row", children: _jsxs("text", { fg: theme.structure.rulesAndLabels, children: [_jsx("span", { fg: broker.color, children: broker.glyph }), " broker ", `${String(state.unreadMessageIds.size)} unread ${String(activeAgentCount(state))} agents v${PAGER_VERSION}`] }) })] }));
|
|
278
279
|
}
|
|
279
280
|
function PortraitStatusBar({ state, theme }) {
|
|
280
281
|
const broker = brokerIndicator(state.connection, theme);
|
|
281
|
-
return (_jsx(PaneBar, { left: _jsx("text", { fg: theme.structure.emphasis, children: "pager" }), right: _jsxs("text", { fg: theme.structure.rulesAndLabels, children: [_jsx("span", { fg: broker.color, children: broker.glyph }), ` ${String(state.unreadMessageIds.size)} unread · ${String(activeAgentCount(state))} agents`] }) }));
|
|
282
|
+
return (_jsx(PaneBar, { left: _jsx("text", { fg: theme.structure.emphasis, children: "pager" }), right: _jsxs("text", { fg: theme.structure.rulesAndLabels, children: [_jsx("span", { fg: broker.color, children: broker.glyph }), ` ${String(state.unreadMessageIds.size)} unread · ${String(activeAgentCount(state))} agents · v${PAGER_VERSION}`] }) }));
|
|
282
283
|
}
|
|
283
284
|
const HELP_KEYS = [
|
|
284
285
|
["↑↓", "move within the focused pane"],
|
package/dist/src/tui/model.js
CHANGED
|
@@ -349,7 +349,7 @@ export class TuiOperatorModel {
|
|
|
349
349
|
if (!this.#active(generation))
|
|
350
350
|
return;
|
|
351
351
|
if (this.#state.connection !== "healthy")
|
|
352
|
-
this.reportDiagnostic("info", "connection", `connected: snapshot at sequence ${String(snapshot.cursor.sequence)}`);
|
|
352
|
+
this.reportDiagnostic("info", "connection", `connected: broker ${snapshot.brokerVersion}, snapshot at sequence ${String(snapshot.cursor.sequence)}`);
|
|
353
353
|
// A fetched snapshot is installed as a fresh start rather than a replacement, so unread identities
|
|
354
354
|
// begin empty from it.
|
|
355
355
|
this.observeSnapshot(structuredClone(snapshot), false);
|