@openscout/protocol 0.2.64 → 0.2.82

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.
Files changed (214) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +103 -52
  3. package/dist/a2a.d.ts +224 -0
  4. package/dist/a2a.d.ts.map +1 -0
  5. package/dist/a2a.js +225 -0
  6. package/dist/activity.d.ts +70 -0
  7. package/dist/activity.d.ts.map +1 -0
  8. package/dist/activity.js +1 -0
  9. package/dist/actors.d.ts +7 -3
  10. package/dist/actors.d.ts.map +1 -0
  11. package/dist/actors.js +15 -1
  12. package/dist/agent-broker-feed.d.ts +84 -0
  13. package/dist/agent-broker-feed.d.ts.map +1 -0
  14. package/dist/agent-broker-feed.js +1 -0
  15. package/dist/agent-identity.d.ts +23 -0
  16. package/dist/agent-identity.d.ts.map +1 -0
  17. package/dist/agent-identity.js +122 -8
  18. package/dist/agent-runs.d.ts +191 -0
  19. package/dist/agent-runs.d.ts.map +1 -0
  20. package/dist/agent-runs.js +365 -0
  21. package/dist/assigned-roles.d.ts +205 -0
  22. package/dist/assigned-roles.d.ts.map +1 -0
  23. package/dist/assigned-roles.js +228 -0
  24. package/dist/broker-routes.d.ts +40 -0
  25. package/dist/broker-routes.d.ts.map +1 -0
  26. package/dist/broker-routes.js +56 -0
  27. package/dist/budget.d.ts +65 -0
  28. package/dist/budget.d.ts.map +1 -0
  29. package/dist/budget.js +19 -0
  30. package/dist/capability-matrix.d.ts +230 -0
  31. package/dist/capability-matrix.d.ts.map +1 -0
  32. package/dist/capability-matrix.js +304 -0
  33. package/dist/channel-identity.d.ts +40 -0
  34. package/dist/channel-identity.d.ts.map +1 -0
  35. package/dist/channel-identity.js +108 -0
  36. package/dist/collaboration.d.ts +45 -16
  37. package/dist/collaboration.d.ts.map +1 -0
  38. package/dist/collaboration.js +39 -23
  39. package/dist/common.d.ts +4 -3
  40. package/dist/common.d.ts.map +1 -0
  41. package/dist/conversations.d.ts +1 -0
  42. package/dist/conversations.d.ts.map +1 -0
  43. package/dist/cursor-transport.d.ts +22 -0
  44. package/dist/cursor-transport.d.ts.map +1 -0
  45. package/dist/cursor-transport.js +10 -0
  46. package/dist/debug-attach.d.ts +29 -0
  47. package/dist/debug-attach.d.ts.map +1 -0
  48. package/dist/debug-attach.js +6 -0
  49. package/dist/deliveries.d.ts +16 -2
  50. package/dist/deliveries.d.ts.map +1 -0
  51. package/dist/durable-actions.d.ts +94 -0
  52. package/dist/durable-actions.d.ts.map +1 -0
  53. package/dist/durable-actions.js +1 -0
  54. package/dist/events.d.ts +27 -1
  55. package/dist/events.d.ts.map +1 -0
  56. package/dist/events.js +14 -1
  57. package/dist/harness-feature-support.d.ts +76 -0
  58. package/dist/harness-feature-support.d.ts.map +1 -0
  59. package/dist/harness-feature-support.js +29 -0
  60. package/dist/host-integrations.d.ts +22 -0
  61. package/dist/host-integrations.d.ts.map +1 -0
  62. package/dist/host-integrations.js +64 -0
  63. package/dist/inbox.d.ts +50 -0
  64. package/dist/inbox.d.ts.map +1 -0
  65. package/dist/inbox.js +9 -0
  66. package/dist/index.d.ts +35 -2
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +34 -2
  69. package/dist/invocations.d.ts +119 -4
  70. package/dist/invocations.d.ts.map +1 -0
  71. package/dist/invocations.js +201 -1
  72. package/dist/mesh.d.ts +98 -0
  73. package/dist/mesh.d.ts.map +1 -0
  74. package/dist/mesh.js +18 -1
  75. package/dist/messages.d.ts +3 -0
  76. package/dist/messages.d.ts.map +1 -0
  77. package/dist/native-interactions.d.ts +30 -0
  78. package/dist/native-interactions.d.ts.map +1 -0
  79. package/dist/native-interactions.js +51 -0
  80. package/dist/observed-status.d.ts +33 -0
  81. package/dist/observed-status.d.ts.map +1 -0
  82. package/dist/observed-status.js +6 -0
  83. package/dist/orchestrator-prompt.d.ts +10 -0
  84. package/dist/orchestrator-prompt.d.ts.map +1 -0
  85. package/dist/orchestrator-prompt.js +36 -0
  86. package/dist/permission-policy.d.ts +5 -0
  87. package/dist/permission-policy.d.ts.map +1 -0
  88. package/dist/permission-policy.js +15 -0
  89. package/dist/provisional-agent-names.d.ts +30 -0
  90. package/dist/provisional-agent-names.d.ts.map +1 -0
  91. package/dist/provisional-agent-names.js +224 -0
  92. package/dist/read-receipts.d.ts +12 -0
  93. package/dist/read-receipts.d.ts.map +1 -0
  94. package/dist/read-receipts.js +1 -0
  95. package/dist/relay-agent-card.d.ts +10 -0
  96. package/dist/relay-agent-card.d.ts.map +1 -0
  97. package/dist/rendezvous.d.ts +97 -0
  98. package/dist/rendezvous.d.ts.map +1 -0
  99. package/dist/rendezvous.js +35 -0
  100. package/dist/reserved-vocabulary.d.ts +21 -0
  101. package/dist/reserved-vocabulary.d.ts.map +1 -0
  102. package/dist/reserved-vocabulary.js +101 -0
  103. package/dist/route-aliases.d.ts +112 -0
  104. package/dist/route-aliases.d.ts.map +1 -0
  105. package/dist/route-aliases.js +1 -0
  106. package/dist/runtime-catalog-contract.d.ts +39 -0
  107. package/dist/runtime-catalog-contract.d.ts.map +1 -0
  108. package/dist/runtime-catalog-contract.js +17 -0
  109. package/dist/runtime-catalog.d.ts +11 -0
  110. package/dist/runtime-catalog.d.ts.map +1 -0
  111. package/dist/runtime-catalog.generated.d.ts +230 -0
  112. package/dist/runtime-catalog.generated.d.ts.map +1 -0
  113. package/dist/runtime-catalog.generated.js +299 -0
  114. package/dist/runtime-catalog.js +192 -0
  115. package/dist/runtime-execution.d.ts +161 -0
  116. package/dist/runtime-execution.d.ts.map +1 -0
  117. package/dist/runtime-execution.js +327 -0
  118. package/dist/runtime-profiles.d.ts +12 -0
  119. package/dist/runtime-profiles.d.ts.map +1 -0
  120. package/dist/runtime-profiles.js +20 -0
  121. package/dist/scout-agent-card.d.ts +10 -0
  122. package/dist/scout-agent-card.d.ts.map +1 -0
  123. package/dist/scout-composer.d.ts +27 -0
  124. package/dist/scout-composer.d.ts.map +1 -0
  125. package/dist/scout-composer.js +192 -0
  126. package/dist/scout-delivery.d.ts +99 -4
  127. package/dist/scout-delivery.d.ts.map +1 -0
  128. package/dist/scout-dispatch.d.ts +40 -3
  129. package/dist/scout-dispatch.d.ts.map +1 -0
  130. package/dist/scout-reply-context.d.ts +1 -0
  131. package/dist/scout-reply-context.d.ts.map +1 -0
  132. package/dist/session-display-state.d.ts +132 -0
  133. package/dist/session-display-state.d.ts.map +1 -0
  134. package/dist/session-display-state.js +94 -0
  135. package/dist/terminal-host-topology.d.ts +119 -0
  136. package/dist/terminal-host-topology.d.ts.map +1 -0
  137. package/dist/terminal-host-topology.js +21 -0
  138. package/dist/terminal-sessions.d.ts +142 -0
  139. package/dist/terminal-sessions.d.ts.map +1 -0
  140. package/dist/terminal-sessions.js +81 -0
  141. package/dist/terminal-surface-id.d.ts +73 -0
  142. package/dist/terminal-surface-id.d.ts.map +1 -0
  143. package/dist/terminal-surface-id.js +162 -0
  144. package/dist/terminal-workspaces.d.ts +199 -0
  145. package/dist/terminal-workspaces.d.ts.map +1 -0
  146. package/dist/terminal-workspaces.js +287 -0
  147. package/dist/thread-events.d.ts +1 -0
  148. package/dist/thread-events.d.ts.map +1 -0
  149. package/dist/time.d.ts +16 -0
  150. package/dist/time.d.ts.map +1 -0
  151. package/dist/time.js +33 -0
  152. package/dist/transports.d.ts +1 -0
  153. package/dist/transports.d.ts.map +1 -0
  154. package/package.json +37 -9
  155. package/src/a2a.ts +471 -0
  156. package/src/activity.ts +113 -0
  157. package/src/actors.ts +42 -8
  158. package/src/agent-broker-feed.ts +102 -0
  159. package/src/agent-identity.ts +143 -8
  160. package/src/agent-runs.ts +675 -0
  161. package/src/assigned-roles.ts +437 -0
  162. package/src/broker-routes.ts +62 -0
  163. package/src/budget.ts +88 -0
  164. package/src/capability-matrix.ts +648 -0
  165. package/src/channel-identity.ts +147 -0
  166. package/src/collaboration.ts +78 -48
  167. package/src/common.ts +24 -5
  168. package/src/cursor-transport.ts +34 -0
  169. package/src/debug-attach.ts +38 -0
  170. package/src/deliveries.ts +16 -1
  171. package/src/durable-actions.ts +125 -0
  172. package/src/events.ts +31 -0
  173. package/src/harness-feature-support.ts +116 -0
  174. package/src/host-integrations.ts +104 -0
  175. package/src/inbox.ts +68 -0
  176. package/src/index.ts +34 -2
  177. package/src/invocations.ts +351 -4
  178. package/src/mesh.ts +130 -0
  179. package/src/messages.ts +2 -0
  180. package/src/native-interactions.ts +100 -0
  181. package/src/observed-status.ts +90 -0
  182. package/src/orchestrator-prompt.ts +47 -0
  183. package/src/permission-policy.ts +21 -0
  184. package/src/provisional-agent-names.ts +272 -0
  185. package/src/read-receipts.ts +12 -0
  186. package/src/relay-agent-card.ts +73 -63
  187. package/src/rendezvous.ts +154 -0
  188. package/src/reserved-vocabulary.ts +135 -0
  189. package/src/route-aliases.ts +130 -0
  190. package/src/runtime-catalog-contract.ts +60 -0
  191. package/src/runtime-catalog.generated.ts +300 -0
  192. package/src/runtime-catalog.md +21 -0
  193. package/src/runtime-catalog.ts +193 -0
  194. package/src/runtime-catalog.v1.json +81 -0
  195. package/src/runtime-execution.ts +545 -0
  196. package/src/runtime-profiles.ts +37 -0
  197. package/src/scout-agent-card.ts +10 -0
  198. package/src/scout-composer.ts +249 -0
  199. package/src/scout-delivery.ts +113 -3
  200. package/src/scout-dispatch.ts +33 -2
  201. package/src/session-display-state.ts +202 -0
  202. package/src/terminal-host-topology.ts +132 -0
  203. package/src/terminal-sessions.ts +187 -0
  204. package/src/terminal-surface-id.ts +189 -0
  205. package/src/terminal-workspaces.ts +455 -0
  206. package/src/time.ts +49 -0
  207. package/dist/agent-address.d.ts +0 -1
  208. package/dist/agent-address.js +0 -1
  209. package/dist/agent-selectors.d.ts +0 -1
  210. package/dist/agent-selectors.js +0 -1
  211. package/src/agent-address.ts +0 -1
  212. package/src/agent-identity.test.ts +0 -435
  213. package/src/agent-selectors.ts +0 -1
  214. package/src/scout-agent-card.test.ts +0 -54
