@openscout/protocol 0.2.61 → 0.2.63
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/actors.d.ts +1 -1
- package/dist/agent-identity.d.ts +5 -1
- package/dist/agent-identity.js +115 -6
- package/dist/events.d.ts +4 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/scout-delivery.d.ts +73 -0
- package/dist/scout-delivery.js +1 -0
- package/dist/scout-dispatch.d.ts +47 -2
- package/package.json +2 -2
- package/src/actors.ts +2 -1
- package/src/agent-identity.test.ts +91 -4
- package/src/agent-identity.ts +140 -7
- package/src/events.ts +5 -0
- package/src/index.ts +1 -0
- package/src/scout-delivery.ts +100 -0
- package/src/scout-dispatch.ts +47 -2
package/dist/actors.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ActorKind, AdvertiseScope, AgentState, MetadataMap, ScoutId } from "./common.js";
|
|
2
2
|
export type AgentClass = "general" | "builder" | "reviewer" | "researcher" | "operator" | "bridge" | "system";
|
|
3
3
|
export type AgentCapability = "chat" | "invoke" | "deliver" | "speak" | "listen" | "bridge" | "summarize" | "review" | "execute";
|
|
4
|
-
export type AgentHarness = "codex" | "claude" | "native" | "worker" | "bridge" | "http";
|
|
4
|
+
export type AgentHarness = "codex" | "claude" | "native" | "worker" | "bridge" | "http" | "pi";
|
|
5
5
|
export type WakePolicy = "manual" | "on_demand" | "keep_warm";
|
|
6
6
|
export interface ActorIdentity {
|
|
7
7
|
id: ScoutId;
|
package/dist/agent-identity.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ScoutId } from "./common.js";
|
|
2
|
-
export type AgentIdentityDimension = "workspace" | "profile" | "harness" | "node";
|
|
2
|
+
export type AgentIdentityDimension = "workspace" | "profile" | "harness" | "model" | "node";
|
|
3
3
|
export interface AgentIdentity {
|
|
4
4
|
raw: string;
|
|
5
5
|
label: string;
|
|
@@ -8,6 +8,7 @@ export interface AgentIdentity {
|
|
|
8
8
|
workspaceQualifier?: string;
|
|
9
9
|
profile?: string;
|
|
10
10
|
harness?: string;
|
|
11
|
+
model?: string;
|
|
11
12
|
}
|
|
12
13
|
export interface AgentIdentityCandidate {
|
|
13
14
|
agentId: ScoutId;
|
|
@@ -16,6 +17,7 @@ export interface AgentIdentityCandidate {
|
|
|
16
17
|
workspaceQualifier?: string;
|
|
17
18
|
profile?: string;
|
|
18
19
|
harness?: string;
|
|
20
|
+
model?: string;
|
|
19
21
|
aliases?: string[];
|
|
20
22
|
}
|
|
21
23
|
export interface AgentIdentityInput {
|
|
@@ -24,6 +26,7 @@ export interface AgentIdentityInput {
|
|
|
24
26
|
workspaceQualifier?: string;
|
|
25
27
|
profile?: string;
|
|
26
28
|
harness?: string;
|
|
29
|
+
model?: string;
|
|
27
30
|
}
|
|
28
31
|
export interface AgentIdentityAlias {
|
|
29
32
|
alias: string;
|
|
@@ -32,6 +35,7 @@ export interface AgentIdentityAlias {
|
|
|
32
35
|
export declare function normalizeAgentIdentitySegment(value: string): string;
|
|
33
36
|
export declare const normalizeAgentSelectorSegment: typeof normalizeAgentIdentitySegment;
|
|
34
37
|
export declare const SCOUT_DISPATCHER_AGENT_ID = "scout";
|
|
38
|
+
export declare const OPENSCOUT_COORDINATOR_AGENT_ID = "openscout";
|
|
35
39
|
export declare const BUILT_IN_AGENT_DEFINITION_IDS: ReadonlySet<string>;
|
|
36
40
|
export declare function isBuiltInAgentDefinitionId(value: string | null | undefined): boolean;
|
|
37
41
|
export declare function isReservedAgentDefinitionId(value: string | null | undefined): boolean;
|
package/dist/agent-identity.js
CHANGED
|
@@ -6,6 +6,7 @@ const DIMENSION_ALIASES = {
|
|
|
6
6
|
persona: "profile",
|
|
7
7
|
harness: "harness",
|
|
8
8
|
runtime: "harness",
|
|
9
|
+
model: "model",
|
|
9
10
|
node: "node",
|
|
10
11
|
host: "node",
|
|
11
12
|
};
|
|
@@ -25,6 +26,7 @@ export function normalizeAgentIdentitySegment(value) {
|
|
|
25
26
|
}
|
|
26
27
|
export const normalizeAgentSelectorSegment = normalizeAgentIdentitySegment;
|
|
27
28
|
export const SCOUT_DISPATCHER_AGENT_ID = "scout";
|
|
29
|
+
export const OPENSCOUT_COORDINATOR_AGENT_ID = "openscout";
|
|
28
30
|
export const BUILT_IN_AGENT_DEFINITION_IDS = new Set([
|
|
29
31
|
SCOUT_DISPATCHER_AGENT_ID,
|
|
30
32
|
"builder",
|
|
@@ -54,6 +56,7 @@ function canonicalizeIdentity(input) {
|
|
|
54
56
|
: undefined;
|
|
55
57
|
const profile = input.profile ? normalizeAgentIdentitySegment(input.profile) : undefined;
|
|
56
58
|
const harness = input.harness ? normalizeAgentIdentitySegment(input.harness) : undefined;
|
|
59
|
+
const model = input.model ? normalizeAgentIdentitySegment(input.model) : undefined;
|
|
57
60
|
const nodeQualifier = input.nodeQualifier
|
|
58
61
|
? normalizeAgentIdentitySegment(input.nodeQualifier)
|
|
59
62
|
: undefined;
|
|
@@ -65,6 +68,7 @@ function canonicalizeIdentity(input) {
|
|
|
65
68
|
...(workspaceQualifier ? { workspaceQualifier } : {}),
|
|
66
69
|
...(profile ? { profile } : {}),
|
|
67
70
|
...(harness ? { harness } : {}),
|
|
71
|
+
...(model ? { model } : {}),
|
|
68
72
|
...(nodeQualifier ? { nodeQualifier } : {}),
|
|
69
73
|
};
|
|
70
74
|
}
|
|
@@ -96,6 +100,9 @@ function parseSegmentedIdentity(raw) {
|
|
|
96
100
|
else if (key === "harness") {
|
|
97
101
|
next.harness = value;
|
|
98
102
|
}
|
|
103
|
+
else if (key === "model") {
|
|
104
|
+
next.model = value;
|
|
105
|
+
}
|
|
99
106
|
else if (key === "node") {
|
|
100
107
|
next.nodeQualifier = value;
|
|
101
108
|
}
|
|
@@ -127,14 +134,60 @@ function parseSegmentedIdentity(raw) {
|
|
|
127
134
|
}
|
|
128
135
|
return constructAgentIdentity(next, { raw });
|
|
129
136
|
}
|
|
137
|
+
function parseShorthandIdentity(raw) {
|
|
138
|
+
const hashIndex = raw.indexOf("#");
|
|
139
|
+
const questionIndex = raw.indexOf("?");
|
|
140
|
+
if (hashIndex === -1 && questionIndex === -1) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
if (raw.indexOf("#", hashIndex + 1) !== -1 || raw.indexOf("?", questionIndex + 1) !== -1) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
if (hashIndex !== -1 && questionIndex !== -1 && questionIndex < hashIndex) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const baseEnd = Math.min(...[hashIndex, questionIndex].filter((index) => index >= 0));
|
|
150
|
+
const base = raw.slice(0, baseEnd);
|
|
151
|
+
if (!base) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const parsedBase = parseSegmentedIdentity(base);
|
|
155
|
+
if (!parsedBase) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
const harnessRaw = hashIndex >= 0
|
|
159
|
+
? raw.slice(hashIndex + 1, questionIndex >= 0 ? questionIndex : raw.length)
|
|
160
|
+
: "";
|
|
161
|
+
const modelRaw = questionIndex >= 0 ? raw.slice(questionIndex + 1) : "";
|
|
162
|
+
if ((hashIndex >= 0 && !harnessRaw) || (questionIndex >= 0 && !modelRaw)) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
if (harnessRaw.includes("#") || harnessRaw.includes("?") || modelRaw.includes("#") || modelRaw.includes("?")) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
if ((parsedBase.harness && harnessRaw) || (parsedBase.model && modelRaw)) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return constructAgentIdentity({
|
|
172
|
+
definitionId: parsedBase.definitionId,
|
|
173
|
+
nodeQualifier: parsedBase.nodeQualifier,
|
|
174
|
+
workspaceQualifier: parsedBase.workspaceQualifier,
|
|
175
|
+
profile: parsedBase.profile,
|
|
176
|
+
harness: harnessRaw || parsedBase.harness,
|
|
177
|
+
model: modelRaw || parsedBase.model,
|
|
178
|
+
}, { raw });
|
|
179
|
+
}
|
|
130
180
|
export function parseAgentIdentity(value) {
|
|
131
181
|
const raw = trimIdentityPrefix(value);
|
|
132
182
|
if (!raw) {
|
|
133
183
|
return null;
|
|
134
184
|
}
|
|
135
|
-
if (raw.includes("@")
|
|
185
|
+
if (raw.includes("@")) {
|
|
136
186
|
return null;
|
|
137
187
|
}
|
|
188
|
+
if (raw.includes("#") || raw.includes("?")) {
|
|
189
|
+
return parseShorthandIdentity(raw);
|
|
190
|
+
}
|
|
138
191
|
return parseSegmentedIdentity(raw);
|
|
139
192
|
}
|
|
140
193
|
export function formatAgentIdentity(input, options = {}) {
|
|
@@ -153,6 +206,9 @@ export function formatAgentIdentity(input, options = {}) {
|
|
|
153
206
|
if (canonical.harness) {
|
|
154
207
|
segments.push(`harness:${canonical.harness}`);
|
|
155
208
|
}
|
|
209
|
+
if (canonical.model) {
|
|
210
|
+
segments.push(`model:${canonical.model}`);
|
|
211
|
+
}
|
|
156
212
|
if (canonical.nodeQualifier) {
|
|
157
213
|
segments.push(`node:${canonical.nodeQualifier}`);
|
|
158
214
|
}
|
|
@@ -171,13 +227,14 @@ export function constructAgentIdentity(input, options = {}) {
|
|
|
171
227
|
...(canonical.workspaceQualifier ? { workspaceQualifier: canonical.workspaceQualifier } : {}),
|
|
172
228
|
...(canonical.profile ? { profile: canonical.profile } : {}),
|
|
173
229
|
...(canonical.harness ? { harness: canonical.harness } : {}),
|
|
230
|
+
...(canonical.model ? { model: canonical.model } : {}),
|
|
174
231
|
};
|
|
175
232
|
}
|
|
176
233
|
export function extractAgentIdentities(text) {
|
|
177
234
|
return extractAgentMentions(text).parsed;
|
|
178
235
|
}
|
|
179
236
|
export function extractAgentMentions(text) {
|
|
180
|
-
const matches = Array.from(text.matchAll(/(
|
|
237
|
+
const matches = Array.from(text.matchAll(/(^|[\s([{'"`])@([a-z0-9][a-z0-9._/:-]*(?:#[a-z0-9][a-z0-9._/:-]*)?(?:\?[a-z0-9][a-z0-9._/:-]*)?)(?=$|[\s)\]}",.!?:;'"`])/gi));
|
|
181
238
|
const identities = new Map();
|
|
182
239
|
const unparsed = [];
|
|
183
240
|
for (const match of matches) {
|
|
@@ -202,6 +259,7 @@ function candidateAliases(candidate) {
|
|
|
202
259
|
workspaceQualifier: candidate.workspaceQualifier,
|
|
203
260
|
profile: candidate.profile,
|
|
204
261
|
harness: candidate.harness,
|
|
262
|
+
model: candidate.model,
|
|
205
263
|
});
|
|
206
264
|
if (!canonical) {
|
|
207
265
|
return [];
|
|
@@ -218,6 +276,9 @@ function candidateAliases(candidate) {
|
|
|
218
276
|
canonical.harness
|
|
219
277
|
? formatAgentIdentity({ definitionId: canonical.definitionId, harness: canonical.harness })
|
|
220
278
|
: "",
|
|
279
|
+
canonical.model
|
|
280
|
+
? formatAgentIdentity({ definitionId: canonical.definitionId, model: canonical.model })
|
|
281
|
+
: "",
|
|
221
282
|
canonical.nodeQualifier
|
|
222
283
|
? formatAgentIdentity({ definitionId: canonical.definitionId, nodeQualifier: canonical.nodeQualifier })
|
|
223
284
|
: "",
|
|
@@ -226,7 +287,11 @@ function candidateAliases(candidate) {
|
|
|
226
287
|
return Array.from(new Set(aliases.map((alias) => trimIdentityPrefix(alias)).filter(Boolean)));
|
|
227
288
|
}
|
|
228
289
|
function explicitCandidateAliases(candidate) {
|
|
229
|
-
|
|
290
|
+
const canonical = candidateCanonicalIdentity(candidate);
|
|
291
|
+
const implicitAliases = canonical?.definitionId === OPENSCOUT_COORDINATOR_AGENT_ID
|
|
292
|
+
? [SCOUT_DISPATCHER_AGENT_ID]
|
|
293
|
+
: [];
|
|
294
|
+
return Array.from(new Set([...(candidate.aliases ?? []), ...implicitAliases]
|
|
230
295
|
.map((alias) => normalizeAgentIdentitySegment(trimIdentityPrefix(alias)))
|
|
231
296
|
.filter(Boolean)));
|
|
232
297
|
}
|
|
@@ -251,6 +316,7 @@ function candidateCanonicalIdentity(candidate) {
|
|
|
251
316
|
workspaceQualifier: candidate.workspaceQualifier,
|
|
252
317
|
profile: candidate.profile,
|
|
253
318
|
harness: candidate.harness,
|
|
319
|
+
model: candidate.model,
|
|
254
320
|
});
|
|
255
321
|
}
|
|
256
322
|
function candidateDimensionValue(candidate, dimension) {
|
|
@@ -264,8 +330,42 @@ function candidateDimensionValue(candidate, dimension) {
|
|
|
264
330
|
return canonical.profile;
|
|
265
331
|
if (dimension === "harness")
|
|
266
332
|
return canonical.harness;
|
|
333
|
+
if (dimension === "model")
|
|
334
|
+
return canonical.model;
|
|
267
335
|
return canonical.nodeQualifier;
|
|
268
336
|
}
|
|
337
|
+
function modelAliasKeys(value) {
|
|
338
|
+
const normalized = value ? normalizeAgentIdentitySegment(value) : "";
|
|
339
|
+
if (!normalized) {
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
const parts = normalized.split("-").filter(Boolean);
|
|
343
|
+
const aliases = new Set([normalized]);
|
|
344
|
+
if (parts.length > 1 && ["claude", "gpt", "openai", "anthropic"].includes(parts[0])) {
|
|
345
|
+
aliases.add(parts.slice(1).join("-"));
|
|
346
|
+
}
|
|
347
|
+
for (const family of ["sonnet", "opus", "haiku", "mini", "nano", "pro"]) {
|
|
348
|
+
const index = parts.indexOf(family);
|
|
349
|
+
if (index >= 0) {
|
|
350
|
+
aliases.add(family);
|
|
351
|
+
aliases.add(parts.slice(index).join("-"));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return Array.from(aliases).filter(Boolean);
|
|
355
|
+
}
|
|
356
|
+
function modelMatches(identityModel, candidate) {
|
|
357
|
+
if (!identityModel) {
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
return modelAliasKeys(candidate.model).includes(identityModel);
|
|
361
|
+
}
|
|
362
|
+
function definitionMatchesIdentity(identityDefinitionId, candidateDefinitionId) {
|
|
363
|
+
if (identityDefinitionId === candidateDefinitionId) {
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
return identityDefinitionId === SCOUT_DISPATCHER_AGENT_ID
|
|
367
|
+
&& candidateDefinitionId === OPENSCOUT_COORDINATOR_AGENT_ID;
|
|
368
|
+
}
|
|
269
369
|
export function agentIdentityMatches(identity, candidate) {
|
|
270
370
|
const aliasKeys = identityAliasKeys(identity);
|
|
271
371
|
const candidateAliasKeys = explicitCandidateAliases(candidate).map(canonicalizeAliasValue);
|
|
@@ -278,8 +378,9 @@ export function agentIdentityMatches(identity, candidate) {
|
|
|
278
378
|
workspaceQualifier: candidate.workspaceQualifier,
|
|
279
379
|
profile: candidate.profile,
|
|
280
380
|
harness: candidate.harness,
|
|
381
|
+
model: candidate.model,
|
|
281
382
|
});
|
|
282
|
-
if (!canonical || identity.definitionId
|
|
383
|
+
if (!canonical || !definitionMatchesIdentity(identity.definitionId, canonical.definitionId)) {
|
|
283
384
|
return false;
|
|
284
385
|
}
|
|
285
386
|
if (identity.nodeQualifier && identity.nodeQualifier !== canonical.nodeQualifier) {
|
|
@@ -294,6 +395,9 @@ export function agentIdentityMatches(identity, candidate) {
|
|
|
294
395
|
if (identity.harness && identity.harness !== canonical.harness) {
|
|
295
396
|
return candidateAliases(candidate).includes(trimIdentityPrefix(identity.label));
|
|
296
397
|
}
|
|
398
|
+
if (identity.model && !modelMatches(identity.model, candidate)) {
|
|
399
|
+
return candidateAliases(candidate).includes(trimIdentityPrefix(identity.label));
|
|
400
|
+
}
|
|
297
401
|
return true;
|
|
298
402
|
}
|
|
299
403
|
export function resolveAgentIdentity(identity, candidates) {
|
|
@@ -319,7 +423,11 @@ export function diagnoseAgentIdentity(identity, candidates) {
|
|
|
319
423
|
if (matches.length === 0) {
|
|
320
424
|
return { kind: "unknown" };
|
|
321
425
|
}
|
|
322
|
-
if (!identity.nodeQualifier
|
|
426
|
+
if (!identity.nodeQualifier
|
|
427
|
+
&& !identity.workspaceQualifier
|
|
428
|
+
&& !identity.profile
|
|
429
|
+
&& !identity.harness
|
|
430
|
+
&& !identity.model) {
|
|
323
431
|
const exactIdMatch = matches.find((candidate) => normalizeAgentIdentitySegment(candidate.agentId) === identity.definitionId);
|
|
324
432
|
if (exactIdMatch) {
|
|
325
433
|
return { kind: "resolved", match: exactIdMatch };
|
|
@@ -366,6 +474,7 @@ function buildIdentitySubset(candidate, dimensions) {
|
|
|
366
474
|
: {}),
|
|
367
475
|
...(dimensions.includes("profile") && canonical.profile ? { profile: canonical.profile } : {}),
|
|
368
476
|
...(dimensions.includes("harness") && canonical.harness ? { harness: canonical.harness } : {}),
|
|
477
|
+
...(dimensions.includes("model") && canonical.model ? { model: canonical.model } : {}),
|
|
369
478
|
...(dimensions.includes("node") && canonical.nodeQualifier ? { nodeQualifier: canonical.nodeQualifier } : {}),
|
|
370
479
|
});
|
|
371
480
|
}
|
|
@@ -389,7 +498,7 @@ export function formatMinimalAgentIdentity(candidate, candidates, options = {})
|
|
|
389
498
|
if (peers.length <= 1) {
|
|
390
499
|
return formatAgentIdentity({ definitionId: canonical.definitionId }, options);
|
|
391
500
|
}
|
|
392
|
-
const orderedDimensions = ["workspace", "profile", "harness", "node"];
|
|
501
|
+
const orderedDimensions = ["workspace", "profile", "harness", "model", "node"];
|
|
393
502
|
const selectedDimensions = [];
|
|
394
503
|
let remainingPeers = peers.filter((peer) => peer !== candidate);
|
|
395
504
|
for (const dimension of orderedDimensions) {
|
package/dist/events.d.ts
CHANGED
|
@@ -27,6 +27,9 @@ export type AgentRegisteredEvent = ControlEventBase<"agent.registered", {
|
|
|
27
27
|
export type AgentEndpointUpsertedEvent = ControlEventBase<"agent.endpoint.upserted", {
|
|
28
28
|
endpoint: AgentEndpoint;
|
|
29
29
|
}>;
|
|
30
|
+
export type AgentEndpointDeletedEvent = ControlEventBase<"agent.endpoint.deleted", {
|
|
31
|
+
endpointId: string;
|
|
32
|
+
}>;
|
|
30
33
|
export type ConversationUpsertedEvent = ControlEventBase<"conversation.upserted", {
|
|
31
34
|
conversation: ConversationDefinition;
|
|
32
35
|
}>;
|
|
@@ -61,4 +64,4 @@ export type CollaborationEventAppendedEvent = ControlEventBase<"collaboration.ev
|
|
|
61
64
|
export type ScoutDispatchedEvent = ControlEventBase<"scout.dispatched", {
|
|
62
65
|
dispatch: ScoutDispatchRecord;
|
|
63
66
|
}>;
|
|
64
|
-
export type ControlEvent = NodeUpsertedEvent | ActorRegisteredEvent | AgentRegisteredEvent | AgentEndpointUpsertedEvent | ConversationUpsertedEvent | BindingUpsertedEvent | MessagePostedEvent | InvocationRequestedEvent | FlightUpdatedEvent | DeliveryPlannedEvent | DeliveryAttemptedEvent | DeliveryStateChangedEvent | CollaborationUpsertedEvent | CollaborationEventAppendedEvent | ScoutDispatchedEvent;
|
|
67
|
+
export type ControlEvent = NodeUpsertedEvent | ActorRegisteredEvent | AgentRegisteredEvent | AgentEndpointUpsertedEvent | AgentEndpointDeletedEvent | ConversationUpsertedEvent | BindingUpsertedEvent | MessagePostedEvent | InvocationRequestedEvent | FlightUpdatedEvent | DeliveryPlannedEvent | DeliveryAttemptedEvent | DeliveryStateChangedEvent | CollaborationUpsertedEvent | CollaborationEventAppendedEvent | ScoutDispatchedEvent;
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./collaboration.js";
|
|
|
11
11
|
export * from "./messages.js";
|
|
12
12
|
export * from "./invocations.js";
|
|
13
13
|
export * from "./scout-dispatch.js";
|
|
14
|
+
export * from "./scout-delivery.js";
|
|
14
15
|
export * from "./deliveries.js";
|
|
15
16
|
export * from "./transports.js";
|
|
16
17
|
export * from "./events.js";
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./collaboration.js";
|
|
|
11
11
|
export * from "./messages.js";
|
|
12
12
|
export * from "./invocations.js";
|
|
13
13
|
export * from "./scout-dispatch.js";
|
|
14
|
+
export * from "./scout-delivery.js";
|
|
14
15
|
export * from "./deliveries.js";
|
|
15
16
|
export * from "./transports.js";
|
|
16
17
|
export * from "./events.js";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ConversationDefinition } from "./conversations.js";
|
|
2
|
+
import type { MetadataMap, ScoutId } from "./common.js";
|
|
3
|
+
import type { FlightRecord, InvocationExecutionPreference } from "./invocations.js";
|
|
4
|
+
import type { MessageRecord } from "./messages.js";
|
|
5
|
+
import type { ScoutCallerContext, ScoutDispatchRecord, ScoutRoutePolicy, ScoutRouteTarget } from "./scout-dispatch.js";
|
|
6
|
+
export type ScoutDeliverIntent = "tell" | "consult";
|
|
7
|
+
export type ScoutDeliverRouteKind = "dm" | "channel" | "broadcast";
|
|
8
|
+
export type ScoutDeliveryRemediationKind = "choose_target" | "register_target" | "wake_target" | "retry_later";
|
|
9
|
+
export type ScoutDeliverRejectReason = "unknown_target" | "ambiguous_target" | "invalid_target" | "missing_target";
|
|
10
|
+
export interface ScoutDeliverRequest {
|
|
11
|
+
id?: ScoutId;
|
|
12
|
+
caller?: ScoutCallerContext;
|
|
13
|
+
requesterId?: ScoutId;
|
|
14
|
+
requesterNodeId?: ScoutId;
|
|
15
|
+
body: string;
|
|
16
|
+
intent: ScoutDeliverIntent;
|
|
17
|
+
target?: ScoutRouteTarget;
|
|
18
|
+
targetLabel?: string;
|
|
19
|
+
targetAgentId?: ScoutId;
|
|
20
|
+
routePolicy?: ScoutRoutePolicy;
|
|
21
|
+
channel?: string;
|
|
22
|
+
replyToMessageId?: ScoutId;
|
|
23
|
+
speechText?: string;
|
|
24
|
+
ensureAwake?: boolean;
|
|
25
|
+
execution?: InvocationExecutionPreference;
|
|
26
|
+
createdAt?: number;
|
|
27
|
+
collaborationRecordId?: ScoutId;
|
|
28
|
+
messageMetadata?: MetadataMap;
|
|
29
|
+
invocationMetadata?: MetadataMap;
|
|
30
|
+
}
|
|
31
|
+
export interface ScoutDeliveryReceipt {
|
|
32
|
+
requestId: ScoutId;
|
|
33
|
+
routeKind: ScoutDeliverRouteKind;
|
|
34
|
+
requesterId: ScoutId;
|
|
35
|
+
requesterNodeId: ScoutId;
|
|
36
|
+
targetAgentId?: ScoutId;
|
|
37
|
+
targetLabel?: string;
|
|
38
|
+
conversationId: ScoutId;
|
|
39
|
+
messageId: ScoutId;
|
|
40
|
+
flightId?: ScoutId;
|
|
41
|
+
acceptedAt: number;
|
|
42
|
+
}
|
|
43
|
+
export interface ScoutDeliveryRemediationAction {
|
|
44
|
+
kind: ScoutDeliveryRemediationKind;
|
|
45
|
+
detail: string;
|
|
46
|
+
targetAgentId?: ScoutId;
|
|
47
|
+
targetLabel?: string;
|
|
48
|
+
dispatchId?: ScoutId;
|
|
49
|
+
}
|
|
50
|
+
export interface ScoutDeliverAcceptedResponse {
|
|
51
|
+
kind: "delivery";
|
|
52
|
+
accepted: true;
|
|
53
|
+
routeKind: ScoutDeliverRouteKind;
|
|
54
|
+
receipt: ScoutDeliveryReceipt;
|
|
55
|
+
conversation: ConversationDefinition;
|
|
56
|
+
message: MessageRecord;
|
|
57
|
+
targetAgentId?: ScoutId;
|
|
58
|
+
flight?: FlightRecord;
|
|
59
|
+
}
|
|
60
|
+
export interface ScoutDeliverQuestionResponse {
|
|
61
|
+
kind: "question";
|
|
62
|
+
accepted: false;
|
|
63
|
+
question: ScoutDispatchRecord;
|
|
64
|
+
remediation?: ScoutDeliveryRemediationAction;
|
|
65
|
+
}
|
|
66
|
+
export interface ScoutDeliverRejectedResponse {
|
|
67
|
+
kind: "rejected";
|
|
68
|
+
accepted: false;
|
|
69
|
+
reason: ScoutDeliverRejectReason;
|
|
70
|
+
rejection: ScoutDispatchRecord;
|
|
71
|
+
remediation?: ScoutDeliveryRemediationAction;
|
|
72
|
+
}
|
|
73
|
+
export type ScoutDeliverResponse = ScoutDeliverAcceptedResponse | ScoutDeliverQuestionResponse | ScoutDeliverRejectedResponse;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/scout-dispatch.d.ts
CHANGED
|
@@ -1,9 +1,43 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { WakePolicy } from "./actors.js";
|
|
2
|
+
import type { AdvertiseScope, DeliveryTransport, MetadataMap, ScoutId } from "./common.js";
|
|
3
|
+
export type ScoutDispatchKind = "ambiguous" | "unknown" | "unparseable" | "unavailable";
|
|
3
4
|
export type ScoutCandidateEndpointState = "online" | "offline" | "unknown";
|
|
5
|
+
export type ScoutRouteTargetKind = "agent_id" | "agent_label" | "channel" | "broadcast";
|
|
6
|
+
export type ScoutRouteAmbiguousPolicy = "reject" | "ask";
|
|
7
|
+
export interface ScoutCallerContext {
|
|
8
|
+
actorId?: ScoutId;
|
|
9
|
+
nodeId?: ScoutId;
|
|
10
|
+
displayName?: string;
|
|
11
|
+
handle?: string;
|
|
12
|
+
currentDirectory?: string;
|
|
13
|
+
metadata?: MetadataMap;
|
|
14
|
+
}
|
|
15
|
+
export type ScoutRouteTarget = {
|
|
16
|
+
kind: "agent_id";
|
|
17
|
+
agentId: ScoutId;
|
|
18
|
+
value?: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: "agent_label";
|
|
21
|
+
label: string;
|
|
22
|
+
value?: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: "channel";
|
|
25
|
+
channel: string;
|
|
26
|
+
value?: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "broadcast";
|
|
29
|
+
value?: string;
|
|
30
|
+
};
|
|
31
|
+
export interface ScoutRoutePolicy {
|
|
32
|
+
preferLocalNodeId?: ScoutId;
|
|
33
|
+
ambiguous?: ScoutRouteAmbiguousPolicy;
|
|
34
|
+
allowStaleDirectId?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export type ScoutDispatchUnavailableReason = "manual_wake_required" | "retired" | "stale_registration" | "unknown";
|
|
4
37
|
export interface ScoutDispatchCandidate {
|
|
5
38
|
agentId: ScoutId;
|
|
6
39
|
displayName: string;
|
|
40
|
+
label: string;
|
|
7
41
|
authorityNodeId?: ScoutId;
|
|
8
42
|
homeNodeId?: ScoutId;
|
|
9
43
|
advertiseScope?: AdvertiseScope;
|
|
@@ -15,11 +49,22 @@ export interface ScoutDispatchCandidate {
|
|
|
15
49
|
endpointState: ScoutCandidateEndpointState;
|
|
16
50
|
transport?: DeliveryTransport | null;
|
|
17
51
|
}
|
|
52
|
+
export interface ScoutDispatchUnavailableTarget {
|
|
53
|
+
agentId: ScoutId;
|
|
54
|
+
displayName: string;
|
|
55
|
+
reason: ScoutDispatchUnavailableReason;
|
|
56
|
+
detail: string;
|
|
57
|
+
wakePolicy?: WakePolicy | null;
|
|
58
|
+
endpointState: ScoutCandidateEndpointState;
|
|
59
|
+
transport?: DeliveryTransport | null;
|
|
60
|
+
projectRoot?: string | null;
|
|
61
|
+
}
|
|
18
62
|
export interface ScoutDispatchEnvelope {
|
|
19
63
|
kind: ScoutDispatchKind;
|
|
20
64
|
askedLabel: string;
|
|
21
65
|
detail: string;
|
|
22
66
|
candidates: ScoutDispatchCandidate[];
|
|
67
|
+
target?: ScoutDispatchUnavailableTarget;
|
|
23
68
|
dispatchedAt: number;
|
|
24
69
|
dispatcherNodeId: ScoutId;
|
|
25
70
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openscout/protocol",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.63",
|
|
4
4
|
"description": "Typed protocol for the OpenScout local control plane",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
8
|
"types": "./src/index.ts",
|
|
9
|
-
"bun": "./
|
|
9
|
+
"bun": "./src/index.ts",
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
package/src/actors.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
resolveAgentAlias,
|
|
15
15
|
resolveAgentIdentity,
|
|
16
16
|
resolveAgentSelector,
|
|
17
|
+
OPENSCOUT_COORDINATOR_AGENT_ID,
|
|
17
18
|
SCOUT_DISPATCHER_AGENT_ID,
|
|
18
19
|
} from "./agent-identity.js";
|
|
19
20
|
|
|
@@ -58,6 +59,23 @@ describe("agent identity grammar", () => {
|
|
|
58
59
|
harness: "codex",
|
|
59
60
|
nodeQualifier: "mini",
|
|
60
61
|
});
|
|
62
|
+
|
|
63
|
+
expect(parseAgentIdentity("@lattices#codex?5.5")).toEqual({
|
|
64
|
+
raw: "lattices#codex?5.5",
|
|
65
|
+
label: "@lattices.harness:codex.model:5-5",
|
|
66
|
+
definitionId: "lattices",
|
|
67
|
+
harness: "codex",
|
|
68
|
+
model: "5-5",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
expect(parseAgentIdentity("@lattices.main#claude?sonnet")).toEqual({
|
|
72
|
+
raw: "lattices.main#claude?sonnet",
|
|
73
|
+
label: "@lattices.main.harness:claude.model:sonnet",
|
|
74
|
+
definitionId: "lattices",
|
|
75
|
+
workspaceQualifier: "main",
|
|
76
|
+
harness: "claude",
|
|
77
|
+
model: "sonnet",
|
|
78
|
+
});
|
|
61
79
|
});
|
|
62
80
|
|
|
63
81
|
test("parses 3-segment positional as definitionId.workspaceQualifier.nodeQualifier", () => {
|
|
@@ -72,6 +90,8 @@ describe("agent identity grammar", () => {
|
|
|
72
90
|
expect(parseAgentIdentity("")).toBeNull();
|
|
73
91
|
expect(parseAgentIdentity("@arc.profile:")).toBeNull();
|
|
74
92
|
expect(parseAgentIdentity("@arc@mini#main")).toBeNull();
|
|
93
|
+
expect(parseAgentIdentity("@arc#")).toBeNull();
|
|
94
|
+
expect(parseAgentIdentity("@arc?sonnet#claude")).toBeNull();
|
|
75
95
|
expect(parseAgentIdentity("@arc.a.b.c")).toBeNull(); // 3+ positionals rejected
|
|
76
96
|
});
|
|
77
97
|
|
|
@@ -81,14 +101,16 @@ describe("agent identity grammar", () => {
|
|
|
81
101
|
workspaceQualifier: "Super Refactor",
|
|
82
102
|
profile: "Dev Browser",
|
|
83
103
|
harness: "Claude",
|
|
104
|
+
model: "GPT-5.5",
|
|
84
105
|
nodeQualifier: "Mini.local",
|
|
85
106
|
})).toEqual({
|
|
86
|
-
raw: "arc.super-refactor.profile:dev-browser.harness:claude.node:mini-local",
|
|
87
|
-
label: "@arc.super-refactor.profile:dev-browser.harness:claude.node:mini-local",
|
|
107
|
+
raw: "arc.super-refactor.profile:dev-browser.harness:claude.model:gpt-5-5.node:mini-local",
|
|
108
|
+
label: "@arc.super-refactor.profile:dev-browser.harness:claude.model:gpt-5-5.node:mini-local",
|
|
88
109
|
definitionId: "arc",
|
|
89
110
|
workspaceQualifier: "super-refactor",
|
|
90
111
|
profile: "dev-browser",
|
|
91
112
|
harness: "claude",
|
|
113
|
+
model: "gpt-5-5",
|
|
92
114
|
nodeQualifier: "mini-local",
|
|
93
115
|
});
|
|
94
116
|
});
|
|
@@ -99,13 +121,14 @@ describe("agent identity grammar", () => {
|
|
|
99
121
|
workspaceQualifier: "main",
|
|
100
122
|
profile: "dev-browser",
|
|
101
123
|
harness: "claude",
|
|
124
|
+
model: "sonnet",
|
|
102
125
|
nodeQualifier: "mini",
|
|
103
|
-
})).toBe("@arc.main.profile:dev-browser.harness:claude.node:mini");
|
|
126
|
+
})).toBe("@arc.main.profile:dev-browser.harness:claude.model:sonnet.node:mini");
|
|
104
127
|
});
|
|
105
128
|
|
|
106
129
|
test("extracts unique identities from text", () => {
|
|
107
130
|
expect(extractAgentIdentities(
|
|
108
|
-
"Compare @arc.main.profile:dev-browser with @hudson, then ping @arc.main.profile:dev-browser.",
|
|
131
|
+
"Compare @arc.main.profile:dev-browser with @hudson, then ping @arc.main.profile:dev-browser and @lattices#codex?5.5.",
|
|
109
132
|
)).toEqual([
|
|
110
133
|
{
|
|
111
134
|
raw: "arc.main.profile:dev-browser",
|
|
@@ -119,6 +142,13 @@ describe("agent identity grammar", () => {
|
|
|
119
142
|
label: "@hudson",
|
|
120
143
|
definitionId: "hudson",
|
|
121
144
|
},
|
|
145
|
+
{
|
|
146
|
+
raw: "lattices#codex?5.5",
|
|
147
|
+
label: "@lattices.harness:codex.model:5-5",
|
|
148
|
+
definitionId: "lattices",
|
|
149
|
+
harness: "codex",
|
|
150
|
+
model: "5-5",
|
|
151
|
+
},
|
|
122
152
|
]);
|
|
123
153
|
});
|
|
124
154
|
|
|
@@ -202,6 +232,27 @@ describe("agent identity resolution", () => {
|
|
|
202
232
|
harness: "codex",
|
|
203
233
|
nodeQualifier: "backup-mac-mini",
|
|
204
234
|
},
|
|
235
|
+
{
|
|
236
|
+
agentId: "lattices.codex-55",
|
|
237
|
+
definitionId: "lattices",
|
|
238
|
+
workspaceQualifier: "main",
|
|
239
|
+
harness: "codex",
|
|
240
|
+
model: "gpt-5.5",
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
agentId: "lattices.codex-54",
|
|
244
|
+
definitionId: "lattices",
|
|
245
|
+
workspaceQualifier: "main",
|
|
246
|
+
harness: "codex",
|
|
247
|
+
model: "gpt-5.4",
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
agentId: "lattices.sonnet",
|
|
251
|
+
definitionId: "lattices",
|
|
252
|
+
workspaceQualifier: "main",
|
|
253
|
+
harness: "claude",
|
|
254
|
+
model: "claude-sonnet-4-6",
|
|
255
|
+
},
|
|
205
256
|
];
|
|
206
257
|
|
|
207
258
|
test("prefers explicit default aliases for bare identities", () => {
|
|
@@ -240,6 +291,16 @@ describe("agent identity resolution", () => {
|
|
|
240
291
|
expect(resolveAgentIdentity(identity!, candidates)).toEqual(candidates[3]);
|
|
241
292
|
});
|
|
242
293
|
|
|
294
|
+
test("resolves shorthand harness and model qualifiers", () => {
|
|
295
|
+
const codex = parseAgentIdentity("@lattices#codex?5.5");
|
|
296
|
+
expect(codex).not.toBeNull();
|
|
297
|
+
expect(resolveAgentIdentity(codex!, candidates)).toEqual(candidates[6]);
|
|
298
|
+
|
|
299
|
+
const sonnet = parseAgentIdentity("@lattices#claude?sonnet");
|
|
300
|
+
expect(sonnet).not.toBeNull();
|
|
301
|
+
expect(resolveAgentIdentity(sonnet!, candidates)).toEqual(candidates[8]);
|
|
302
|
+
});
|
|
303
|
+
|
|
243
304
|
test("resolves aliases through the explicit alias table", () => {
|
|
244
305
|
const identity = resolveAgentAlias("@huddy", [
|
|
245
306
|
{
|
|
@@ -278,6 +339,7 @@ describe("agent identity resolution", () => {
|
|
|
278
339
|
describe("scout dispatcher reservation", () => {
|
|
279
340
|
test("reserves the scout definition id", () => {
|
|
280
341
|
expect(SCOUT_DISPATCHER_AGENT_ID).toBe("scout");
|
|
342
|
+
expect(OPENSCOUT_COORDINATOR_AGENT_ID).toBe("openscout");
|
|
281
343
|
expect(isReservedAgentDefinitionId("scout")).toBe(true);
|
|
282
344
|
expect(isReservedAgentDefinitionId("Scout")).toBe(true);
|
|
283
345
|
expect(isReservedAgentDefinitionId("SCOUT ")).toBe(true);
|
|
@@ -287,6 +349,31 @@ describe("scout dispatcher reservation", () => {
|
|
|
287
349
|
expect(isReservedAgentDefinitionId(null)).toBe(false);
|
|
288
350
|
expect(isReservedAgentDefinitionId(undefined)).toBe(false);
|
|
289
351
|
});
|
|
352
|
+
|
|
353
|
+
test("resolves @scout to the stable OpenScout coordinator", () => {
|
|
354
|
+
const identity = parseAgentIdentity("@scout");
|
|
355
|
+
expect(identity).not.toBeNull();
|
|
356
|
+
const candidates = [
|
|
357
|
+
{
|
|
358
|
+
agentId: "openscout.main.mini",
|
|
359
|
+
definitionId: "openscout",
|
|
360
|
+
nodeQualifier: "mini",
|
|
361
|
+
workspaceQualifier: "main",
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
agentId: "ranger.main.mini",
|
|
365
|
+
definitionId: "ranger",
|
|
366
|
+
nodeQualifier: "mini",
|
|
367
|
+
workspaceQualifier: "main",
|
|
368
|
+
},
|
|
369
|
+
];
|
|
370
|
+
const result = diagnoseAgentIdentity(identity!, candidates);
|
|
371
|
+
expect(result.kind).toBe("resolved");
|
|
372
|
+
if (result.kind === "resolved") {
|
|
373
|
+
expect(result.match.agentId).toBe("openscout.main.mini");
|
|
374
|
+
}
|
|
375
|
+
expect(formatMinimalAgentIdentity(candidates[0]!, candidates)).toBe("@scout");
|
|
376
|
+
});
|
|
290
377
|
});
|
|
291
378
|
|
|
292
379
|
describe("agent identity diagnosis", () => {
|
package/src/agent-identity.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ScoutId } from "./common.js";
|
|
2
2
|
|
|
3
|
-
export type AgentIdentityDimension = "workspace" | "profile" | "harness" | "node";
|
|
3
|
+
export type AgentIdentityDimension = "workspace" | "profile" | "harness" | "model" | "node";
|
|
4
4
|
|
|
5
5
|
export interface AgentIdentity {
|
|
6
6
|
raw: string;
|
|
@@ -10,6 +10,7 @@ export interface AgentIdentity {
|
|
|
10
10
|
workspaceQualifier?: string;
|
|
11
11
|
profile?: string;
|
|
12
12
|
harness?: string;
|
|
13
|
+
model?: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export interface AgentIdentityCandidate {
|
|
@@ -19,6 +20,7 @@ export interface AgentIdentityCandidate {
|
|
|
19
20
|
workspaceQualifier?: string;
|
|
20
21
|
profile?: string;
|
|
21
22
|
harness?: string;
|
|
23
|
+
model?: string;
|
|
22
24
|
aliases?: string[];
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -28,6 +30,7 @@ export interface AgentIdentityInput {
|
|
|
28
30
|
workspaceQualifier?: string;
|
|
29
31
|
profile?: string;
|
|
30
32
|
harness?: string;
|
|
33
|
+
model?: string;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
export interface AgentIdentityAlias {
|
|
@@ -43,6 +46,7 @@ const DIMENSION_ALIASES: Record<string, AgentIdentityDimension> = {
|
|
|
43
46
|
persona: "profile",
|
|
44
47
|
harness: "harness",
|
|
45
48
|
runtime: "harness",
|
|
49
|
+
model: "model",
|
|
46
50
|
node: "node",
|
|
47
51
|
host: "node",
|
|
48
52
|
};
|
|
@@ -66,6 +70,7 @@ export function normalizeAgentIdentitySegment(value: string): string {
|
|
|
66
70
|
export const normalizeAgentSelectorSegment = normalizeAgentIdentitySegment;
|
|
67
71
|
|
|
68
72
|
export const SCOUT_DISPATCHER_AGENT_ID = "scout";
|
|
73
|
+
export const OPENSCOUT_COORDINATOR_AGENT_ID = "openscout";
|
|
69
74
|
|
|
70
75
|
export const BUILT_IN_AGENT_DEFINITION_IDS: ReadonlySet<string> = new Set([
|
|
71
76
|
SCOUT_DISPATCHER_AGENT_ID,
|
|
@@ -99,6 +104,7 @@ function canonicalizeIdentity(input: AgentIdentityInput): AgentIdentityInput | n
|
|
|
99
104
|
: undefined;
|
|
100
105
|
const profile = input.profile ? normalizeAgentIdentitySegment(input.profile) : undefined;
|
|
101
106
|
const harness = input.harness ? normalizeAgentIdentitySegment(input.harness) : undefined;
|
|
107
|
+
const model = input.model ? normalizeAgentIdentitySegment(input.model) : undefined;
|
|
102
108
|
const nodeQualifier = input.nodeQualifier
|
|
103
109
|
? normalizeAgentIdentitySegment(input.nodeQualifier)
|
|
104
110
|
: undefined;
|
|
@@ -112,6 +118,7 @@ function canonicalizeIdentity(input: AgentIdentityInput): AgentIdentityInput | n
|
|
|
112
118
|
...(workspaceQualifier ? { workspaceQualifier } : {}),
|
|
113
119
|
...(profile ? { profile } : {}),
|
|
114
120
|
...(harness ? { harness } : {}),
|
|
121
|
+
...(model ? { model } : {}),
|
|
115
122
|
...(nodeQualifier ? { nodeQualifier } : {}),
|
|
116
123
|
};
|
|
117
124
|
}
|
|
@@ -144,6 +151,8 @@ function parseSegmentedIdentity(raw: string): AgentIdentity | null {
|
|
|
144
151
|
next.profile = value;
|
|
145
152
|
} else if (key === "harness") {
|
|
146
153
|
next.harness = value;
|
|
154
|
+
} else if (key === "model") {
|
|
155
|
+
next.model = value;
|
|
147
156
|
} else if (key === "node") {
|
|
148
157
|
next.nodeQualifier = value;
|
|
149
158
|
}
|
|
@@ -173,16 +182,70 @@ function parseSegmentedIdentity(raw: string): AgentIdentity | null {
|
|
|
173
182
|
return constructAgentIdentity(next, { raw });
|
|
174
183
|
}
|
|
175
184
|
|
|
185
|
+
function parseShorthandIdentity(raw: string): AgentIdentity | null {
|
|
186
|
+
const hashIndex = raw.indexOf("#");
|
|
187
|
+
const questionIndex = raw.indexOf("?");
|
|
188
|
+
if (hashIndex === -1 && questionIndex === -1) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
if (raw.indexOf("#", hashIndex + 1) !== -1 || raw.indexOf("?", questionIndex + 1) !== -1) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (hashIndex !== -1 && questionIndex !== -1 && questionIndex < hashIndex) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const baseEnd = Math.min(
|
|
199
|
+
...[hashIndex, questionIndex].filter((index) => index >= 0),
|
|
200
|
+
);
|
|
201
|
+
const base = raw.slice(0, baseEnd);
|
|
202
|
+
if (!base) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const parsedBase = parseSegmentedIdentity(base);
|
|
207
|
+
if (!parsedBase) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const harnessRaw = hashIndex >= 0
|
|
212
|
+
? raw.slice(hashIndex + 1, questionIndex >= 0 ? questionIndex : raw.length)
|
|
213
|
+
: "";
|
|
214
|
+
const modelRaw = questionIndex >= 0 ? raw.slice(questionIndex + 1) : "";
|
|
215
|
+
if ((hashIndex >= 0 && !harnessRaw) || (questionIndex >= 0 && !modelRaw)) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
if (harnessRaw.includes("#") || harnessRaw.includes("?") || modelRaw.includes("#") || modelRaw.includes("?")) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
if ((parsedBase.harness && harnessRaw) || (parsedBase.model && modelRaw)) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return constructAgentIdentity({
|
|
226
|
+
definitionId: parsedBase.definitionId,
|
|
227
|
+
nodeQualifier: parsedBase.nodeQualifier,
|
|
228
|
+
workspaceQualifier: parsedBase.workspaceQualifier,
|
|
229
|
+
profile: parsedBase.profile,
|
|
230
|
+
harness: harnessRaw || parsedBase.harness,
|
|
231
|
+
model: modelRaw || parsedBase.model,
|
|
232
|
+
}, { raw });
|
|
233
|
+
}
|
|
234
|
+
|
|
176
235
|
export function parseAgentIdentity(value: string): AgentIdentity | null {
|
|
177
236
|
const raw = trimIdentityPrefix(value);
|
|
178
237
|
if (!raw) {
|
|
179
238
|
return null;
|
|
180
239
|
}
|
|
181
240
|
|
|
182
|
-
if (raw.includes("@")
|
|
241
|
+
if (raw.includes("@")) {
|
|
183
242
|
return null;
|
|
184
243
|
}
|
|
185
244
|
|
|
245
|
+
if (raw.includes("#") || raw.includes("?")) {
|
|
246
|
+
return parseShorthandIdentity(raw);
|
|
247
|
+
}
|
|
248
|
+
|
|
186
249
|
return parseSegmentedIdentity(raw);
|
|
187
250
|
}
|
|
188
251
|
|
|
@@ -207,6 +270,9 @@ export function formatAgentIdentity(
|
|
|
207
270
|
if (canonical.harness) {
|
|
208
271
|
segments.push(`harness:${canonical.harness}`);
|
|
209
272
|
}
|
|
273
|
+
if (canonical.model) {
|
|
274
|
+
segments.push(`model:${canonical.model}`);
|
|
275
|
+
}
|
|
210
276
|
if (canonical.nodeQualifier) {
|
|
211
277
|
segments.push(`node:${canonical.nodeQualifier}`);
|
|
212
278
|
}
|
|
@@ -231,6 +297,7 @@ export function constructAgentIdentity(
|
|
|
231
297
|
...(canonical.workspaceQualifier ? { workspaceQualifier: canonical.workspaceQualifier } : {}),
|
|
232
298
|
...(canonical.profile ? { profile: canonical.profile } : {}),
|
|
233
299
|
...(canonical.harness ? { harness: canonical.harness } : {}),
|
|
300
|
+
...(canonical.model ? { model: canonical.model } : {}),
|
|
234
301
|
};
|
|
235
302
|
}
|
|
236
303
|
|
|
@@ -240,7 +307,7 @@ export function extractAgentIdentities(text: string): AgentIdentity[] {
|
|
|
240
307
|
|
|
241
308
|
export function extractAgentMentions(text: string): { parsed: AgentIdentity[]; unparsed: string[] } {
|
|
242
309
|
const matches = Array.from(
|
|
243
|
-
text.matchAll(/(
|
|
310
|
+
text.matchAll(/(^|[\s([{'"`])@([a-z0-9][a-z0-9._/:-]*(?:#[a-z0-9][a-z0-9._/:-]*)?(?:\?[a-z0-9][a-z0-9._/:-]*)?)(?=$|[\s)\]}",.!?:;'"`])/gi),
|
|
244
311
|
);
|
|
245
312
|
const identities = new Map<string, AgentIdentity>();
|
|
246
313
|
const unparsed: string[] = [];
|
|
@@ -269,6 +336,7 @@ function candidateAliases(candidate: AgentIdentityCandidate): string[] {
|
|
|
269
336
|
workspaceQualifier: candidate.workspaceQualifier,
|
|
270
337
|
profile: candidate.profile,
|
|
271
338
|
harness: candidate.harness,
|
|
339
|
+
model: candidate.model,
|
|
272
340
|
});
|
|
273
341
|
if (!canonical) {
|
|
274
342
|
return [];
|
|
@@ -286,6 +354,9 @@ function candidateAliases(candidate: AgentIdentityCandidate): string[] {
|
|
|
286
354
|
canonical.harness
|
|
287
355
|
? formatAgentIdentity({ definitionId: canonical.definitionId, harness: canonical.harness })
|
|
288
356
|
: "",
|
|
357
|
+
canonical.model
|
|
358
|
+
? formatAgentIdentity({ definitionId: canonical.definitionId, model: canonical.model })
|
|
359
|
+
: "",
|
|
289
360
|
canonical.nodeQualifier
|
|
290
361
|
? formatAgentIdentity({ definitionId: canonical.definitionId, nodeQualifier: canonical.nodeQualifier })
|
|
291
362
|
: "",
|
|
@@ -296,8 +367,12 @@ function candidateAliases(candidate: AgentIdentityCandidate): string[] {
|
|
|
296
367
|
}
|
|
297
368
|
|
|
298
369
|
function explicitCandidateAliases(candidate: AgentIdentityCandidate): string[] {
|
|
370
|
+
const canonical = candidateCanonicalIdentity(candidate);
|
|
371
|
+
const implicitAliases = canonical?.definitionId === OPENSCOUT_COORDINATOR_AGENT_ID
|
|
372
|
+
? [SCOUT_DISPATCHER_AGENT_ID]
|
|
373
|
+
: [];
|
|
299
374
|
return Array.from(new Set(
|
|
300
|
-
(candidate.aliases ?? [])
|
|
375
|
+
[...(candidate.aliases ?? []), ...implicitAliases]
|
|
301
376
|
.map((alias) => normalizeAgentIdentitySegment(trimIdentityPrefix(alias)))
|
|
302
377
|
.filter(Boolean),
|
|
303
378
|
));
|
|
@@ -327,6 +402,7 @@ function candidateCanonicalIdentity(candidate: AgentIdentityCandidate): AgentIde
|
|
|
327
402
|
workspaceQualifier: candidate.workspaceQualifier,
|
|
328
403
|
profile: candidate.profile,
|
|
329
404
|
harness: candidate.harness,
|
|
405
|
+
model: candidate.model,
|
|
330
406
|
});
|
|
331
407
|
}
|
|
332
408
|
|
|
@@ -342,9 +418,55 @@ function candidateDimensionValue(
|
|
|
342
418
|
if (dimension === "workspace") return canonical.workspaceQualifier;
|
|
343
419
|
if (dimension === "profile") return canonical.profile;
|
|
344
420
|
if (dimension === "harness") return canonical.harness;
|
|
421
|
+
if (dimension === "model") return canonical.model;
|
|
345
422
|
return canonical.nodeQualifier;
|
|
346
423
|
}
|
|
347
424
|
|
|
425
|
+
function modelAliasKeys(value: string | undefined): string[] {
|
|
426
|
+
const normalized = value ? normalizeAgentIdentitySegment(value) : "";
|
|
427
|
+
if (!normalized) {
|
|
428
|
+
return [];
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const parts = normalized.split("-").filter(Boolean);
|
|
432
|
+
const aliases = new Set<string>([normalized]);
|
|
433
|
+
if (parts.length > 1 && ["claude", "gpt", "openai", "anthropic"].includes(parts[0]!)) {
|
|
434
|
+
aliases.add(parts.slice(1).join("-"));
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
for (const family of ["sonnet", "opus", "haiku", "mini", "nano", "pro"]) {
|
|
438
|
+
const index = parts.indexOf(family);
|
|
439
|
+
if (index >= 0) {
|
|
440
|
+
aliases.add(family);
|
|
441
|
+
aliases.add(parts.slice(index).join("-"));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return Array.from(aliases).filter(Boolean);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function modelMatches(
|
|
449
|
+
identityModel: string | undefined,
|
|
450
|
+
candidate: AgentIdentityCandidate,
|
|
451
|
+
): boolean {
|
|
452
|
+
if (!identityModel) {
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
return modelAliasKeys(candidate.model).includes(identityModel);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function definitionMatchesIdentity(
|
|
459
|
+
identityDefinitionId: string,
|
|
460
|
+
candidateDefinitionId: string,
|
|
461
|
+
): boolean {
|
|
462
|
+
if (identityDefinitionId === candidateDefinitionId) {
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return identityDefinitionId === SCOUT_DISPATCHER_AGENT_ID
|
|
467
|
+
&& candidateDefinitionId === OPENSCOUT_COORDINATOR_AGENT_ID;
|
|
468
|
+
}
|
|
469
|
+
|
|
348
470
|
export function agentIdentityMatches(
|
|
349
471
|
identity: AgentIdentity,
|
|
350
472
|
candidate: AgentIdentityCandidate,
|
|
@@ -361,8 +483,9 @@ export function agentIdentityMatches(
|
|
|
361
483
|
workspaceQualifier: candidate.workspaceQualifier,
|
|
362
484
|
profile: candidate.profile,
|
|
363
485
|
harness: candidate.harness,
|
|
486
|
+
model: candidate.model,
|
|
364
487
|
});
|
|
365
|
-
if (!canonical || identity.definitionId
|
|
488
|
+
if (!canonical || !definitionMatchesIdentity(identity.definitionId, canonical.definitionId)) {
|
|
366
489
|
return false;
|
|
367
490
|
}
|
|
368
491
|
|
|
@@ -378,6 +501,9 @@ export function agentIdentityMatches(
|
|
|
378
501
|
if (identity.harness && identity.harness !== canonical.harness) {
|
|
379
502
|
return candidateAliases(candidate).includes(trimIdentityPrefix(identity.label));
|
|
380
503
|
}
|
|
504
|
+
if (identity.model && !modelMatches(identity.model, candidate)) {
|
|
505
|
+
return candidateAliases(candidate).includes(trimIdentityPrefix(identity.label));
|
|
506
|
+
}
|
|
381
507
|
|
|
382
508
|
return true;
|
|
383
509
|
}
|
|
@@ -419,7 +545,13 @@ export function diagnoseAgentIdentity<T extends AgentIdentityCandidate>(
|
|
|
419
545
|
return { kind: "unknown" };
|
|
420
546
|
}
|
|
421
547
|
|
|
422
|
-
if (
|
|
548
|
+
if (
|
|
549
|
+
!identity.nodeQualifier
|
|
550
|
+
&& !identity.workspaceQualifier
|
|
551
|
+
&& !identity.profile
|
|
552
|
+
&& !identity.harness
|
|
553
|
+
&& !identity.model
|
|
554
|
+
) {
|
|
423
555
|
const exactIdMatch = matches.find(
|
|
424
556
|
(candidate) => normalizeAgentIdentitySegment(candidate.agentId) === identity.definitionId,
|
|
425
557
|
);
|
|
@@ -491,6 +623,7 @@ function buildIdentitySubset(
|
|
|
491
623
|
: {}),
|
|
492
624
|
...(dimensions.includes("profile") && canonical.profile ? { profile: canonical.profile } : {}),
|
|
493
625
|
...(dimensions.includes("harness") && canonical.harness ? { harness: canonical.harness } : {}),
|
|
626
|
+
...(dimensions.includes("model") && canonical.model ? { model: canonical.model } : {}),
|
|
494
627
|
...(dimensions.includes("node") && canonical.nodeQualifier ? { nodeQualifier: canonical.nodeQualifier } : {}),
|
|
495
628
|
});
|
|
496
629
|
}
|
|
@@ -523,7 +656,7 @@ export function formatMinimalAgentIdentity<T extends AgentIdentityCandidate>(
|
|
|
523
656
|
return formatAgentIdentity({ definitionId: canonical.definitionId }, options);
|
|
524
657
|
}
|
|
525
658
|
|
|
526
|
-
const orderedDimensions: AgentIdentityDimension[] = ["workspace", "profile", "harness", "node"];
|
|
659
|
+
const orderedDimensions: AgentIdentityDimension[] = ["workspace", "profile", "harness", "model", "node"];
|
|
527
660
|
const selectedDimensions: AgentIdentityDimension[] = [];
|
|
528
661
|
let remainingPeers = peers.filter((peer) => peer !== candidate);
|
|
529
662
|
|
package/src/events.ts
CHANGED
|
@@ -33,6 +33,10 @@ export type AgentEndpointUpsertedEvent = ControlEventBase<"agent.endpoint.upsert
|
|
|
33
33
|
endpoint: AgentEndpoint;
|
|
34
34
|
}>;
|
|
35
35
|
|
|
36
|
+
export type AgentEndpointDeletedEvent = ControlEventBase<"agent.endpoint.deleted", {
|
|
37
|
+
endpointId: string;
|
|
38
|
+
}>;
|
|
39
|
+
|
|
36
40
|
export type ConversationUpsertedEvent = ControlEventBase<"conversation.upserted", {
|
|
37
41
|
conversation: ConversationDefinition;
|
|
38
42
|
}>;
|
|
@@ -83,6 +87,7 @@ export type ControlEvent =
|
|
|
83
87
|
| ActorRegisteredEvent
|
|
84
88
|
| AgentRegisteredEvent
|
|
85
89
|
| AgentEndpointUpsertedEvent
|
|
90
|
+
| AgentEndpointDeletedEvent
|
|
86
91
|
| ConversationUpsertedEvent
|
|
87
92
|
| BindingUpsertedEvent
|
|
88
93
|
| MessagePostedEvent
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./collaboration.js";
|
|
|
11
11
|
export * from "./messages.js";
|
|
12
12
|
export * from "./invocations.js";
|
|
13
13
|
export * from "./scout-dispatch.js";
|
|
14
|
+
export * from "./scout-delivery.js";
|
|
14
15
|
export * from "./deliveries.js";
|
|
15
16
|
export * from "./transports.js";
|
|
16
17
|
export * from "./events.js";
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ConversationDefinition } from "./conversations.js";
|
|
2
|
+
import type { MetadataMap, ScoutId } from "./common.js";
|
|
3
|
+
import type { FlightRecord, InvocationExecutionPreference } from "./invocations.js";
|
|
4
|
+
import type { MessageRecord } from "./messages.js";
|
|
5
|
+
import type {
|
|
6
|
+
ScoutCallerContext,
|
|
7
|
+
ScoutDispatchRecord,
|
|
8
|
+
ScoutRoutePolicy,
|
|
9
|
+
ScoutRouteTarget,
|
|
10
|
+
} from "./scout-dispatch.js";
|
|
11
|
+
|
|
12
|
+
export type ScoutDeliverIntent = "tell" | "consult";
|
|
13
|
+
|
|
14
|
+
export type ScoutDeliverRouteKind = "dm" | "channel" | "broadcast";
|
|
15
|
+
|
|
16
|
+
export type ScoutDeliveryRemediationKind =
|
|
17
|
+
| "choose_target"
|
|
18
|
+
| "register_target"
|
|
19
|
+
| "wake_target"
|
|
20
|
+
| "retry_later";
|
|
21
|
+
|
|
22
|
+
export type ScoutDeliverRejectReason =
|
|
23
|
+
| "unknown_target"
|
|
24
|
+
| "ambiguous_target"
|
|
25
|
+
| "invalid_target"
|
|
26
|
+
| "missing_target";
|
|
27
|
+
|
|
28
|
+
export interface ScoutDeliverRequest {
|
|
29
|
+
id?: ScoutId;
|
|
30
|
+
caller?: ScoutCallerContext;
|
|
31
|
+
requesterId?: ScoutId;
|
|
32
|
+
requesterNodeId?: ScoutId;
|
|
33
|
+
body: string;
|
|
34
|
+
intent: ScoutDeliverIntent;
|
|
35
|
+
target?: ScoutRouteTarget;
|
|
36
|
+
targetLabel?: string;
|
|
37
|
+
targetAgentId?: ScoutId;
|
|
38
|
+
routePolicy?: ScoutRoutePolicy;
|
|
39
|
+
channel?: string;
|
|
40
|
+
replyToMessageId?: ScoutId;
|
|
41
|
+
speechText?: string;
|
|
42
|
+
ensureAwake?: boolean;
|
|
43
|
+
execution?: InvocationExecutionPreference;
|
|
44
|
+
createdAt?: number;
|
|
45
|
+
collaborationRecordId?: ScoutId;
|
|
46
|
+
messageMetadata?: MetadataMap;
|
|
47
|
+
invocationMetadata?: MetadataMap;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface ScoutDeliveryReceipt {
|
|
51
|
+
requestId: ScoutId;
|
|
52
|
+
routeKind: ScoutDeliverRouteKind;
|
|
53
|
+
requesterId: ScoutId;
|
|
54
|
+
requesterNodeId: ScoutId;
|
|
55
|
+
targetAgentId?: ScoutId;
|
|
56
|
+
targetLabel?: string;
|
|
57
|
+
conversationId: ScoutId;
|
|
58
|
+
messageId: ScoutId;
|
|
59
|
+
flightId?: ScoutId;
|
|
60
|
+
acceptedAt: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ScoutDeliveryRemediationAction {
|
|
64
|
+
kind: ScoutDeliveryRemediationKind;
|
|
65
|
+
detail: string;
|
|
66
|
+
targetAgentId?: ScoutId;
|
|
67
|
+
targetLabel?: string;
|
|
68
|
+
dispatchId?: ScoutId;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ScoutDeliverAcceptedResponse {
|
|
72
|
+
kind: "delivery";
|
|
73
|
+
accepted: true;
|
|
74
|
+
routeKind: ScoutDeliverRouteKind;
|
|
75
|
+
receipt: ScoutDeliveryReceipt;
|
|
76
|
+
conversation: ConversationDefinition;
|
|
77
|
+
message: MessageRecord;
|
|
78
|
+
targetAgentId?: ScoutId;
|
|
79
|
+
flight?: FlightRecord;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface ScoutDeliverQuestionResponse {
|
|
83
|
+
kind: "question";
|
|
84
|
+
accepted: false;
|
|
85
|
+
question: ScoutDispatchRecord;
|
|
86
|
+
remediation?: ScoutDeliveryRemediationAction;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface ScoutDeliverRejectedResponse {
|
|
90
|
+
kind: "rejected";
|
|
91
|
+
accepted: false;
|
|
92
|
+
reason: ScoutDeliverRejectReason;
|
|
93
|
+
rejection: ScoutDispatchRecord;
|
|
94
|
+
remediation?: ScoutDeliveryRemediationAction;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type ScoutDeliverResponse =
|
|
98
|
+
| ScoutDeliverAcceptedResponse
|
|
99
|
+
| ScoutDeliverQuestionResponse
|
|
100
|
+
| ScoutDeliverRejectedResponse;
|
package/src/scout-dispatch.ts
CHANGED
|
@@ -1,12 +1,45 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { WakePolicy } from "./actors.js";
|
|
2
|
+
import type { AdvertiseScope, DeliveryTransport, MetadataMap, ScoutId } from "./common.js";
|
|
2
3
|
|
|
3
|
-
export type ScoutDispatchKind = "ambiguous" | "unknown" | "unparseable";
|
|
4
|
+
export type ScoutDispatchKind = "ambiguous" | "unknown" | "unparseable" | "unavailable";
|
|
4
5
|
|
|
5
6
|
export type ScoutCandidateEndpointState = "online" | "offline" | "unknown";
|
|
6
7
|
|
|
8
|
+
export type ScoutRouteTargetKind = "agent_id" | "agent_label" | "channel" | "broadcast";
|
|
9
|
+
|
|
10
|
+
export type ScoutRouteAmbiguousPolicy = "reject" | "ask";
|
|
11
|
+
|
|
12
|
+
export interface ScoutCallerContext {
|
|
13
|
+
actorId?: ScoutId;
|
|
14
|
+
nodeId?: ScoutId;
|
|
15
|
+
displayName?: string;
|
|
16
|
+
handle?: string;
|
|
17
|
+
currentDirectory?: string;
|
|
18
|
+
metadata?: MetadataMap;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type ScoutRouteTarget =
|
|
22
|
+
| { kind: "agent_id"; agentId: ScoutId; value?: string }
|
|
23
|
+
| { kind: "agent_label"; label: string; value?: string }
|
|
24
|
+
| { kind: "channel"; channel: string; value?: string }
|
|
25
|
+
| { kind: "broadcast"; value?: string };
|
|
26
|
+
|
|
27
|
+
export interface ScoutRoutePolicy {
|
|
28
|
+
preferLocalNodeId?: ScoutId;
|
|
29
|
+
ambiguous?: ScoutRouteAmbiguousPolicy;
|
|
30
|
+
allowStaleDirectId?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type ScoutDispatchUnavailableReason =
|
|
34
|
+
| "manual_wake_required"
|
|
35
|
+
| "retired"
|
|
36
|
+
| "stale_registration"
|
|
37
|
+
| "unknown";
|
|
38
|
+
|
|
7
39
|
export interface ScoutDispatchCandidate {
|
|
8
40
|
agentId: ScoutId;
|
|
9
41
|
displayName: string;
|
|
42
|
+
label: string;
|
|
10
43
|
authorityNodeId?: ScoutId;
|
|
11
44
|
homeNodeId?: ScoutId;
|
|
12
45
|
advertiseScope?: AdvertiseScope;
|
|
@@ -19,11 +52,23 @@ export interface ScoutDispatchCandidate {
|
|
|
19
52
|
transport?: DeliveryTransport | null;
|
|
20
53
|
}
|
|
21
54
|
|
|
55
|
+
export interface ScoutDispatchUnavailableTarget {
|
|
56
|
+
agentId: ScoutId;
|
|
57
|
+
displayName: string;
|
|
58
|
+
reason: ScoutDispatchUnavailableReason;
|
|
59
|
+
detail: string;
|
|
60
|
+
wakePolicy?: WakePolicy | null;
|
|
61
|
+
endpointState: ScoutCandidateEndpointState;
|
|
62
|
+
transport?: DeliveryTransport | null;
|
|
63
|
+
projectRoot?: string | null;
|
|
64
|
+
}
|
|
65
|
+
|
|
22
66
|
export interface ScoutDispatchEnvelope {
|
|
23
67
|
kind: ScoutDispatchKind;
|
|
24
68
|
askedLabel: string;
|
|
25
69
|
detail: string;
|
|
26
70
|
candidates: ScoutDispatchCandidate[];
|
|
71
|
+
target?: ScoutDispatchUnavailableTarget;
|
|
27
72
|
dispatchedAt: number;
|
|
28
73
|
dispatcherNodeId: ScoutId;
|
|
29
74
|
}
|