@haywoodmarx/agent-pager 0.1.0
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/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/src/broker/address.d.ts +49 -0
- package/dist/src/broker/address.js +110 -0
- package/dist/src/broker/broker.d.ts +665 -0
- package/dist/src/broker/broker.js +2270 -0
- package/dist/src/broker/database.d.ts +81 -0
- package/dist/src/broker/database.js +69 -0
- package/dist/src/broker/operator-projection.d.ts +291 -0
- package/dist/src/broker/operator-projection.js +722 -0
- package/dist/src/broker/protocol.d.ts +237 -0
- package/dist/src/broker/protocol.js +311 -0
- package/dist/src/broker/schema.d.ts +94 -0
- package/dist/src/broker/schema.js +454 -0
- package/dist/src/broker/snapshot.d.ts +171 -0
- package/dist/src/broker/snapshot.js +78 -0
- package/dist/src/configuration-defaults.d.ts +64 -0
- package/dist/src/configuration-defaults.js +48 -0
- package/dist/src/configuration.d.ts +76 -0
- package/dist/src/configuration.js +156 -0
- package/dist/src/implementations/claude/environment.d.ts +8 -0
- package/dist/src/implementations/claude/environment.js +9 -0
- package/dist/src/implementations/claude/gateway-cli.d.ts +2 -0
- package/dist/src/implementations/claude/gateway-cli.js +136 -0
- package/dist/src/implementations/claude/gateway-identity.d.ts +134 -0
- package/dist/src/implementations/claude/gateway-identity.js +103 -0
- package/dist/src/implementations/claude/gateway.d.ts +148 -0
- package/dist/src/implementations/claude/gateway.js +236 -0
- package/dist/src/implementations/claude/instructions.d.ts +33 -0
- package/dist/src/implementations/claude/instructions.js +64 -0
- package/dist/src/implementations/claude/manifest.d.ts +33 -0
- package/dist/src/implementations/claude/manifest.js +108 -0
- package/dist/src/implementations/claude/plugin-directory.d.ts +80 -0
- package/dist/src/implementations/claude/plugin-directory.js +137 -0
- package/dist/src/implementations/claude/session-continuation.d.ts +25 -0
- package/dist/src/implementations/claude/session-continuation.js +125 -0
- package/dist/src/implementations/claude/session-identity.d.ts +25 -0
- package/dist/src/implementations/claude/session-identity.js +31 -0
- package/dist/src/implementations/claude/session-start-cli.d.ts +2 -0
- package/dist/src/implementations/claude/session-start-cli.js +16 -0
- package/dist/src/implementations/claude/socket-delivery.d.ts +52 -0
- package/dist/src/implementations/claude/socket-delivery.js +159 -0
- package/dist/src/implementations/model.d.ts +162 -0
- package/dist/src/implementations/model.js +1 -0
- package/dist/src/implementations/opencode/adapter.d.ts +71 -0
- package/dist/src/implementations/opencode/adapter.js +94 -0
- package/dist/src/implementations/opencode/environment.d.ts +8 -0
- package/dist/src/implementations/opencode/environment.js +9 -0
- package/dist/src/implementations/opencode/manifest.d.ts +30 -0
- package/dist/src/implementations/opencode/manifest.js +93 -0
- package/dist/src/implementations/opencode/plugin-api.d.ts +120 -0
- package/dist/src/implementations/opencode/plugin-api.js +1 -0
- package/dist/src/implementations/opencode/plugin.d.ts +38 -0
- package/dist/src/implementations/opencode/plugin.js +303 -0
- package/dist/src/implementations/opencode/self-close-tool.d.ts +9 -0
- package/dist/src/implementations/opencode/self-close-tool.js +31 -0
- package/dist/src/implementations/orca/terminal.d.ts +48 -0
- package/dist/src/implementations/orca/terminal.js +154 -0
- package/dist/src/implementations/pi/broker-client.d.ts +56 -0
- package/dist/src/implementations/pi/broker-client.js +156 -0
- package/dist/src/implementations/pi/environment.d.ts +8 -0
- package/dist/src/implementations/pi/environment.js +9 -0
- package/dist/src/implementations/pi/extension.d.ts +57 -0
- package/dist/src/implementations/pi/extension.js +253 -0
- package/dist/src/implementations/pi/host-slots.d.ts +18 -0
- package/dist/src/implementations/pi/host-slots.js +27 -0
- package/dist/src/implementations/pi/manifest.d.ts +11 -0
- package/dist/src/implementations/pi/manifest.js +61 -0
- package/dist/src/implementations/pi/self-close-tool.d.ts +7 -0
- package/dist/src/implementations/pi/self-close-tool.js +27 -0
- package/dist/src/implementations/registry.d.ts +47 -0
- package/dist/src/implementations/registry.js +157 -0
- package/dist/src/implementations/session-key.d.ts +13 -0
- package/dist/src/implementations/session-key.js +19 -0
- package/dist/src/index.d.ts +27 -0
- package/dist/src/index.js +26 -0
- package/dist/src/launch/broker-lock.d.ts +257 -0
- package/dist/src/launch/broker-lock.js +644 -0
- package/dist/src/launch/cli.d.ts +37 -0
- package/dist/src/launch/cli.js +317 -0
- package/dist/src/launch/environment.d.ts +45 -0
- package/dist/src/launch/environment.js +48 -0
- package/dist/src/launch/launch.d.ts +235 -0
- package/dist/src/launch/launch.js +223 -0
- package/dist/src/launch/on-demand.d.ts +202 -0
- package/dist/src/launch/on-demand.js +435 -0
- package/dist/src/launch/runtime-composition.d.ts +88 -0
- package/dist/src/launch/runtime-composition.js +253 -0
- package/dist/src/launch/runtime.d.ts +79 -0
- package/dist/src/launch/runtime.js +196 -0
- package/dist/src/launch/service-system.d.ts +11 -0
- package/dist/src/launch/service-system.js +39 -0
- package/dist/src/launch/service.d.ts +173 -0
- package/dist/src/launch/service.js +492 -0
- package/dist/src/launch/skill.d.ts +57 -0
- package/dist/src/launch/skill.js +81 -0
- package/dist/src/launch/terminal.d.ts +26 -0
- package/dist/src/launch/terminal.js +43 -0
- package/dist/src/launch/workspace-branch.d.ts +10 -0
- package/dist/src/launch/workspace-branch.js +29 -0
- package/dist/src/messaging/delivery-client.d.ts +144 -0
- package/dist/src/messaging/delivery-client.js +261 -0
- package/dist/src/messaging/delivery.d.ts +75 -0
- package/dist/src/messaging/delivery.js +22 -0
- package/dist/src/messaging/inbox-socket.d.ts +98 -0
- package/dist/src/messaging/inbox-socket.js +278 -0
- package/dist/src/messaging/mcp.d.ts +53 -0
- package/dist/src/messaging/mcp.js +501 -0
- package/dist/src/messaging/notifications.d.ts +231 -0
- package/dist/src/messaging/notifications.js +489 -0
- package/dist/src/messaging/push-delivery.d.ts +77 -0
- package/dist/src/messaging/push-delivery.js +187 -0
- package/dist/src/messaging/tools.d.ts +11 -0
- package/dist/src/messaging/tools.js +102 -0
- package/dist/src/messaging/wake.d.ts +23 -0
- package/dist/src/messaging/wake.js +39 -0
- package/dist/src/operator/agent-projection.d.ts +102 -0
- package/dist/src/operator/agent-projection.js +249 -0
- package/dist/src/operator/commands.d.ts +50 -0
- package/dist/src/operator/commands.js +199 -0
- package/dist/src/operator/diagnostics.d.ts +21 -0
- package/dist/src/operator/diagnostics.js +22 -0
- package/dist/src/operator/protocol.d.ts +511 -0
- package/dist/src/operator/protocol.js +49 -0
- package/dist/src/operator/repository-system.d.ts +14 -0
- package/dist/src/operator/repository-system.js +150 -0
- package/dist/src/operator/repository.d.ts +79 -0
- package/dist/src/operator/repository.js +72 -0
- package/dist/src/operator/traffic-projection.d.ts +19 -0
- package/dist/src/operator/traffic-projection.js +229 -0
- package/dist/src/operator/transport.d.ts +87 -0
- package/dist/src/operator/transport.js +549 -0
- package/dist/src/operator/update-store.d.ts +53 -0
- package/dist/src/operator/update-store.js +162 -0
- package/dist/src/operator/updates.d.ts +94 -0
- package/dist/src/operator/updates.js +280 -0
- package/dist/src/operator/validation.d.ts +114 -0
- package/dist/src/operator/validation.js +916 -0
- package/dist/src/shared/guards.d.ts +59 -0
- package/dist/src/shared/guards.js +119 -0
- package/dist/src/shared/http.d.ts +30 -0
- package/dist/src/shared/http.js +50 -0
- package/dist/src/shared/row.d.ts +2 -0
- package/dist/src/shared/row.js +1 -0
- package/dist/src/shared/shell.d.ts +6 -0
- package/dist/src/shared/shell.js +8 -0
- package/dist/src/tui/actions.d.ts +166 -0
- package/dist/src/tui/actions.js +253 -0
- package/dist/src/tui/agent-tree.d.ts +125 -0
- package/dist/src/tui/agent-tree.js +663 -0
- package/dist/src/tui/app.d.ts +38 -0
- package/dist/src/tui/app.js +378 -0
- package/dist/src/tui/chrome.d.ts +86 -0
- package/dist/src/tui/chrome.js +85 -0
- package/dist/src/tui/client.d.ts +41 -0
- package/dist/src/tui/client.js +279 -0
- package/dist/src/tui/composition.d.ts +41 -0
- package/dist/src/tui/composition.js +146 -0
- package/dist/src/tui/message-views.d.ts +94 -0
- package/dist/src/tui/message-views.js +515 -0
- package/dist/src/tui/model.d.ts +414 -0
- package/dist/src/tui/model.js +758 -0
- package/dist/src/tui/presentation.d.ts +350 -0
- package/dist/src/tui/presentation.js +541 -0
- package/dist/src/tui/production.d.ts +79 -0
- package/dist/src/tui/production.js +253 -0
- package/dist/src/tui/run-terminal.d.ts +32 -0
- package/dist/src/tui/run-terminal.js +54 -0
- package/dist/src/tui/runtime.d.ts +113 -0
- package/dist/src/tui/runtime.js +189 -0
- package/dist/src/tui/theme.d.ts +49 -0
- package/dist/src/tui/theme.js +55 -0
- package/dist/src/tui/transport.d.ts +31 -0
- package/dist/src/tui/transport.js +255 -0
- package/dist/src/version.d.ts +11 -0
- package/dist/src/version.js +52 -0
- package/package.json +142 -0
- package/skills/pager/SKILL.md +112 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { FETCH_LIMIT_DEFAULT } from "../broker/protocol.js";
|
|
2
|
+
/**
|
|
3
|
+
* Turns wakes for host-push recipients into deliveries written by their implementation.
|
|
4
|
+
*
|
|
5
|
+
* The service claims on the recipient's behalf immediately before writing, so a message is never
|
|
6
|
+
* held by a session that has not been handed it. A wake for a recipient without a registered
|
|
7
|
+
* address is left to the redelivery schedule, which offers it again once an address arrives.
|
|
8
|
+
*/
|
|
9
|
+
export class PushDeliveryService {
|
|
10
|
+
/**
|
|
11
|
+
* Hand one diagnostic to the report port, absorbing whatever the port does with it.
|
|
12
|
+
*
|
|
13
|
+
* Every call site is annotating an outcome it is in the middle of handling — a delivery that
|
|
14
|
+
* failed, an address that would not parse — so a sink that throws would replace the outcome with
|
|
15
|
+
* its own.
|
|
16
|
+
* @param message - Operator-facing diagnostic to report.
|
|
17
|
+
*/
|
|
18
|
+
#report(message) {
|
|
19
|
+
try {
|
|
20
|
+
this.#dependencies.report(message);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// A diagnostic that cannot be delivered is not worth losing the outcome it described.
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
#dependencies;
|
|
27
|
+
#registrations = new Map();
|
|
28
|
+
#chains = new Map();
|
|
29
|
+
/**
|
|
30
|
+
* Construct the service over injected broker, hub, scheduler, and capability resolution.
|
|
31
|
+
* @param dependencies - Collaborators the service reaches outside its own state.
|
|
32
|
+
*/
|
|
33
|
+
constructor(dependencies) {
|
|
34
|
+
this.#dependencies = dependencies;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Hold the address a session registered, and deliver anything already waiting for it.
|
|
38
|
+
*
|
|
39
|
+
* An unchanged address is a no-op.
|
|
40
|
+
* @param agentId - Recipient whose session supplied the address.
|
|
41
|
+
* @param raw - Untrusted address text exactly as the session supplied it.
|
|
42
|
+
* @returns - What the registration changed.
|
|
43
|
+
*/
|
|
44
|
+
registerAddress(agentId, raw) {
|
|
45
|
+
const existing = this.#registrations.get(agentId);
|
|
46
|
+
if (existing?.raw === raw)
|
|
47
|
+
return "unchanged";
|
|
48
|
+
const implementation = this.#dependencies.broker.implementationForAgent(agentId);
|
|
49
|
+
const capability = implementation ? this.#dependencies.capabilityFor(implementation.id) : undefined;
|
|
50
|
+
if (!capability)
|
|
51
|
+
return "not_push";
|
|
52
|
+
const parsed = capability.parseAddress(raw);
|
|
53
|
+
if (!parsed.known) {
|
|
54
|
+
this.#report(`pager delivery: ${agentId} registered a malformed delivery address`);
|
|
55
|
+
return "rejected";
|
|
56
|
+
}
|
|
57
|
+
existing?.unsubscribe();
|
|
58
|
+
// A host process reuses one physical inbox across the identities it registers in turn (Pi's
|
|
59
|
+
// direct-mode re-registration is the case this guards): if the identity that previously held it
|
|
60
|
+
// never got the chance to detach — the call itself failed, not only a successful one — its
|
|
61
|
+
// registration would otherwise sit here pointing at the same address this new one is about to
|
|
62
|
+
// claim, and a wake meant for it would still land in the inbox the new identity now reads.
|
|
63
|
+
for (const [holder, registration] of this.#registrations) {
|
|
64
|
+
if (holder !== agentId && registration.raw === raw)
|
|
65
|
+
this.forget(holder);
|
|
66
|
+
}
|
|
67
|
+
const unsubscribe = this.#dependencies.hub.subscribe(agentId, (event) => {
|
|
68
|
+
this.#enqueue(agentId, event);
|
|
69
|
+
});
|
|
70
|
+
this.#registrations.set(agentId, { raw, address: parsed.value, capability, unsubscribe });
|
|
71
|
+
this.#dependencies.scheduler.declareAdapter(agentId, { coalescing: true, maxProtocolVersion: 1 });
|
|
72
|
+
if (this.#dependencies.broker.hasQueuedMessages(agentId))
|
|
73
|
+
this.#dependencies.scheduler.notify(agentId, "resync");
|
|
74
|
+
return "registered";
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Drop a recipient's address so no further wake is written to it.
|
|
78
|
+
* @param agentId - Recipient whose address is no longer usable.
|
|
79
|
+
*/
|
|
80
|
+
forget(agentId) {
|
|
81
|
+
this.#registrations.get(agentId)?.unsubscribe();
|
|
82
|
+
this.#registrations.delete(agentId);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Report whether a recipient currently has an address to deliver to.
|
|
86
|
+
* @param agentId - Recipient to check.
|
|
87
|
+
* @returns - Whether a delivery address is held for the recipient.
|
|
88
|
+
*/
|
|
89
|
+
hasAddress(agentId) {
|
|
90
|
+
return this.#registrations.has(agentId);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolve once every delivery in flight, and every one released by their completion, has finished.
|
|
94
|
+
* @returns - Settles when no delivery chain remains.
|
|
95
|
+
*/
|
|
96
|
+
async settled() {
|
|
97
|
+
while (this.#chains.size > 0)
|
|
98
|
+
await Promise.all([...this.#chains.values()]);
|
|
99
|
+
}
|
|
100
|
+
/** Drop every address and stop listening for wakes. */
|
|
101
|
+
close() {
|
|
102
|
+
for (const agentId of [...this.#registrations.keys()])
|
|
103
|
+
this.forget(agentId);
|
|
104
|
+
}
|
|
105
|
+
#enqueue(agentId, event) {
|
|
106
|
+
const previous = this.#chains.get(agentId) ?? Promise.resolve();
|
|
107
|
+
const next = previous.then(() => this.#deliver(agentId, event));
|
|
108
|
+
this.#chains.set(agentId, next);
|
|
109
|
+
void next.finally(() => {
|
|
110
|
+
if (this.#chains.get(agentId) === next)
|
|
111
|
+
this.#chains.delete(agentId);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async #deliver(agentId, event) {
|
|
115
|
+
const registration = this.#registrations.get(agentId);
|
|
116
|
+
if (!registration)
|
|
117
|
+
return;
|
|
118
|
+
// A shed wake is the scheduler recording that budget refused this one; the schedule offers the
|
|
119
|
+
// inbox again later, so nothing is written now.
|
|
120
|
+
if (event.reason === "shed")
|
|
121
|
+
return;
|
|
122
|
+
this.#dependencies.scheduler.setBusy(agentId, true);
|
|
123
|
+
try {
|
|
124
|
+
if (event.reason === "operator_ping") {
|
|
125
|
+
await this.#write(agentId, registration, event, []);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const claims = this.#dependencies.broker
|
|
129
|
+
.fetch({ agentId, limit: FETCH_LIMIT_DEFAULT, claimedBy: "delivery" })
|
|
130
|
+
.map(({ id, fromAddress, broadcastAddress, subject, type, body, claimToken }) => ({
|
|
131
|
+
id,
|
|
132
|
+
fromAddress,
|
|
133
|
+
...(broadcastAddress === undefined ? {} : { broadcastAddress }),
|
|
134
|
+
subject,
|
|
135
|
+
type,
|
|
136
|
+
body,
|
|
137
|
+
claimToken,
|
|
138
|
+
}));
|
|
139
|
+
if (claims.length === 0)
|
|
140
|
+
return;
|
|
141
|
+
const wake = {
|
|
142
|
+
type: "claims_delivered",
|
|
143
|
+
agentId,
|
|
144
|
+
pushedAt: this.#dependencies.now(),
|
|
145
|
+
claims,
|
|
146
|
+
};
|
|
147
|
+
await this.#write(agentId, registration, wake, claims);
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
this.#dependencies.scheduler.setBusy(agentId, false);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async #write(agentId, registration, wake, claims) {
|
|
154
|
+
let reach;
|
|
155
|
+
try {
|
|
156
|
+
reach = await registration.capability.deliver(registration.address, wake);
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
reach = { reach: "undelivered", cause: error instanceof Error ? error.message : String(error) };
|
|
160
|
+
}
|
|
161
|
+
switch (reach.reach) {
|
|
162
|
+
case "claimed":
|
|
163
|
+
for (const claim of claims)
|
|
164
|
+
this.#dependencies.broker.recordNotification(claim.id, "delivered");
|
|
165
|
+
return;
|
|
166
|
+
case "notified":
|
|
167
|
+
// A body-free wake carries no claims, so a transport reporting one for a claim-carrying wake
|
|
168
|
+
// handed nothing over; the recipient must be offered the messages again.
|
|
169
|
+
if (claims.length > 0)
|
|
170
|
+
this.#release(agentId, claims, "transport wrote a body-free wake for claims");
|
|
171
|
+
return;
|
|
172
|
+
case "undelivered":
|
|
173
|
+
this.#release(agentId, claims, reach.cause);
|
|
174
|
+
// The session re-registers on its next request if it is still alive.
|
|
175
|
+
if (this.#registrations.get(agentId) === registration)
|
|
176
|
+
this.forget(agentId);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
#release(agentId, claims, cause) {
|
|
181
|
+
const broker = this.#dependencies.broker;
|
|
182
|
+
broker.releaseClaims(agentId, claims.map(({ claimToken }) => claimToken));
|
|
183
|
+
for (const claim of claims)
|
|
184
|
+
broker.recordNotification(claim.id, "failed");
|
|
185
|
+
this.#report(`pager delivery: ${agentId} not delivered: ${cause}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Describes one MCP tool at the shared server discovery boundary. */
|
|
2
|
+
export type McpToolDefinition = {
|
|
3
|
+
/** Supplies the stable tool name. */
|
|
4
|
+
name: string;
|
|
5
|
+
/** Explains the operation to an MCP client. */
|
|
6
|
+
description: string;
|
|
7
|
+
/** Validates caller-controlled tool arguments. */
|
|
8
|
+
inputSchema: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
/** Supplies the single discovery declaration consumed by every Pager MCP server. */
|
|
11
|
+
export declare const MCP_TOOLS: readonly McpToolDefinition[];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { FETCH_LIMIT_DEFAULT, MESSAGE_SUBJECT_LIMIT_CHARACTERS, MESSAGE_TYPE_LIMIT_CHARACTERS, } from "../broker/protocol.js";
|
|
2
|
+
const noArguments = { type: "object", properties: {}, additionalProperties: false };
|
|
3
|
+
const schema = (properties, required = []) => ({
|
|
4
|
+
type: "object",
|
|
5
|
+
properties,
|
|
6
|
+
required,
|
|
7
|
+
additionalProperties: false,
|
|
8
|
+
});
|
|
9
|
+
const messageContent = {
|
|
10
|
+
subject: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: `One line, at most ${String(MESSAGE_SUBJECT_LIMIT_CHARACTERS)} characters. Keep it terse, like a commit summary; detail belongs in the body.`,
|
|
13
|
+
},
|
|
14
|
+
type: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: `Message category, at most ${String(MESSAGE_TYPE_LIMIT_CHARACTERS)} characters, such as "brief", "idle", or "question". Free text, first-class on the wire.`,
|
|
17
|
+
},
|
|
18
|
+
body: { description: "Message payload retained for the recipient to claim." },
|
|
19
|
+
};
|
|
20
|
+
/** Supplies the single discovery declaration consumed by every Pager MCP server. */
|
|
21
|
+
export const MCP_TOOLS = [
|
|
22
|
+
{
|
|
23
|
+
name: "whoami",
|
|
24
|
+
description: "Return the authenticated agent id, current alias, and prior aliases.",
|
|
25
|
+
inputSchema: noArguments,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "update_agent_alias",
|
|
29
|
+
description: "Replace this agent's workspace-unique alias and durable session name. The alias is lowercased and inner whitespace becomes hyphens, so use the alias returned in the result rather than the one requested.",
|
|
30
|
+
inputSchema: schema({
|
|
31
|
+
alias: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "New workspace-unique alias, with an optional leading @. Lowercased, and inner whitespace becomes hyphens.",
|
|
34
|
+
},
|
|
35
|
+
}, ["alias"]),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "list_team_members",
|
|
39
|
+
description: "List agents in this agent's own workspace.",
|
|
40
|
+
inputSchema: noArguments,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "list_agents",
|
|
44
|
+
description: "List every agent on the broker with the address that reaches it from any workspace.",
|
|
45
|
+
inputSchema: noArguments,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "send_agent_message",
|
|
49
|
+
description: "Durably send a message to an agent.",
|
|
50
|
+
inputSchema: schema({
|
|
51
|
+
to: {
|
|
52
|
+
type: "string",
|
|
53
|
+
description: "Recipient agent id, an alias in this workspace, or alias@workspace for an agent elsewhere.",
|
|
54
|
+
},
|
|
55
|
+
...messageContent,
|
|
56
|
+
idempotencyKey: { type: "string", description: "Repeat of an earlier send; returns the original message." },
|
|
57
|
+
}, ["to", "subject", "body", "type"]),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "broadcast_agent_message",
|
|
61
|
+
description: "Durably send its own copy of one message to each other agent in a workspace, skipping archived agents and ended ephemeral sessions. The result lists each copy under `sent`, and under `refused` each recipient a rate or inbox limit turned away.",
|
|
62
|
+
inputSchema: schema({
|
|
63
|
+
workspace: {
|
|
64
|
+
type: "string",
|
|
65
|
+
description: "Workspace name, as it appears after the @ of an address; defaults to this agent's own.",
|
|
66
|
+
},
|
|
67
|
+
...messageContent,
|
|
68
|
+
idempotencyKey: {
|
|
69
|
+
type: "string",
|
|
70
|
+
description: "Repeat of an earlier broadcast; returns its original message to each recipient it reached and sends to the rest.",
|
|
71
|
+
},
|
|
72
|
+
}, ["subject", "body", "type"]),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "fetch_agent_messages",
|
|
76
|
+
description: "Claim available inbox messages.",
|
|
77
|
+
inputSchema: schema({
|
|
78
|
+
limit: {
|
|
79
|
+
type: "number",
|
|
80
|
+
description: `Maximum messages to claim in this call; defaults to ${String(FETCH_LIMIT_DEFAULT)}.`,
|
|
81
|
+
},
|
|
82
|
+
}),
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "ack_agent_messages",
|
|
86
|
+
description: "Acknowledge claimed messages. The result lists `acked` tokens and, for each token that acknowledged nothing, `rejected` with a reason: unknown_token, foreign_token, claim_expired (the message is back in the queue: fetch it again), already_acknowledged, or message_settled.",
|
|
87
|
+
inputSchema: schema({
|
|
88
|
+
claimTokens: {
|
|
89
|
+
type: "array",
|
|
90
|
+
description: "One claim token per message you hold; unacknowledged claims are redelivered.",
|
|
91
|
+
items: { type: "string", description: "Token delivered with the claim, authorizing this ack." },
|
|
92
|
+
},
|
|
93
|
+
}, ["claimTokens"]),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "get_message_status",
|
|
97
|
+
description: "Return a sender-visible message status.",
|
|
98
|
+
inputSchema: schema({ messageId: { type: "string", description: "Id of a message this agent sent." } }, [
|
|
99
|
+
"messageId",
|
|
100
|
+
]),
|
|
101
|
+
},
|
|
102
|
+
];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { InboxChanged } from "./notifications.js";
|
|
2
|
+
/** Names the wake vocabulary shared by every implementation adapter. */
|
|
3
|
+
export declare const PAGER_WAKE_TYPE = "pager-inbox";
|
|
4
|
+
/**
|
|
5
|
+
* Build the flat body-free metadata every implementation exposes for one wake.
|
|
6
|
+
*
|
|
7
|
+
* The broker epoch is supplied rather than read from the event because implementations differ in
|
|
8
|
+
* whether they expose the raw epoch or its public digest to the model.
|
|
9
|
+
* @param event - Body-free inbox event published by the broker.
|
|
10
|
+
* @param brokerEpoch - Epoch value this implementation exposes to the model.
|
|
11
|
+
* @returns - Flat string metadata carrying no peer message body.
|
|
12
|
+
*/
|
|
13
|
+
export declare function wakeMetadata(event: InboxChanged, brokerEpoch: string): Record<string, string>;
|
|
14
|
+
/**
|
|
15
|
+
* Render one wake as a single line, for adapters whose transport is line-delimited.
|
|
16
|
+
*
|
|
17
|
+
* Standing guidance is omitted because such an adapter establishes it once per session; the line
|
|
18
|
+
* carries only the shared metadata vocabulary.
|
|
19
|
+
* @param event - Body-free inbox event published by the broker.
|
|
20
|
+
* @param brokerEpoch - Epoch value this implementation exposes to the model.
|
|
21
|
+
* @returns - Single line with no embedded newline, never carrying a peer message body.
|
|
22
|
+
*/
|
|
23
|
+
export declare function wakeLine(event: InboxChanged, brokerEpoch: string): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { PROTOCOL_VERSION } from "../broker/protocol.js";
|
|
2
|
+
/** Names the wake vocabulary shared by every implementation adapter. */
|
|
3
|
+
export const PAGER_WAKE_TYPE = "pager-inbox";
|
|
4
|
+
/**
|
|
5
|
+
* Build the flat body-free metadata every implementation exposes for one wake.
|
|
6
|
+
*
|
|
7
|
+
* The broker epoch is supplied rather than read from the event because implementations differ in
|
|
8
|
+
* whether they expose the raw epoch or its public digest to the model.
|
|
9
|
+
* @param event - Body-free inbox event published by the broker.
|
|
10
|
+
* @param brokerEpoch - Epoch value this implementation exposes to the model.
|
|
11
|
+
* @returns - Flat string metadata carrying no peer message body.
|
|
12
|
+
*/
|
|
13
|
+
export function wakeMetadata(event, brokerEpoch) {
|
|
14
|
+
return {
|
|
15
|
+
protocol_version: String(PROTOCOL_VERSION),
|
|
16
|
+
agent_id: event.agentId,
|
|
17
|
+
broker_epoch: brokerEpoch,
|
|
18
|
+
inbox_sequence: String(event.sequence),
|
|
19
|
+
reason: event.reason,
|
|
20
|
+
...(event.reason === "operator_ping"
|
|
21
|
+
? {
|
|
22
|
+
control_id: event.controlId,
|
|
23
|
+
coalesced_control_ids: JSON.stringify(event.coalescedControlIds),
|
|
24
|
+
}
|
|
25
|
+
: {}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Render one wake as a single line, for adapters whose transport is line-delimited.
|
|
30
|
+
*
|
|
31
|
+
* Standing guidance is omitted because such an adapter establishes it once per session; the line
|
|
32
|
+
* carries only the shared metadata vocabulary.
|
|
33
|
+
* @param event - Body-free inbox event published by the broker.
|
|
34
|
+
* @param brokerEpoch - Epoch value this implementation exposes to the model.
|
|
35
|
+
* @returns - Single line with no embedded newline, never carrying a peer message body.
|
|
36
|
+
*/
|
|
37
|
+
export function wakeLine(event, brokerEpoch) {
|
|
38
|
+
return `${PAGER_WAKE_TYPE} ${JSON.stringify(wakeMetadata(event, brokerEpoch))}`;
|
|
39
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { BrokerSnapshot } from "../broker/snapshot.js";
|
|
2
|
+
import { type OperatorDiagnosticSink } from "./diagnostics.js";
|
|
3
|
+
import type { OperatorAcceptedDelivery, OperatorAgent, OperatorJsonObject, OperatorRepository, OperatorTraffic } from "./protocol.js";
|
|
4
|
+
import { type RepositoryProjectionDependencies } from "./repository.js";
|
|
5
|
+
/** Supplies installed-provider presentation data without interpreting open provider identities. */
|
|
6
|
+
export interface OperatorImplementationCatalogPort {
|
|
7
|
+
/**
|
|
8
|
+
* Resolve public presentation metadata for one persisted provider identity.
|
|
9
|
+
* @param implementationId - Open provider identity stored with the agent.
|
|
10
|
+
* @returns Public provider data, or null when the provider is unavailable.
|
|
11
|
+
*/
|
|
12
|
+
find(implementationId: string): {
|
|
13
|
+
displayName: string;
|
|
14
|
+
metadata: OperatorJsonObject;
|
|
15
|
+
} | null;
|
|
16
|
+
}
|
|
17
|
+
/** Supplies explicit collaborators to the repository and agent projection. */
|
|
18
|
+
export type OperatorAgentProjectionDependencies = RepositoryProjectionDependencies & {
|
|
19
|
+
/** Resolves provider-owned public presentation data. */
|
|
20
|
+
implementations: OperatorImplementationCatalogPort;
|
|
21
|
+
/** Receives rows the projection could not admit; defaults to discarding them. */
|
|
22
|
+
diagnostics?: OperatorDiagnosticSink;
|
|
23
|
+
/** Supplies the observation clock; defaults to the wall clock. */
|
|
24
|
+
now?: () => number;
|
|
25
|
+
};
|
|
26
|
+
/** Carries bounded repository and agent collections for an operator snapshot producer. */
|
|
27
|
+
export type OperatorAgentProjection = {
|
|
28
|
+
/** Lists presentation-only repository groups. */
|
|
29
|
+
repositories: OperatorRepository[];
|
|
30
|
+
/** Lists durable agents in stable registration order. */
|
|
31
|
+
agents: OperatorAgent[];
|
|
32
|
+
};
|
|
33
|
+
/** Reports an invalid or unbounded projection source. */
|
|
34
|
+
export declare class OperatorAgentProjectionError extends Error {
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Project broker snapshot repositories and agents without changing broker identities.
|
|
38
|
+
* @param snapshot - Authoritative broker state containing durable identity and attachment facts.
|
|
39
|
+
* @param dependencies - Explicit repository and implementation collaborators.
|
|
40
|
+
* @returns Bounded repositories and agents suitable for an operator snapshot.
|
|
41
|
+
*/
|
|
42
|
+
export declare function projectOperatorAgents(snapshot: Pick<BrokerSnapshot, "workspaces" | "agents" | "connections">, dependencies: OperatorAgentProjectionDependencies): OperatorAgentProjection;
|
|
43
|
+
/** Describes the presentation-neutral health classification for one agent. */
|
|
44
|
+
export type OperatorAgentHealth = {
|
|
45
|
+
kind: "archived";
|
|
46
|
+
} | {
|
|
47
|
+
kind: "connection_disconnected";
|
|
48
|
+
} | {
|
|
49
|
+
kind: "delivery_queued";
|
|
50
|
+
} | {
|
|
51
|
+
kind: "delivery_claimed";
|
|
52
|
+
} | {
|
|
53
|
+
kind: "delivery_failed";
|
|
54
|
+
} | {
|
|
55
|
+
kind: "delivery_acknowledged";
|
|
56
|
+
} | {
|
|
57
|
+
kind: "neutral";
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Report why an agent belongs to the operator archive rather than the active list.
|
|
61
|
+
*
|
|
62
|
+
* Archive membership is not lifecycle alone: an agent whose workspace directory has been removed
|
|
63
|
+
* can no longer be worked in, so it archives while the broker still holds it active and routable.
|
|
64
|
+
* An operator-archived agent whose workspace is still there did not lose its workspace, so a surface
|
|
65
|
+
* naming the cause must be told which one it is. The causes can hold together, and the first that holds
|
|
66
|
+
* is reported, in this order: the operator's archive, then lifecycle, then a removed workspace. The
|
|
67
|
+
* operator's own action comes first because it is why they see the agent archived.
|
|
68
|
+
* @param agent - Projected agent whose archive cause is requested.
|
|
69
|
+
* @returns The first cause that holds, or null when the agent is still active.
|
|
70
|
+
*/
|
|
71
|
+
export declare function operatorArchiveReason(agent: OperatorAgent): "operator" | "lifecycle" | "workspace_removed" | null;
|
|
72
|
+
/**
|
|
73
|
+
* Decide whether an agent belongs to the operator archive rather than the active list.
|
|
74
|
+
* @param agent - Projected agent whose archive membership is requested.
|
|
75
|
+
* @returns True when the agent belongs in a repository's archived section.
|
|
76
|
+
*/
|
|
77
|
+
export declare function isOperatorAgentArchived(agent: OperatorAgent): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Select the repositories whose every conversation is archived.
|
|
80
|
+
*
|
|
81
|
+
* A repository holding no agents at all is not archived: there is nothing in it that ended.
|
|
82
|
+
* @param agents - Projected agents across every repository.
|
|
83
|
+
* @returns - Identities of the repositories that are archived in their entirety.
|
|
84
|
+
*/
|
|
85
|
+
export declare function archivedOperatorRepositoryIds(agents: readonly OperatorAgent[]): ReadonlySet<string>;
|
|
86
|
+
/**
|
|
87
|
+
* Select one exhaustive health classification from authoritative agent and traffic facts.
|
|
88
|
+
* @param agent - Projected agent whose health is requested.
|
|
89
|
+
* @param traffic - Complete operator traffic collection for the snapshot.
|
|
90
|
+
* @returns The first classification that applies: archived, then disconnected (an expired connection
|
|
91
|
+
* counts), then the state of the agent's newest accepted delivery, where an expired delivery is neutral.
|
|
92
|
+
*/
|
|
93
|
+
export declare function selectOperatorAgentHealth(agent: OperatorAgent, traffic: readonly OperatorTraffic[]): OperatorAgentHealth;
|
|
94
|
+
/**
|
|
95
|
+
* Report whether the broker would still accept an expiry for one delivery.
|
|
96
|
+
*
|
|
97
|
+
* Expiry moves a message to a terminal state, so a delivery already in one is refused with a conflict.
|
|
98
|
+
* Offering the action anyway would put the operator's only feedback in the diagnostics log.
|
|
99
|
+
* @param message - Delivery the operator is being offered expiry for.
|
|
100
|
+
* @returns Whether expiry can still change this delivery's state.
|
|
101
|
+
*/
|
|
102
|
+
export declare function isTuiExpirable(message: OperatorAcceptedDelivery): boolean;
|