@@ -0,0 +1,437 @@
1
+ /**
2
+ * Assigned roles: small explicit duties with hooks and allowed actions.
3
+ *
4
+ * Not agentClass, harness, or identity. No assignment → no hooks → no mission log.
5
+ * First role: orchestrator (mission spine + cheap mission log).
6
+ *
7
+ * See docs/proposals/assigned-roles-and-mission-log.md
8
+ */
9
+
10
+ import type { MetadataMap, ScoutId } from "./common.js";
11
+
12
+ /* ── Hook moments ── */
13
+
14
+ export const SCOUT_ROLE_HOOK_IDS = [
15
+ "mission.started",
16
+ "turn.ended",
17
+ "delegation.created",
18
+ "child.updated",
19
+ "waiting.entered",
20
+ "mission.heartbeat",
21
+ "mission.finished",
22
+ ] as const;
23
+
24
+ export type ScoutRoleHookId = (typeof SCOUT_ROLE_HOOK_IDS)[number];
25
+
26
+ /**
27
+ * Scout-side lifecycle moments (broker fires these; role definitions bind actions).
28
+ * Distinct from optional soft "hooks" the agent may also honor.
29
+ */
30
+ export const SCOUT_ROLE_LIFECYCLE_EVENT_IDS = [
31
+ "ask.completed",
32
+ "ask.failed",
33
+ "work.completed",
34
+ ] as const;
35
+
36
+ export type ScoutRoleLifecycleEventId = (typeof SCOUT_ROLE_LIFECYCLE_EVENT_IDS)[number];
37
+
38
+ /** Who must hold the role for a lifecycle binding to run. */
39
+ export type ScoutRoleLifecycleWhen = "target" | "requester" | "either";
40
+
41
+ /* ── Actions (v0 string ids; expand as tools land) ── */
42
+
43
+ export const SCOUT_MISSION_LOG_APPEND = "mission_log.append" as const;
44
+ export const SCOUT_WORK_CHILD_LINK = "work.child_link" as const;
45
+ export const SCOUT_WORK_SET_NEXT_MOVE = "work.set_next_move" as const;
46
+
47
+ export const SCOUT_ROLE_ACTION_IDS = [
48
+ SCOUT_MISSION_LOG_APPEND,
49
+ SCOUT_WORK_CHILD_LINK,
50
+ SCOUT_WORK_SET_NEXT_MOVE,
51
+ ] as const;
52
+
53
+ /** Closed set for catalog authoring; open only at untrusted boundaries if needed. */
54
+ export type ScoutRoleActionId = (typeof SCOUT_ROLE_ACTION_IDS)[number];
55
+
56
+ /**
57
+ * Role-owned function at a lifecycle moment.
58
+ * Example: orchestrator → on ask.completed → mission_log.append (post_ask_summary).
59
+ */
60
+ export interface ScoutRoleLifecycleBinding {
61
+ on: ScoutRoleLifecycleEventId;
62
+ action: ScoutRoleActionId;
63
+ when: ScoutRoleLifecycleWhen;
64
+ /**
65
+ * Stable function id for logs/telemetry (role-defined name of the behavior).
66
+ * e.g. post_ask_summary, post_work_summary
67
+ */
68
+ functionId: string;
69
+ }
70
+
71
+ /* ── Catalog ── */
72
+
73
+ export const SCOUT_ORCHESTRATOR_ROLE_ID = "orchestrator" as const;
74
+
75
+ /** Built-in role ids. Future roles append here and stay closed in the catalog. */
76
+ export type ScoutBuiltinRoleId = typeof SCOUT_ORCHESTRATOR_ROLE_ID;
77
+
78
+ /** Role id on definitions/assignments; catalog rows use built-ins. */
79
+ export type ScoutRoleId = ScoutBuiltinRoleId | (string & {});
80
+
81
+ export interface ScoutRoleDefinition {
82
+ id: ScoutRoleId;
83
+ label: string;
84
+ summary: string;
85
+ hooks: readonly ScoutRoleHookId[];
86
+ actions: readonly ScoutRoleActionId[];
87
+ /**
88
+ * Scout-enforced lifecycle functions for this role.
89
+ * Fired by the broker at ask/work boundaries — not agent-loaded hooks.
90
+ */
91
+ lifecycle?: readonly ScoutRoleLifecycleBinding[];
92
+ }
93
+
94
+ /** Orchestrator lifecycle: auto post-ask / post-work summary into mission log. */
95
+ export const SCOUT_ORCHESTRATOR_LIFECYCLE: readonly ScoutRoleLifecycleBinding[] = [
96
+ {
97
+ on: "ask.completed",
98
+ action: SCOUT_MISSION_LOG_APPEND,
99
+ when: "either",
100
+ functionId: "post_ask_summary",
101
+ },
102
+ {
103
+ on: "ask.failed",
104
+ action: SCOUT_MISSION_LOG_APPEND,
105
+ when: "either",
106
+ functionId: "post_ask_summary",
107
+ },
108
+ {
109
+ on: "work.completed",
110
+ action: SCOUT_MISSION_LOG_APPEND,
111
+ when: "target",
112
+ functionId: "post_work_summary",
113
+ },
114
+ ] as const;
115
+
116
+ export const SCOUT_ORCHESTRATOR_ROLE: ScoutRoleDefinition = {
117
+ id: SCOUT_ORCHESTRATOR_ROLE_ID,
118
+ label: "Orchestrator",
119
+ summary:
120
+ "Owns a long-running mission spine and keeps a cheap mission log current.",
121
+ hooks: SCOUT_ROLE_HOOK_IDS,
122
+ actions: [
123
+ SCOUT_MISSION_LOG_APPEND,
124
+ SCOUT_WORK_CHILD_LINK,
125
+ SCOUT_WORK_SET_NEXT_MOVE,
126
+ ],
127
+ lifecycle: SCOUT_ORCHESTRATOR_LIFECYCLE,
128
+ };
129
+
130
+ /** Built-in catalog. Future roles (qa, sre, reviewer) append here. */
131
+ export const SCOUT_ROLE_CATALOG: readonly ScoutRoleDefinition[] = [
132
+ SCOUT_ORCHESTRATOR_ROLE,
133
+ ] as const;
134
+
135
+ /* ── Assignment scope ── */
136
+
137
+ /**
138
+ * Mission scope uses a work-item id in v0 (campaign root).
139
+ * Agent scope is a standing duty on that agent.
140
+ * Project scope is reserved for Slice 1+ (requires projectRoot in context).
141
+ */
142
+ export type ScoutRoleAssignmentScope =
143
+ | { kind: "mission"; missionId: ScoutId }
144
+ | { kind: "agent" }
145
+ | { kind: "project"; projectRoot: string };
146
+
147
+ export interface ScoutRoleAssignment {
148
+ id: ScoutId;
149
+ roleId: ScoutRoleId;
150
+ agentId: ScoutId;
151
+ scope: ScoutRoleAssignmentScope;
152
+ assignedById: ScoutId;
153
+ assignedAt: number;
154
+ active: boolean;
155
+ revokedAt?: number;
156
+ revokedById?: ScoutId;
157
+ metadata?: MetadataMap;
158
+ }
159
+
160
+ /** Context used to decide whether an assignment applies. */
161
+ export interface ScoutRoleScopeContext {
162
+ missionId?: ScoutId;
163
+ projectRoot?: string;
164
+ }
165
+
166
+ /* ── Mission log (situation plane; not chat) ── */
167
+
168
+ export const SCOUT_MISSION_LOG_KINDS = [
169
+ "heartbeat",
170
+ "progress",
171
+ "delegation",
172
+ "waiting",
173
+ "decision",
174
+ "risk",
175
+ "integration",
176
+ "done",
177
+ "failed",
178
+ ] as const;
179
+
180
+ export type ScoutMissionLogKind = (typeof SCOUT_MISSION_LOG_KINDS)[number];
181
+
182
+ /** Soft limits for writers, tool schemas, and prompts — single source of truth. */
183
+ export const SCOUT_MISSION_LOG_LIMITS = {
184
+ intentMaxWords: 16,
185
+ /** Short current-action line (field name: `status`, not `now`). */
186
+ statusMaxWords: 24,
187
+ noteMaxChars: 240,
188
+ } as const;
189
+
190
+ export interface ScoutMissionLogBlocker {
191
+ label: string;
192
+ ownerId?: ScoutId;
193
+ metadata?: MetadataMap;
194
+ }
195
+
196
+ export interface ScoutMissionLogRefs {
197
+ messageId?: ScoutId;
198
+ flightId?: ScoutId;
199
+ workId?: ScoutId;
200
+ sessionId?: ScoutId;
201
+ invocationId?: ScoutId;
202
+ }
203
+
204
+ /**
205
+ * Cheap structured entry written by an assigned orchestrator (or other role
206
+ * that lists mission_log.append). Prefer short intent/status; optional note max
207
+ * one sentence when truly useful.
208
+ *
209
+ * v0 persistence may map only a subset into work.progress + collaboration_events
210
+ * (lossy). Full fidelity wants an append-only log store in Slice 2+.
211
+ */
212
+ export interface ScoutMissionLogEntry {
213
+ id: ScoutId;
214
+ /** Work-item id of the campaign root (v0). */
215
+ missionId: ScoutId;
216
+ /** Child work / flight node when the entry is about a branch. */
217
+ nodeId?: ScoutId;
218
+ at: number;
219
+ /** Per-mission monotonic order; avoids same-ms collisions in UI. */
220
+ seq?: number;
221
+ actorId: ScoutId;
222
+ kind: ScoutMissionLogKind;
223
+ /** Stable goal for this node (keep short). */
224
+ intent: string;
225
+ /** Current action / state (keep short). Not a timestamp. */
226
+ status: string;
227
+ checkpoint?: string;
228
+ blockers?: ScoutMissionLogBlocker[];
229
+ refs?: ScoutMissionLogRefs;
230
+ note?: string;
231
+ metadata?: MetadataMap;
232
+ }
233
+
234
+ export interface ScoutMissionLogAppendInput {
235
+ missionId: ScoutId;
236
+ nodeId?: ScoutId;
237
+ actorId: ScoutId;
238
+ kind: ScoutMissionLogKind;
239
+ intent: string;
240
+ status: string;
241
+ checkpoint?: string;
242
+ blockers?: ScoutMissionLogBlocker[];
243
+ refs?: ScoutMissionLogRefs;
244
+ note?: string;
245
+ metadata?: MetadataMap;
246
+ /** Caller-supplied id; store may assign if omitted. */
247
+ id?: ScoutId;
248
+ at?: number;
249
+ seq?: number;
250
+ }
251
+
252
+ /* ── Helpers ── */
253
+
254
+ export function scoutRoleDefinition(
255
+ roleId: string,
256
+ catalog: readonly ScoutRoleDefinition[] = SCOUT_ROLE_CATALOG,
257
+ ): ScoutRoleDefinition | undefined {
258
+ return catalog.find((role) => role.id === roleId);
259
+ }
260
+
261
+ export function isScoutRoleHookId(value: string): value is ScoutRoleHookId {
262
+ return (SCOUT_ROLE_HOOK_IDS as readonly string[]).includes(value);
263
+ }
264
+
265
+ export function isScoutMissionLogKind(value: string): value is ScoutMissionLogKind {
266
+ return (SCOUT_MISSION_LOG_KINDS as readonly string[]).includes(value);
267
+ }
268
+
269
+ export function assignmentAppliesTo(
270
+ assignment: ScoutRoleAssignment,
271
+ ctx: ScoutRoleScopeContext,
272
+ ): boolean {
273
+ switch (assignment.scope.kind) {
274
+ case "agent":
275
+ return true;
276
+ case "mission":
277
+ return ctx.missionId !== undefined
278
+ && assignment.scope.missionId === ctx.missionId;
279
+ case "project":
280
+ return ctx.projectRoot !== undefined
281
+ && assignment.scope.projectRoot === ctx.projectRoot;
282
+ default:
283
+ return false;
284
+ }
285
+ }
286
+
287
+ export function activeScoutRoleAssignments(
288
+ assignments: readonly ScoutRoleAssignment[],
289
+ ): ScoutRoleAssignment[] {
290
+ return assignments.filter((assignment) => assignment.active);
291
+ }
292
+
293
+ export function scoutRoleAssignmentsForAgent(
294
+ assignments: readonly ScoutRoleAssignment[],
295
+ agentId: string,
296
+ opts?: { activeOnly?: boolean },
297
+ ): ScoutRoleAssignment[] {
298
+ const activeOnly = opts?.activeOnly ?? true;
299
+ return assignments.filter((assignment) => {
300
+ if (assignment.agentId !== agentId) return false;
301
+ if (activeOnly && !assignment.active) return false;
302
+ return true;
303
+ });
304
+ }
305
+
306
+ export function scoutRoleAssignmentsForMission(
307
+ assignments: readonly ScoutRoleAssignment[],
308
+ missionId: string,
309
+ opts?: {
310
+ activeOnly?: boolean;
311
+ roleId?: string;
312
+ /** Include standing agent-scope assignments (default true for "who covers this mission"). */
313
+ includeStanding?: boolean;
314
+ },
315
+ ): ScoutRoleAssignment[] {
316
+ const activeOnly = opts?.activeOnly ?? true;
317
+ const includeStanding = opts?.includeStanding ?? true;
318
+ const ctx: ScoutRoleScopeContext = { missionId };
319
+
320
+ return assignments.filter((assignment) => {
321
+ if (activeOnly && !assignment.active) return false;
322
+ if (opts?.roleId && assignment.roleId !== opts.roleId) return false;
323
+ if (assignment.scope.kind === "agent") return includeStanding;
324
+ return assignmentAppliesTo(assignment, ctx);
325
+ });
326
+ }
327
+
328
+ /** Distinct agent ids holding an active role for a mission (incl. standing if enabled). */
329
+ export function activeRoleHoldersForMission(
330
+ assignments: readonly ScoutRoleAssignment[],
331
+ missionId: string,
332
+ roleId: string,
333
+ opts?: { includeStanding?: boolean },
334
+ ): ScoutId[] {
335
+ const rows = scoutRoleAssignmentsForMission(assignments, missionId, {
336
+ activeOnly: true,
337
+ roleId,
338
+ includeStanding: opts?.includeStanding ?? true,
339
+ });
340
+ return [...new Set(rows.map((row) => row.agentId))];
341
+ }
342
+
343
+ /**
344
+ * True when this agent may write the mission log for a mission under the
345
+ * anti-spam rules: active assignment whose definition includes mission_log.append
346
+ * and whose scope applies to the mission (or standing agent-scope).
347
+ */
348
+ export function agentMayWriteMissionLog(opts: {
349
+ agentId: string;
350
+ missionId: string;
351
+ assignments: readonly ScoutRoleAssignment[];
352
+ catalog?: readonly ScoutRoleDefinition[];
353
+ projectRoot?: string;
354
+ }): boolean {
355
+ const catalog = opts.catalog ?? SCOUT_ROLE_CATALOG;
356
+ const ctx: ScoutRoleScopeContext = {
357
+ missionId: opts.missionId,
358
+ projectRoot: opts.projectRoot,
359
+ };
360
+
361
+ for (const assignment of opts.assignments) {
362
+ if (!assignment.active) continue;
363
+ if (assignment.agentId !== opts.agentId) continue;
364
+ if (!assignmentAppliesTo(assignment, ctx)) continue;
365
+ const definition = scoutRoleDefinition(assignment.roleId, catalog);
366
+ if (!definition?.actions.includes(SCOUT_MISSION_LOG_APPEND)) continue;
367
+ return true;
368
+ }
369
+ return false;
370
+ }
371
+
372
+ export function roleDefinitionAllowsHook(
373
+ definition: ScoutRoleDefinition,
374
+ hookId: ScoutRoleHookId,
375
+ ): boolean {
376
+ return definition.hooks.includes(hookId);
377
+ }
378
+
379
+ export function assignmentsMatchingHook(opts: {
380
+ hookId: ScoutRoleHookId;
381
+ assignments: readonly ScoutRoleAssignment[];
382
+ context?: ScoutRoleScopeContext;
383
+ catalog?: readonly ScoutRoleDefinition[];
384
+ }): ScoutRoleAssignment[] {
385
+ const catalog = opts.catalog ?? SCOUT_ROLE_CATALOG;
386
+ const ctx = opts.context ?? {};
387
+
388
+ return opts.assignments.filter((assignment) => {
389
+ if (!assignment.active) return false;
390
+ if (!assignmentAppliesTo(assignment, ctx)) return false;
391
+ const definition = scoutRoleDefinition(assignment.roleId, catalog);
392
+ if (!definition || !roleDefinitionAllowsHook(definition, opts.hookId)) {
393
+ return false;
394
+ }
395
+ return true;
396
+ });
397
+ }
398
+
399
+ export type MissionLogEntryValidation =
400
+ | { ok: true }
401
+ | { ok: false; errors: string[] };
402
+
403
+ function wordCount(value: string): number {
404
+ return value.trim().split(/\s+/u).filter(Boolean).length;
405
+ }
406
+
407
+ /** Soft structural validation for writers and tests (untrusted tool args). */
408
+ export function validateMissionLogEntryFields(
409
+ input: Pick<ScoutMissionLogAppendInput, "intent" | "status" | "note" | "kind">,
410
+ ): MissionLogEntryValidation {
411
+ const errors: string[] = [];
412
+ if (typeof input.intent !== "string" || !input.intent.trim()) {
413
+ errors.push("intent is required");
414
+ } else if (wordCount(input.intent) > SCOUT_MISSION_LOG_LIMITS.intentMaxWords) {
415
+ errors.push(`intent should be ≤ ${SCOUT_MISSION_LOG_LIMITS.intentMaxWords} words`);
416
+ }
417
+
418
+ if (typeof input.status !== "string" || !input.status.trim()) {
419
+ errors.push("status is required");
420
+ } else if (wordCount(input.status) > SCOUT_MISSION_LOG_LIMITS.statusMaxWords) {
421
+ errors.push(`status should be ≤ ${SCOUT_MISSION_LOG_LIMITS.statusMaxWords} words`);
422
+ }
423
+
424
+ if (typeof input.kind !== "string" || !isScoutMissionLogKind(input.kind)) {
425
+ errors.push(`unknown kind: ${String(input.kind)}`);
426
+ }
427
+
428
+ if (input.note !== undefined && input.note !== null) {
429
+ if (typeof input.note !== "string") {
430
+ errors.push("note must be a string");
431
+ } else if (input.note.trim().length > SCOUT_MISSION_LOG_LIMITS.noteMaxChars) {
432
+ errors.push(`note should be ≤ ${SCOUT_MISSION_LOG_LIMITS.noteMaxChars} characters`);
433
+ }
434
+ }
435
+
436
+ return errors.length === 0 ? { ok: true } : { ok: false, errors };
437
+ }
@@ -0,0 +1,62 @@
1
+ // Canonical client-side paths for the broker HTTP API. The routes defined here
2
+ // are served by the broker runtime; a route-inventory test ensures every entry
3
+ // in this map matches a live implementation.
4
+
5
+ export const scoutBrokerPaths = {
6
+ health: "/health",
7
+ v1: {
8
+ home: "/v1/home",
9
+ node: "/v1/node",
10
+ snapshot: "/v1/snapshot",
11
+ capabilities: "/v1/capabilities",
12
+ runtimeCatalog: "/v1/runtime-catalog",
13
+ topologySnapshot: "/v1/topology/snapshot",
14
+ tailDiscover: "/v1/tail/discover",
15
+ tailRecent: "/v1/tail/recent",
16
+ repoWatchSnapshot: "/v1/repo-watch/snapshot",
17
+ messages: "/v1/messages",
18
+ brokerMessages: "/v1/broker/messages",
19
+ eventsStream: "/v1/events/stream",
20
+ actors: "/v1/actors",
21
+ agents: "/v1/agents",
22
+ endpoints: "/v1/endpoints",
23
+ conversations: "/v1/conversations",
24
+ invocations: "/v1/invocations",
25
+ flights: "/v1/flights",
26
+ deliver: "/v1/deliver",
27
+ rendezvousMatch: "/v1/rendezvous/match",
28
+ aliases: "/v1/aliases",
29
+ aliasesResolve: "/v1/aliases/resolve",
30
+ activity: "/v1/activity",
31
+ collaborationRecords: "/v1/collaboration/records",
32
+ collaborationEvents: "/v1/collaboration/events",
33
+ pairingAttach: "/v1/pairing/attach",
34
+ pairingDetach: "/v1/pairing/detach",
35
+ localSessionsAttach: "/v1/local-sessions/attach",
36
+ localSessionsDetach: "/v1/local-sessions/detach",
37
+ },
38
+ } as const;
39
+
40
+ export function scoutBrokerMessagesListPath(search: URLSearchParams): string {
41
+ const q = search.toString();
42
+ return q ? `${scoutBrokerPaths.v1.messages}?${q}` : scoutBrokerPaths.v1.messages;
43
+ }
44
+
45
+ export function scoutBrokerMessagesPath(search: URLSearchParams): string {
46
+ const q = search.toString();
47
+ return q
48
+ ? `${scoutBrokerPaths.v1.brokerMessages}?${q}`
49
+ : scoutBrokerPaths.v1.brokerMessages;
50
+ }
51
+
52
+ export function scoutBrokerInvocationPath(invocationId: string): string {
53
+ return `${scoutBrokerPaths.v1.invocations}/${encodeURIComponent(invocationId)}`;
54
+ }
55
+
56
+ export function scoutBrokerInvocationStreamPath(invocationId: string): string {
57
+ return `${scoutBrokerInvocationPath(invocationId)}/stream`;
58
+ }
59
+
60
+ export function scoutBrokerInvocationLifecyclePath(invocationId: string): string {
61
+ return `${scoutBrokerInvocationPath(invocationId)}/lifecycle`;
62
+ }
package/src/budget.ts ADDED
@@ -0,0 +1,88 @@
1
+ import type { AgentHarness } from "./actors.js";
2
+ import type { DeliveryTransport, MetadataMap, ScoutId } from "./common.js";
3
+
4
+ export const BUDGET_USAGE_SCOPES = [
5
+ "protocol_overhead",
6
+ "harness_execution",
7
+ ] as const;
8
+
9
+ export type BudgetUsageScope = typeof BUDGET_USAGE_SCOPES[number];
10
+
11
+ export const BUDGET_USAGE_SOURCES = [
12
+ "provider_exact",
13
+ "provider_reported",
14
+ "provider_session_snapshot",
15
+ "observed_transcript",
16
+ "tokenizer_estimate",
17
+ "char_heuristic",
18
+ "manual_estimate",
19
+ ] as const;
20
+
21
+ export type BudgetUsageSource = typeof BUDGET_USAGE_SOURCES[number];
22
+
23
+ export interface BudgetUsageRecord {
24
+ id: ScoutId;
25
+ scope: BudgetUsageScope;
26
+ source: BudgetUsageSource;
27
+ provider?: string;
28
+ harness?: AgentHarness | string;
29
+ transport?: DeliveryTransport | string;
30
+ model?: string;
31
+ agentId?: ScoutId;
32
+ endpointId?: ScoutId;
33
+ sessionId?: ScoutId;
34
+ projectRoot?: string;
35
+ conversationId?: ScoutId;
36
+ messageId?: ScoutId;
37
+ invocationId?: ScoutId;
38
+ flightId?: ScoutId;
39
+ workId?: ScoutId;
40
+ occurredAt: number;
41
+ inputTokens?: number;
42
+ outputTokens?: number;
43
+ reasoningOutputTokens?: number;
44
+ cacheCreationInputTokens?: number;
45
+ cacheReadInputTokens?: number;
46
+ totalTokens?: number;
47
+ estimatedUsd?: number;
48
+ billedUsd?: number;
49
+ currency?: string;
50
+ dedupKey?: string;
51
+ metadata?: MetadataMap;
52
+ createdAt?: number;
53
+ }
54
+
55
+ export const BUDGET_QUOTA_WINDOW_SOURCES = [
56
+ "provider_reported",
57
+ "observed",
58
+ "estimated",
59
+ "manual",
60
+ ] as const;
61
+
62
+ export type BudgetQuotaWindowSource = typeof BUDGET_QUOTA_WINDOW_SOURCES[number];
63
+
64
+ export interface BudgetQuotaWindowSnapshot {
65
+ id: ScoutId;
66
+ source: BudgetQuotaWindowSource;
67
+ provider?: string;
68
+ harness?: AgentHarness | string;
69
+ transport?: DeliveryTransport | string;
70
+ model?: string;
71
+ agentId?: ScoutId;
72
+ endpointId?: ScoutId;
73
+ sessionId?: ScoutId;
74
+ userId?: string;
75
+ accountId?: string;
76
+ planType?: string;
77
+ label: "5h" | "weekly" | string;
78
+ windowKind?: "primary" | "secondary" | "five_hour" | "weekly" | string;
79
+ usedPercent?: number;
80
+ percentRemaining?: number;
81
+ used?: number;
82
+ limit?: number;
83
+ resetAt?: number;
84
+ windowMs?: number;
85
+ capturedAt: number;
86
+ metadata?: MetadataMap;
87
+ createdAt?: number;
88
+ }