@openscout/protocol 0.2.37 → 0.2.39
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/agent-identity.d.ts +12 -0
- package/dist/agent-identity.js +25 -7
- package/dist/common.d.ts +2 -1
- package/dist/events.d.ts +9 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/scout-dispatch.d.ts +31 -0
- package/dist/scout-dispatch.js +1 -0
- package/package.json +1 -1
- package/src/agent-identity.test.ts +75 -0
- package/src/agent-identity.ts +38 -7
- package/src/common.ts +18 -2
- package/src/events.ts +13 -1
- package/src/index.ts +1 -0
- package/src/scout-dispatch.ts +36 -0
package/dist/agent-identity.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface AgentIdentityAlias {
|
|
|
31
31
|
}
|
|
32
32
|
export declare function normalizeAgentIdentitySegment(value: string): string;
|
|
33
33
|
export declare const normalizeAgentSelectorSegment: typeof normalizeAgentIdentitySegment;
|
|
34
|
+
export declare const SCOUT_DISPATCHER_AGENT_ID = "scout";
|
|
35
|
+
export declare function isReservedAgentDefinitionId(value: string | null | undefined): boolean;
|
|
34
36
|
export declare function parseAgentIdentity(value: string): AgentIdentity | null;
|
|
35
37
|
export declare function formatAgentIdentity(input: AgentIdentityInput, options?: {
|
|
36
38
|
includeSigil?: boolean;
|
|
@@ -45,6 +47,16 @@ export declare function extractAgentMentions(text: string): {
|
|
|
45
47
|
};
|
|
46
48
|
export declare function agentIdentityMatches(identity: AgentIdentity, candidate: AgentIdentityCandidate): boolean;
|
|
47
49
|
export declare function resolveAgentIdentity<T extends AgentIdentityCandidate>(identity: AgentIdentity, candidates: T[]): T | null;
|
|
50
|
+
export type AgentIdentityDiagnosis<T extends AgentIdentityCandidate> = {
|
|
51
|
+
kind: "resolved";
|
|
52
|
+
match: T;
|
|
53
|
+
} | {
|
|
54
|
+
kind: "ambiguous";
|
|
55
|
+
candidates: T[];
|
|
56
|
+
} | {
|
|
57
|
+
kind: "unknown";
|
|
58
|
+
};
|
|
59
|
+
export declare function diagnoseAgentIdentity<T extends AgentIdentityCandidate>(identity: AgentIdentity, candidates: T[]): AgentIdentityDiagnosis<T>;
|
|
48
60
|
export declare function constructAgentAlias(input: {
|
|
49
61
|
alias: string;
|
|
50
62
|
target: AgentIdentityInput;
|
package/dist/agent-identity.js
CHANGED
|
@@ -24,6 +24,15 @@ export function normalizeAgentIdentitySegment(value) {
|
|
|
24
24
|
.replace(/^-+|-+$/g, "");
|
|
25
25
|
}
|
|
26
26
|
export const normalizeAgentSelectorSegment = normalizeAgentIdentitySegment;
|
|
27
|
+
export const SCOUT_DISPATCHER_AGENT_ID = "scout";
|
|
28
|
+
const RESERVED_AGENT_DEFINITION_IDS = new Set([
|
|
29
|
+
SCOUT_DISPATCHER_AGENT_ID,
|
|
30
|
+
]);
|
|
31
|
+
export function isReservedAgentDefinitionId(value) {
|
|
32
|
+
if (!value)
|
|
33
|
+
return false;
|
|
34
|
+
return RESERVED_AGENT_DEFINITION_IDS.has(normalizeAgentIdentitySegment(value));
|
|
35
|
+
}
|
|
27
36
|
function normalizeDimensionKey(value) {
|
|
28
37
|
return DIMENSION_ALIASES[normalizeAgentIdentitySegment(value)] ?? null;
|
|
29
38
|
}
|
|
@@ -277,27 +286,36 @@ export function agentIdentityMatches(identity, candidate) {
|
|
|
277
286
|
return true;
|
|
278
287
|
}
|
|
279
288
|
export function resolveAgentIdentity(identity, candidates) {
|
|
289
|
+
const diagnosis = diagnoseAgentIdentity(identity, candidates);
|
|
290
|
+
return diagnosis.kind === "resolved" ? diagnosis.match : null;
|
|
291
|
+
}
|
|
292
|
+
export function diagnoseAgentIdentity(identity, candidates) {
|
|
280
293
|
const aliasKeys = identityAliasKeys(identity);
|
|
281
294
|
const exactAliasMatches = candidates.filter((candidate) => {
|
|
282
295
|
const candidateAliasKeys = explicitCandidateAliases(candidate).map(canonicalizeAliasValue);
|
|
283
296
|
return aliasKeys.some((key) => candidateAliasKeys.includes(key));
|
|
284
297
|
});
|
|
285
298
|
if (exactAliasMatches.length === 1) {
|
|
286
|
-
return exactAliasMatches[0];
|
|
299
|
+
return { kind: "resolved", match: exactAliasMatches[0] };
|
|
287
300
|
}
|
|
288
301
|
if (exactAliasMatches.length > 1) {
|
|
289
|
-
return
|
|
302
|
+
return { kind: "ambiguous", candidates: exactAliasMatches };
|
|
290
303
|
}
|
|
291
304
|
const matches = candidates.filter((candidate) => agentIdentityMatches(identity, candidate));
|
|
292
305
|
if (matches.length === 1) {
|
|
293
|
-
return matches[0];
|
|
306
|
+
return { kind: "resolved", match: matches[0] };
|
|
307
|
+
}
|
|
308
|
+
if (matches.length === 0) {
|
|
309
|
+
return { kind: "unknown" };
|
|
294
310
|
}
|
|
295
311
|
if (!identity.nodeQualifier && !identity.workspaceQualifier && !identity.profile && !identity.harness) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
312
|
+
const exactIdMatch = matches.find((candidate) => normalizeAgentIdentitySegment(candidate.agentId) === identity.definitionId);
|
|
313
|
+
if (exactIdMatch) {
|
|
314
|
+
return { kind: "resolved", match: exactIdMatch };
|
|
315
|
+
}
|
|
316
|
+
return { kind: "ambiguous", candidates: matches };
|
|
299
317
|
}
|
|
300
|
-
return
|
|
318
|
+
return { kind: "ambiguous", candidates: matches };
|
|
301
319
|
}
|
|
302
320
|
export function constructAgentAlias(input) {
|
|
303
321
|
const alias = normalizeAgentIdentitySegment(trimIdentityPrefix(input.alias));
|
package/dist/common.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export type ScoutId = string;
|
|
|
2
2
|
export type ActorKind = "person" | "helper" | "agent" | "system" | "bridge" | "device";
|
|
3
3
|
export type AgentState = "offline" | "idle" | "active" | "waiting";
|
|
4
4
|
export type VisibilityScope = "private" | "workspace" | "public" | "system";
|
|
5
|
-
export type DeliveryStatus = "
|
|
5
|
+
export type DeliveryStatus = "accepted" | "peer_acked" | "running" | "completed" | "deferred" | "failed" | "cancelled" | "pending" | "leased" | "sent" | "acknowledged";
|
|
6
|
+
export type DeliveryFailureReason = "peer_unreachable" | "peer_rejected" | "agent_offline" | "timeout" | "harness_error" | "dispatch" | "cancelled";
|
|
6
7
|
export type DeliveryPolicy = "best_effort" | "must_ack" | "durable" | "ephemeral";
|
|
7
8
|
export type DeliveryTargetKind = "participant" | "agent" | "bridge" | "device" | "voice_session" | "webhook";
|
|
8
9
|
export type DeliveryTransport = "local_socket" | "websocket" | "peer_broker" | "http" | "webhook" | "telegram" | "discord" | "sms" | "email" | "tts" | "native_voice" | "claude_stream_json" | "codex_app_server" | "codex_exec" | "claude_resume" | "tmux";
|
package/dist/events.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { FlightRecord, InvocationRequest } from "./invocations.js";
|
|
|
6
6
|
import type { MessageRecord } from "./messages.js";
|
|
7
7
|
import type { AgentDefinition, AgentEndpoint, ActorIdentity } from "./actors.js";
|
|
8
8
|
import type { CollaborationEvent, CollaborationRecord } from "./collaboration.js";
|
|
9
|
+
import type { ScoutDispatchRecord } from "./scout-dispatch.js";
|
|
9
10
|
export interface ControlEventBase<K extends string, P> {
|
|
10
11
|
id: ScoutId;
|
|
11
12
|
kind: K;
|
|
@@ -47,10 +48,17 @@ export type DeliveryPlannedEvent = ControlEventBase<"delivery.planned", {
|
|
|
47
48
|
export type DeliveryAttemptedEvent = ControlEventBase<"delivery.attempted", {
|
|
48
49
|
attempt: DeliveryAttempt;
|
|
49
50
|
}>;
|
|
51
|
+
export type DeliveryStateChangedEvent = ControlEventBase<"delivery.state.changed", {
|
|
52
|
+
delivery: DeliveryIntent;
|
|
53
|
+
previousStatus?: DeliveryIntent["status"];
|
|
54
|
+
}>;
|
|
50
55
|
export type CollaborationUpsertedEvent = ControlEventBase<"collaboration.upserted", {
|
|
51
56
|
record: CollaborationRecord;
|
|
52
57
|
}>;
|
|
53
58
|
export type CollaborationEventAppendedEvent = ControlEventBase<"collaboration.event.appended", {
|
|
54
59
|
event: CollaborationEvent;
|
|
55
60
|
}>;
|
|
56
|
-
export type
|
|
61
|
+
export type ScoutDispatchedEvent = ControlEventBase<"scout.dispatched", {
|
|
62
|
+
dispatch: ScoutDispatchRecord;
|
|
63
|
+
}>;
|
|
64
|
+
export type ControlEvent = NodeUpsertedEvent | ActorRegisteredEvent | AgentRegisteredEvent | AgentEndpointUpsertedEvent | ConversationUpsertedEvent | BindingUpsertedEvent | MessagePostedEvent | InvocationRequestedEvent | FlightUpdatedEvent | DeliveryPlannedEvent | DeliveryAttemptedEvent | DeliveryStateChangedEvent | CollaborationUpsertedEvent | CollaborationEventAppendedEvent | ScoutDispatchedEvent;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./conversations.js";
|
|
|
9
9
|
export * from "./collaboration.js";
|
|
10
10
|
export * from "./messages.js";
|
|
11
11
|
export * from "./invocations.js";
|
|
12
|
+
export * from "./scout-dispatch.js";
|
|
12
13
|
export * from "./deliveries.js";
|
|
13
14
|
export * from "./transports.js";
|
|
14
15
|
export * from "./events.js";
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./conversations.js";
|
|
|
9
9
|
export * from "./collaboration.js";
|
|
10
10
|
export * from "./messages.js";
|
|
11
11
|
export * from "./invocations.js";
|
|
12
|
+
export * from "./scout-dispatch.js";
|
|
12
13
|
export * from "./deliveries.js";
|
|
13
14
|
export * from "./transports.js";
|
|
14
15
|
export * from "./events.js";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AdvertiseScope, DeliveryTransport, ScoutId } from "./common.js";
|
|
2
|
+
export type ScoutDispatchKind = "ambiguous" | "unknown" | "unparseable";
|
|
3
|
+
export type ScoutCandidateEndpointState = "online" | "offline" | "unknown";
|
|
4
|
+
export interface ScoutDispatchCandidate {
|
|
5
|
+
agentId: ScoutId;
|
|
6
|
+
displayName: string;
|
|
7
|
+
authorityNodeId?: ScoutId;
|
|
8
|
+
homeNodeId?: ScoutId;
|
|
9
|
+
advertiseScope?: AdvertiseScope;
|
|
10
|
+
selector?: string | null;
|
|
11
|
+
defaultSelector?: string | null;
|
|
12
|
+
workspace?: string | null;
|
|
13
|
+
node?: string | null;
|
|
14
|
+
projectRoot?: string | null;
|
|
15
|
+
endpointState: ScoutCandidateEndpointState;
|
|
16
|
+
transport?: DeliveryTransport | null;
|
|
17
|
+
}
|
|
18
|
+
export interface ScoutDispatchEnvelope {
|
|
19
|
+
kind: ScoutDispatchKind;
|
|
20
|
+
askedLabel: string;
|
|
21
|
+
detail: string;
|
|
22
|
+
candidates: ScoutDispatchCandidate[];
|
|
23
|
+
dispatchedAt: number;
|
|
24
|
+
dispatcherNodeId: ScoutId;
|
|
25
|
+
}
|
|
26
|
+
export interface ScoutDispatchRecord extends ScoutDispatchEnvelope {
|
|
27
|
+
id: ScoutId;
|
|
28
|
+
invocationId?: ScoutId;
|
|
29
|
+
conversationId?: ScoutId;
|
|
30
|
+
requesterId?: ScoutId;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -3,15 +3,18 @@ import { describe, expect, test } from "bun:test";
|
|
|
3
3
|
import {
|
|
4
4
|
constructAgentAlias,
|
|
5
5
|
constructAgentIdentity,
|
|
6
|
+
diagnoseAgentIdentity,
|
|
6
7
|
extractAgentIdentities,
|
|
7
8
|
formatAgentAlias,
|
|
8
9
|
formatAgentIdentity,
|
|
9
10
|
formatMinimalAgentIdentity,
|
|
11
|
+
isReservedAgentDefinitionId,
|
|
10
12
|
parseAgentIdentity,
|
|
11
13
|
parseAgentSelector,
|
|
12
14
|
resolveAgentAlias,
|
|
13
15
|
resolveAgentIdentity,
|
|
14
16
|
resolveAgentSelector,
|
|
17
|
+
SCOUT_DISPATCHER_AGENT_ID,
|
|
15
18
|
} from "./agent-identity.js";
|
|
16
19
|
|
|
17
20
|
describe("agent identity grammar", () => {
|
|
@@ -271,3 +274,75 @@ describe("agent identity resolution", () => {
|
|
|
271
274
|
expect(formatMinimalAgentIdentity(candidates[5], candidates)).toBe("@hudson.node:backup-mac-mini");
|
|
272
275
|
});
|
|
273
276
|
});
|
|
277
|
+
|
|
278
|
+
describe("scout dispatcher reservation", () => {
|
|
279
|
+
test("reserves the scout definition id", () => {
|
|
280
|
+
expect(SCOUT_DISPATCHER_AGENT_ID).toBe("scout");
|
|
281
|
+
expect(isReservedAgentDefinitionId("scout")).toBe(true);
|
|
282
|
+
expect(isReservedAgentDefinitionId("Scout")).toBe(true);
|
|
283
|
+
expect(isReservedAgentDefinitionId("SCOUT ")).toBe(true);
|
|
284
|
+
expect(isReservedAgentDefinitionId("scoutie")).toBe(false);
|
|
285
|
+
expect(isReservedAgentDefinitionId("scout-ie")).toBe(false);
|
|
286
|
+
expect(isReservedAgentDefinitionId("")).toBe(false);
|
|
287
|
+
expect(isReservedAgentDefinitionId(null)).toBe(false);
|
|
288
|
+
expect(isReservedAgentDefinitionId(undefined)).toBe(false);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe("agent identity diagnosis", () => {
|
|
293
|
+
const scoutieMiniMain = {
|
|
294
|
+
agentId: "scoutie.mini.main",
|
|
295
|
+
definitionId: "scoutie",
|
|
296
|
+
nodeQualifier: "mini",
|
|
297
|
+
workspaceQualifier: "main",
|
|
298
|
+
aliases: ["@scoutie.main.node:mini", "@scoutie"],
|
|
299
|
+
};
|
|
300
|
+
const scoutieMainMini = {
|
|
301
|
+
agentId: "scoutie.main.mini",
|
|
302
|
+
definitionId: "scoutie",
|
|
303
|
+
nodeQualifier: "mini",
|
|
304
|
+
workspaceQualifier: "main",
|
|
305
|
+
aliases: ["@scoutie.main.node:mini", "@scoutie"],
|
|
306
|
+
};
|
|
307
|
+
const arachMini = {
|
|
308
|
+
agentId: "arach.mini",
|
|
309
|
+
definitionId: "arach",
|
|
310
|
+
nodeQualifier: "mini",
|
|
311
|
+
aliases: ["@arach.node:mini", "@arach"],
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
test("returns resolved when exactly one candidate matches", () => {
|
|
315
|
+
const identity = parseAgentIdentity("@arach");
|
|
316
|
+
expect(identity).not.toBeNull();
|
|
317
|
+
const result = diagnoseAgentIdentity(identity!, [scoutieMiniMain, arachMini]);
|
|
318
|
+
expect(result.kind).toBe("resolved");
|
|
319
|
+
if (result.kind === "resolved") {
|
|
320
|
+
expect(result.match.agentId).toBe("arach.mini");
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test("returns ambiguous when multiple exact alias matches exist", () => {
|
|
325
|
+
const identity = parseAgentIdentity("@scoutie");
|
|
326
|
+
expect(identity).not.toBeNull();
|
|
327
|
+
const result = diagnoseAgentIdentity(identity!, [scoutieMiniMain, scoutieMainMini, arachMini]);
|
|
328
|
+
expect(result.kind).toBe("ambiguous");
|
|
329
|
+
if (result.kind === "ambiguous") {
|
|
330
|
+
expect(result.candidates.map((c) => c.agentId).sort()).toEqual([
|
|
331
|
+
"scoutie.main.mini",
|
|
332
|
+
"scoutie.mini.main",
|
|
333
|
+
]);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("returns unknown when no candidate matches", () => {
|
|
338
|
+
const identity = parseAgentIdentity("@nonexistent");
|
|
339
|
+
expect(identity).not.toBeNull();
|
|
340
|
+
const result = diagnoseAgentIdentity(identity!, [scoutieMiniMain, arachMini]);
|
|
341
|
+
expect(result.kind).toBe("unknown");
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test("resolveAgentIdentity returns null on ambiguity (back-compat)", () => {
|
|
345
|
+
const identity = parseAgentIdentity("@scoutie");
|
|
346
|
+
expect(resolveAgentIdentity(identity!, [scoutieMiniMain, scoutieMainMini])).toBeNull();
|
|
347
|
+
});
|
|
348
|
+
});
|
package/src/agent-identity.ts
CHANGED
|
@@ -65,6 +65,17 @@ export function normalizeAgentIdentitySegment(value: string): string {
|
|
|
65
65
|
|
|
66
66
|
export const normalizeAgentSelectorSegment = normalizeAgentIdentitySegment;
|
|
67
67
|
|
|
68
|
+
export const SCOUT_DISPATCHER_AGENT_ID = "scout";
|
|
69
|
+
|
|
70
|
+
const RESERVED_AGENT_DEFINITION_IDS: ReadonlySet<string> = new Set([
|
|
71
|
+
SCOUT_DISPATCHER_AGENT_ID,
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
export function isReservedAgentDefinitionId(value: string | null | undefined): boolean {
|
|
75
|
+
if (!value) return false;
|
|
76
|
+
return RESERVED_AGENT_DEFINITION_IDS.has(normalizeAgentIdentitySegment(value));
|
|
77
|
+
}
|
|
78
|
+
|
|
68
79
|
function normalizeDimensionKey(value: string): AgentIdentityDimension | null {
|
|
69
80
|
return DIMENSION_ALIASES[normalizeAgentIdentitySegment(value)] ?? null;
|
|
70
81
|
}
|
|
@@ -363,30 +374,50 @@ export function resolveAgentIdentity<T extends AgentIdentityCandidate>(
|
|
|
363
374
|
identity: AgentIdentity,
|
|
364
375
|
candidates: T[],
|
|
365
376
|
): T | null {
|
|
377
|
+
const diagnosis = diagnoseAgentIdentity(identity, candidates);
|
|
378
|
+
return diagnosis.kind === "resolved" ? diagnosis.match : null;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export type AgentIdentityDiagnosis<T extends AgentIdentityCandidate> =
|
|
382
|
+
| { kind: "resolved"; match: T }
|
|
383
|
+
| { kind: "ambiguous"; candidates: T[] }
|
|
384
|
+
| { kind: "unknown" };
|
|
385
|
+
|
|
386
|
+
export function diagnoseAgentIdentity<T extends AgentIdentityCandidate>(
|
|
387
|
+
identity: AgentIdentity,
|
|
388
|
+
candidates: T[],
|
|
389
|
+
): AgentIdentityDiagnosis<T> {
|
|
366
390
|
const aliasKeys = identityAliasKeys(identity);
|
|
367
391
|
const exactAliasMatches = candidates.filter((candidate) => {
|
|
368
392
|
const candidateAliasKeys = explicitCandidateAliases(candidate).map(canonicalizeAliasValue);
|
|
369
393
|
return aliasKeys.some((key) => candidateAliasKeys.includes(key));
|
|
370
394
|
});
|
|
371
395
|
if (exactAliasMatches.length === 1) {
|
|
372
|
-
return exactAliasMatches[0];
|
|
396
|
+
return { kind: "resolved", match: exactAliasMatches[0] };
|
|
373
397
|
}
|
|
374
398
|
if (exactAliasMatches.length > 1) {
|
|
375
|
-
return
|
|
399
|
+
return { kind: "ambiguous", candidates: exactAliasMatches };
|
|
376
400
|
}
|
|
377
401
|
|
|
378
402
|
const matches = candidates.filter((candidate) => agentIdentityMatches(identity, candidate));
|
|
379
403
|
if (matches.length === 1) {
|
|
380
|
-
return matches[0];
|
|
404
|
+
return { kind: "resolved", match: matches[0] };
|
|
405
|
+
}
|
|
406
|
+
if (matches.length === 0) {
|
|
407
|
+
return { kind: "unknown" };
|
|
381
408
|
}
|
|
382
409
|
|
|
383
410
|
if (!identity.nodeQualifier && !identity.workspaceQualifier && !identity.profile && !identity.harness) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
411
|
+
const exactIdMatch = matches.find(
|
|
412
|
+
(candidate) => normalizeAgentIdentitySegment(candidate.agentId) === identity.definitionId,
|
|
413
|
+
);
|
|
414
|
+
if (exactIdMatch) {
|
|
415
|
+
return { kind: "resolved", match: exactIdMatch };
|
|
416
|
+
}
|
|
417
|
+
return { kind: "ambiguous", candidates: matches };
|
|
387
418
|
}
|
|
388
419
|
|
|
389
|
-
return
|
|
420
|
+
return { kind: "ambiguous", candidates: matches };
|
|
390
421
|
}
|
|
391
422
|
|
|
392
423
|
export function constructAgentAlias(input: {
|
package/src/common.ts
CHANGED
|
@@ -17,11 +17,27 @@ export type AgentState =
|
|
|
17
17
|
export type VisibilityScope = "private" | "workspace" | "public" | "system";
|
|
18
18
|
|
|
19
19
|
export type DeliveryStatus =
|
|
20
|
+
// Outbox lifecycle (Issue 3 of broker-three-contracts)
|
|
21
|
+
| "accepted" // local journal write done, awaiting forward
|
|
22
|
+
| "peer_acked" // peer broker journaled the envelope
|
|
23
|
+
| "running" // target agent claimed the flight
|
|
24
|
+
| "completed" // terminal success
|
|
25
|
+
| "deferred" // within retry window — see metadata.nextAttemptAt
|
|
26
|
+
| "failed" // terminal failure — see metadata.failureReason
|
|
27
|
+
| "cancelled"
|
|
28
|
+
// Legacy values kept for backwards-compatible journal replay
|
|
20
29
|
| "pending"
|
|
21
30
|
| "leased"
|
|
22
31
|
| "sent"
|
|
23
|
-
| "acknowledged"
|
|
24
|
-
|
|
32
|
+
| "acknowledged";
|
|
33
|
+
|
|
34
|
+
export type DeliveryFailureReason =
|
|
35
|
+
| "peer_unreachable" // TCP/network failure — retry-able
|
|
36
|
+
| "peer_rejected" // peer broker explicitly refused — not retried
|
|
37
|
+
| "agent_offline" // peer ACK'd but target agent never claimed
|
|
38
|
+
| "timeout"
|
|
39
|
+
| "harness_error"
|
|
40
|
+
| "dispatch" // ambiguous label — see metadata.scoutDispatch
|
|
25
41
|
| "cancelled";
|
|
26
42
|
|
|
27
43
|
export type DeliveryPolicy = "best_effort" | "must_ack" | "durable" | "ephemeral";
|
package/src/events.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { FlightRecord, InvocationRequest } from "./invocations.js";
|
|
|
6
6
|
import type { MessageRecord } from "./messages.js";
|
|
7
7
|
import type { AgentDefinition, AgentEndpoint, ActorIdentity } from "./actors.js";
|
|
8
8
|
import type { CollaborationEvent, CollaborationRecord } from "./collaboration.js";
|
|
9
|
+
import type { ScoutDispatchRecord } from "./scout-dispatch.js";
|
|
9
10
|
|
|
10
11
|
export interface ControlEventBase<K extends string, P> {
|
|
11
12
|
id: ScoutId;
|
|
@@ -60,6 +61,11 @@ export type DeliveryAttemptedEvent = ControlEventBase<"delivery.attempted", {
|
|
|
60
61
|
attempt: DeliveryAttempt;
|
|
61
62
|
}>;
|
|
62
63
|
|
|
64
|
+
export type DeliveryStateChangedEvent = ControlEventBase<"delivery.state.changed", {
|
|
65
|
+
delivery: DeliveryIntent;
|
|
66
|
+
previousStatus?: DeliveryIntent["status"];
|
|
67
|
+
}>;
|
|
68
|
+
|
|
63
69
|
export type CollaborationUpsertedEvent = ControlEventBase<"collaboration.upserted", {
|
|
64
70
|
record: CollaborationRecord;
|
|
65
71
|
}>;
|
|
@@ -68,6 +74,10 @@ export type CollaborationEventAppendedEvent = ControlEventBase<"collaboration.ev
|
|
|
68
74
|
event: CollaborationEvent;
|
|
69
75
|
}>;
|
|
70
76
|
|
|
77
|
+
export type ScoutDispatchedEvent = ControlEventBase<"scout.dispatched", {
|
|
78
|
+
dispatch: ScoutDispatchRecord;
|
|
79
|
+
}>;
|
|
80
|
+
|
|
71
81
|
export type ControlEvent =
|
|
72
82
|
| NodeUpsertedEvent
|
|
73
83
|
| ActorRegisteredEvent
|
|
@@ -80,5 +90,7 @@ export type ControlEvent =
|
|
|
80
90
|
| FlightUpdatedEvent
|
|
81
91
|
| DeliveryPlannedEvent
|
|
82
92
|
| DeliveryAttemptedEvent
|
|
93
|
+
| DeliveryStateChangedEvent
|
|
83
94
|
| CollaborationUpsertedEvent
|
|
84
|
-
| CollaborationEventAppendedEvent
|
|
95
|
+
| CollaborationEventAppendedEvent
|
|
96
|
+
| ScoutDispatchedEvent;
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./conversations.js";
|
|
|
9
9
|
export * from "./collaboration.js";
|
|
10
10
|
export * from "./messages.js";
|
|
11
11
|
export * from "./invocations.js";
|
|
12
|
+
export * from "./scout-dispatch.js";
|
|
12
13
|
export * from "./deliveries.js";
|
|
13
14
|
export * from "./transports.js";
|
|
14
15
|
export * from "./events.js";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AdvertiseScope, DeliveryTransport, ScoutId } from "./common.js";
|
|
2
|
+
|
|
3
|
+
export type ScoutDispatchKind = "ambiguous" | "unknown" | "unparseable";
|
|
4
|
+
|
|
5
|
+
export type ScoutCandidateEndpointState = "online" | "offline" | "unknown";
|
|
6
|
+
|
|
7
|
+
export interface ScoutDispatchCandidate {
|
|
8
|
+
agentId: ScoutId;
|
|
9
|
+
displayName: string;
|
|
10
|
+
authorityNodeId?: ScoutId;
|
|
11
|
+
homeNodeId?: ScoutId;
|
|
12
|
+
advertiseScope?: AdvertiseScope;
|
|
13
|
+
selector?: string | null;
|
|
14
|
+
defaultSelector?: string | null;
|
|
15
|
+
workspace?: string | null;
|
|
16
|
+
node?: string | null;
|
|
17
|
+
projectRoot?: string | null;
|
|
18
|
+
endpointState: ScoutCandidateEndpointState;
|
|
19
|
+
transport?: DeliveryTransport | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ScoutDispatchEnvelope {
|
|
23
|
+
kind: ScoutDispatchKind;
|
|
24
|
+
askedLabel: string;
|
|
25
|
+
detail: string;
|
|
26
|
+
candidates: ScoutDispatchCandidate[];
|
|
27
|
+
dispatchedAt: number;
|
|
28
|
+
dispatcherNodeId: ScoutId;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ScoutDispatchRecord extends ScoutDispatchEnvelope {
|
|
32
|
+
id: ScoutId;
|
|
33
|
+
invocationId?: ScoutId;
|
|
34
|
+
conversationId?: ScoutId;
|
|
35
|
+
requesterId?: ScoutId;
|
|
36
|
+
}
|