@nightowlsdev/core 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +270 -0
- package/dist/index.cjs +1598 -146
- package/dist/index.d.cts +979 -80
- package/dist/index.d.ts +979 -80
- package/dist/index.js +1575 -145
- package/package.json +3 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,175 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { HookDispatcher, ToolApprovalPolicy, SwarmHooks } from '@nightowlsdev/hooks';
|
|
3
|
+
export { ALLOW, ALLOW_TOOL, DEFAULT_READ_ONLY_TOOLS, GuardMutationEvent, GuardMutationHook, HookDecision, HookDispatcher, PreGenerationEvent, PreGenerationHook, PreToolCallHook, SwarmHooks, ToolApprovalPolicy, ToolDecision, ToolPreCallEvent, ask, createHookDispatcher, defineHook, deny } from '@nightowlsdev/hooks';
|
|
4
|
+
|
|
5
|
+
interface Price {
|
|
6
|
+
inUsdPerMtok: number;
|
|
7
|
+
outUsdPerMtok: number;
|
|
8
|
+
/**
|
|
9
|
+
* Per-class USD rates per million tokens (SP1). All OPTIONAL + additive — a price entry without them
|
|
10
|
+
* prices exactly like before. When a class's rate is absent the engine falls back to the closest base
|
|
11
|
+
* rate, picked to match how providers bill these classes:
|
|
12
|
+
* - `cacheReadUsdPerMtok` → falls back to `inUsdPerMtok` (cache reads are discounted input; absent a
|
|
13
|
+
* discount rate we conservatively bill them at the full input rate rather than free).
|
|
14
|
+
* - `cacheWriteUsdPerMtok` → falls back to `inUsdPerMtok` (cache writes are an input-side surcharge).
|
|
15
|
+
* - `reasoningUsdPerMtok` → falls back to `outUsdPerMtok` (reasoning tokens are generated output).
|
|
16
|
+
*/
|
|
17
|
+
cacheReadUsdPerMtok?: number;
|
|
18
|
+
cacheWriteUsdPerMtok?: number;
|
|
19
|
+
reasoningUsdPerMtok?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Per-generation usage, by token class (SP1). `inputTokens`/`outputTokens` are always present (today's
|
|
23
|
+
* shape); the rest are OPTIONAL — a provider that does not report a class leaves it undefined and it is
|
|
24
|
+
* priced as zero (NEVER fabricated). `toolCalls`/`agentActivations` are activity counts for telemetry,
|
|
25
|
+
* not priced. Plain domain type — @mastra-free (engine wall, CONTRACTS §1).
|
|
26
|
+
*/
|
|
27
|
+
interface UsageBreakdown {
|
|
28
|
+
inputTokens: number;
|
|
29
|
+
outputTokens: number;
|
|
30
|
+
cacheReadTokens?: number;
|
|
31
|
+
cacheWriteTokens?: number;
|
|
32
|
+
reasoningTokens?: number;
|
|
33
|
+
/** Number of (non-delegation) tool calls in this generation. Telemetry only — not priced. */
|
|
34
|
+
toolCalls?: number;
|
|
35
|
+
/** Number of sub-agent delegations (`agent-<slug>` calls) in this generation. Telemetry only — not priced. */
|
|
36
|
+
agentActivations?: number;
|
|
37
|
+
}
|
|
38
|
+
/** A priced usage: the computed USD plus the breakdown it was priced from. */
|
|
39
|
+
interface UsageCost {
|
|
40
|
+
usd: number;
|
|
41
|
+
breakdown: UsageBreakdown;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Optional seam that supplies/overrides per-model prices (SP1). Supplies NUMBERS only (no @mastra) so the
|
|
45
|
+
* engine wall holds. `prices()` returns a full `modelId → Price` map merged over the built-in `PRICE_TABLE`
|
|
46
|
+
* + any static `prices` config; a host can back it with a live price feed. Sync to keep governor construction
|
|
47
|
+
* synchronous on the hot path — resolve/refresh out of band and return the current snapshot here.
|
|
48
|
+
*/
|
|
49
|
+
interface PriceFeed {
|
|
50
|
+
prices(): Record<string, Price>;
|
|
51
|
+
}
|
|
52
|
+
declare const PRICE_TABLE: Record<string, Price>;
|
|
53
|
+
/** Options shared by the pricing helpers + the governors that thread pricing config through. */
|
|
54
|
+
interface PricingOpts {
|
|
55
|
+
/**
|
|
56
|
+
* When TRUE an unpriced `modelId` THROWS instead of pricing at $0. Default FALSE so OSS users (the
|
|
57
|
+
* built-in `PRICE_TABLE` has only 2 entries) are not broken — an unknown model keeps the historical
|
|
58
|
+
* $0 fallback (the cost cap simply can't fire on it). A host that wants billing safety flips this on.
|
|
59
|
+
*/
|
|
60
|
+
failOnUnknownModel?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/** Price one usage at a model's rate, across EVERY token class (SP1). Shared by the global CostGovernor and
|
|
63
|
+
* per-delegate DelegateBudgets so the two caps always agree on what a token costs. Each class is priced at
|
|
64
|
+
* its explicit per-class rate, with the documented fallbacks (see `Price`) when a rate is absent; a token
|
|
65
|
+
* class the provider omitted is treated as zero (never fabricated). An unknown model prices at $0 by default
|
|
66
|
+
* (the cap can't fire on a model with no price entry — historical behavior) unless `failOnUnknownModel`. */
|
|
67
|
+
declare function priceUsage(prices: Record<string, Price>, modelId: string, u: UsageBreakdown, opts?: PricingOpts): number;
|
|
68
|
+
/**
|
|
69
|
+
* Sum a list of `UsageBreakdown` into a single turn-total breakdown (SP4 — the room-turn billing unit).
|
|
70
|
+
* The always-present base classes (`inputTokens`/`outputTokens`) sum as plain numbers. Each OPTIONAL class
|
|
71
|
+
* (cacheRead/cacheWrite/reasoning/toolCalls/agentActivations) sums with UNDEFINED as the additive identity:
|
|
72
|
+
* - undefined + number → the number (a class present in only some generations still totals correctly),
|
|
73
|
+
* - all-undefined → stays UNDEFINED (we never fabricate a class no provider reported as a zero),
|
|
74
|
+
* - present-and-zero → preserved as 0 (an explicit 0 means "reported zero", distinct from "not reported").
|
|
75
|
+
* An empty list yields the zero base breakdown `{ inputTokens: 0, outputTokens: 0 }`. @mastra-free.
|
|
76
|
+
*/
|
|
77
|
+
declare function sumBreakdowns(items: UsageBreakdown[]): UsageBreakdown;
|
|
78
|
+
/** One generation's priced usage attributed to an agent slug — the unit the turn aggregates over. */
|
|
79
|
+
interface SlugUsage {
|
|
80
|
+
slug: string;
|
|
81
|
+
breakdown: UsageBreakdown;
|
|
82
|
+
cost: UsageCost;
|
|
83
|
+
}
|
|
84
|
+
/** A room-turn's aggregate usage (SP4): the summed breakdown + summed USD for the WHOLE turn, plus a
|
|
85
|
+
* per-agent-slug split so the platform can attribute credits per agent. `bySlug` is in first-seen order. */
|
|
86
|
+
interface TurnUsage {
|
|
87
|
+
breakdown: UsageBreakdown;
|
|
88
|
+
cost: UsageCost;
|
|
89
|
+
bySlug: SlugUsage[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Aggregate every generation's `(slug, breakdown, cost)` over a room-turn into ONE turn total + a per-slug
|
|
93
|
+
* split (SP4). The turn total breakdown/USD are the sum across ALL generations; `bySlug` folds every
|
|
94
|
+
* generation of the same slug into one entry (so a slug that generated twice — e.g. an orchestrator before
|
|
95
|
+
* and after a delegation — appears once with its combined breakdown/USD), in FIRST-SEEN order for a stable
|
|
96
|
+
* split. By construction the per-slug entries sum back to the turn total (the invariant the platform's
|
|
97
|
+
* per-agent credit attribution relies on). An empty list yields a zero total + empty split. @mastra-free.
|
|
98
|
+
*/
|
|
99
|
+
declare function sumTurnUsage(items: SlugUsage[]): TurnUsage;
|
|
100
|
+
declare class CostGovernor {
|
|
101
|
+
private opts;
|
|
102
|
+
private steps;
|
|
103
|
+
private usd;
|
|
104
|
+
private prices;
|
|
105
|
+
private failOnUnknownModel;
|
|
106
|
+
constructor(opts: {
|
|
107
|
+
maxSteps: number;
|
|
108
|
+
maxCostUsd: number;
|
|
109
|
+
/** Static per-model price overrides, merged over PRICE_TABLE. */
|
|
110
|
+
prices?: Record<string, Price>;
|
|
111
|
+
/** Optional live price seam (SP1). Merged OVER `prices` (a feed entry wins) at construction. */
|
|
112
|
+
priceFeed?: PriceFeed;
|
|
113
|
+
} & PricingOpts);
|
|
114
|
+
step(): void;
|
|
115
|
+
/** Price a single usage WITHOUT accumulating it (for per-generation telemetry cost). */
|
|
116
|
+
priceOf(modelId: string, u: UsageBreakdown): number;
|
|
117
|
+
/** Price a single usage WITHOUT accumulating it, returning the usd + the breakdown it was priced from. */
|
|
118
|
+
costOf(modelId: string, u: UsageBreakdown): UsageCost;
|
|
119
|
+
addUsage(modelId: string, u: UsageBreakdown): void;
|
|
120
|
+
costUsd(): number;
|
|
121
|
+
/** The current USD cap (SP9-core: the cap-that-asks reads this to surface "spend / cap" + to compute the raise). */
|
|
122
|
+
get maxCostUsd(): number;
|
|
123
|
+
/**
|
|
124
|
+
* SP9-core — RAISE the USD cap by `incrementUsd` (the budget an approved "Budget cap reached — continue?"
|
|
125
|
+
* grants). Mutates the governor's ceiling so a freshly-resumed generation isn't immediately re-blocked at the
|
|
126
|
+
* SAME cap; the run gets real additional headroom. Only the cap-that-asks resume path calls this; the default
|
|
127
|
+
* terminal-stop path never does, so today's behaviour is unchanged.
|
|
128
|
+
*/
|
|
129
|
+
raiseCostCap(incrementUsd: number): void;
|
|
130
|
+
shouldStop(): {
|
|
131
|
+
stop: boolean;
|
|
132
|
+
reason?: string;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Per-delegate USD sub-budgets (R5). The global `cost.maxCostUsd` bounds the WHOLE turn; this adds an
|
|
137
|
+
* optional ceiling applied to EACH delegate (sub-agent) so one runaway sub-agent can't burn the entire
|
|
138
|
+
* turn before the global cap notices. `maxCostUsd` is the default ceiling for every delegate; `bySlug`
|
|
139
|
+
* overrides it per delegate slug (a slug listed there is capped even if there is no default). The run's
|
|
140
|
+
* root orchestrator is NOT a delegate and is never capped here — the global cap already covers it.
|
|
141
|
+
*/
|
|
142
|
+
interface PerDelegateBudget {
|
|
143
|
+
/** Default USD ceiling per delegate. Omit to cap only the slugs named in `bySlug`. */
|
|
144
|
+
maxCostUsd?: number;
|
|
145
|
+
/** Per-slug overrides of `maxCostUsd`. */
|
|
146
|
+
bySlug?: Record<string, {
|
|
147
|
+
maxCostUsd?: number;
|
|
148
|
+
}>;
|
|
149
|
+
}
|
|
150
|
+
/** Tracks per-delegate USD spend and reports the first delegate to exceed its budget. Priced from the same
|
|
151
|
+
* table as CostGovernor (via the shared `priceUsage`) so the global and per-delegate caps agree. Usage
|
|
152
|
+
* attributed to the root orchestrator slug is ignored. */
|
|
153
|
+
declare class DelegateBudgets {
|
|
154
|
+
private cfg;
|
|
155
|
+
private rootSlug;
|
|
156
|
+
private usd;
|
|
157
|
+
private prices;
|
|
158
|
+
private failOnUnknownModel;
|
|
159
|
+
constructor(cfg: PerDelegateBudget, rootSlug: string, pricing?: {
|
|
160
|
+
prices?: Record<string, Price>;
|
|
161
|
+
priceFeed?: PriceFeed;
|
|
162
|
+
} & PricingOpts);
|
|
163
|
+
/** The USD cap for a delegate: its `bySlug` override if present, else the default. `undefined` → uncapped. */
|
|
164
|
+
private capFor;
|
|
165
|
+
/** Accumulate one generation's usage against a delegate. No-op for the root orchestrator (not a delegate). */
|
|
166
|
+
addUsage(slug: string, modelId: string, u: UsageBreakdown): void;
|
|
167
|
+
/** The first delegate that has met or exceeded its USD cap, or null. */
|
|
168
|
+
exceeded(): {
|
|
169
|
+
slug: string;
|
|
170
|
+
reason: string;
|
|
171
|
+
} | null;
|
|
172
|
+
}
|
|
2
173
|
|
|
3
174
|
interface SwarmContext {
|
|
4
175
|
tenantId: string;
|
|
@@ -17,8 +188,63 @@ interface AuthProvider {
|
|
|
17
188
|
authenticate(req: Request): Promise<AuthContext | null>;
|
|
18
189
|
can?(ctx: AuthContext, capability: string): boolean | Promise<boolean>;
|
|
19
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* The PRINCIPAL performing a definition mutation (SP6) — a plain (@mastra-free) discriminated union covering
|
|
193
|
+
* every kind of actor that can publish/rollback an agent definition. Distinct from `AuthContext` (the run's
|
|
194
|
+
* end-user identity): an actor models WHO is mutating the swarm's own code, which may be a human in the
|
|
195
|
+
* no-code builder, a service (CI/seeding pipeline), or a system task.
|
|
196
|
+
*
|
|
197
|
+
* - `human` → a person acting in a tenant (the no-code builder's authenticated user).
|
|
198
|
+
* - `service` → a non-human automation acting in a tenant (CI, a seeding job, a migration runner).
|
|
199
|
+
* - `system` → an internal/un-tenanted operation (bootstrap seed, ops rollback) carrying a `reason`.
|
|
200
|
+
* - `agent` → an AGENT acting on its own behalf. This variant exists for ONE reason: so an agent
|
|
201
|
+
* principal is REPRESENTABLE and can therefore be UNCONDITIONALLY BARRED from mutating a
|
|
202
|
+
* definition (an agent must never be able to rewrite the swarm's own code). The bar is
|
|
203
|
+
* enforced in the repo contract layer (`assertActorMayMutateDefinition`) and CANNOT be
|
|
204
|
+
* overridden by any policy/hook. No code should ever construct an `agent` actor expecting a
|
|
205
|
+
* mutation to succeed — it is the deny sentinel.
|
|
206
|
+
*/
|
|
207
|
+
type SwarmActor = {
|
|
208
|
+
type: "human";
|
|
209
|
+
userId: string;
|
|
210
|
+
tenantId: string;
|
|
211
|
+
} | {
|
|
212
|
+
type: "service";
|
|
213
|
+
serviceId: string;
|
|
214
|
+
tenantId: string;
|
|
215
|
+
} | {
|
|
216
|
+
type: "system";
|
|
217
|
+
reason: string;
|
|
218
|
+
} | {
|
|
219
|
+
type: "agent";
|
|
220
|
+
agentSlug: string;
|
|
221
|
+
tenantId: string;
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* The NON-BYPASSABLE security invariant for definition mutations (SP6): an `agent` principal can NEVER
|
|
225
|
+
* publish or roll back an agent definition, regardless of any configured policy/hook. Every writable-repo
|
|
226
|
+
* mutation path MUST call this FIRST (before the `guardDefinitionMutation` policy hook) so the bar cannot be
|
|
227
|
+
* weakened by an allow-all hook. Throws `AgentMutationForbidden` for an `agent` actor; a no-op otherwise.
|
|
228
|
+
*
|
|
229
|
+
* This lives in the contract layer (not behind a hook) on purpose: the hook is removable/host-configurable
|
|
230
|
+
* policy, whereas this bar is a framework invariant — fail-closed and non-negotiable.
|
|
231
|
+
*/
|
|
232
|
+
declare function assertActorMayMutateDefinition(actor: SwarmActor): void;
|
|
233
|
+
/** Thrown by `assertActorMayMutateDefinition` when an `agent` principal attempts a definition mutation. A
|
|
234
|
+
* named class so callers can distinguish the security bar from an ordinary failure (e.g. a missing version). */
|
|
235
|
+
declare class AgentMutationForbidden extends Error {
|
|
236
|
+
readonly code: "AGENT_MUTATION_FORBIDDEN";
|
|
237
|
+
constructor(agentSlug: string);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The seam a platform vault (SP15-platform) implements: given a secret `ref` + the RUN's identity ctx, it
|
|
241
|
+
* supplies the secret VALUE. @mastra-free. Resolution is execution-time + ctx-scoped (the connector calls it at
|
|
242
|
+
* tool-call time with the live run ctx — see @nightowlsdev/mcp connector.ts), so the vault enforces per-tenant
|
|
243
|
+
* scoping off `ctx.tenantId`: a run must NOT be able to resolve another tenant's secret. Returns `undefined` for
|
|
244
|
+
* an unknown ref (a tool then sees "no secret") — the resolver should never leak across tenants.
|
|
245
|
+
*/
|
|
20
246
|
interface SecretResolver {
|
|
21
|
-
resolve(ref: string, ctx: SwarmContext): Promise<string>;
|
|
247
|
+
resolve(ref: string, ctx: SwarmContext): Promise<string | undefined>;
|
|
22
248
|
}
|
|
23
249
|
/** An optional rich input the asking agent CONSTRUCTS for a HITL `ask`, so the UI can render a fitting
|
|
24
250
|
* widget instead of a bare text box. Omitted ⇒ a plain text input. Answer shape by kind: confirm→boolean,
|
|
@@ -54,6 +280,9 @@ interface EvBase {
|
|
|
54
280
|
ts: number;
|
|
55
281
|
seq?: number;
|
|
56
282
|
schemaVersion: 1;
|
|
283
|
+
/** The run's thread (the lane this event belongs to). NOT set on live events — the store fills it in on a
|
|
284
|
+
* container restore (listForContainer) so a client can tell a lane side-chat apart from the main thread. */
|
|
285
|
+
threadId?: string;
|
|
57
286
|
}
|
|
58
287
|
type SwarmEvent = (EvBase & {
|
|
59
288
|
type: "swarm.status";
|
|
@@ -65,7 +294,7 @@ type SwarmEvent = (EvBase & {
|
|
|
65
294
|
}) | (EvBase & {
|
|
66
295
|
type: "swarm.message";
|
|
67
296
|
data: {
|
|
68
|
-
role: "assistant";
|
|
297
|
+
role: "assistant" | "user";
|
|
69
298
|
delta?: string;
|
|
70
299
|
text?: string;
|
|
71
300
|
};
|
|
@@ -110,6 +339,31 @@ type SwarmEvent = (EvBase & {
|
|
|
110
339
|
from: "user" | string;
|
|
111
340
|
answer: unknown;
|
|
112
341
|
};
|
|
342
|
+
}) | (EvBase & {
|
|
343
|
+
type: "swarm.usage";
|
|
344
|
+
data: {
|
|
345
|
+
slug: string;
|
|
346
|
+
modelId: string;
|
|
347
|
+
breakdown: UsageBreakdown;
|
|
348
|
+
cost: UsageCost;
|
|
349
|
+
};
|
|
350
|
+
}) | (EvBase & {
|
|
351
|
+
type: "swarm.turn_usage";
|
|
352
|
+
data: {
|
|
353
|
+
breakdown: UsageBreakdown;
|
|
354
|
+
cost: UsageCost;
|
|
355
|
+
bySlug: Array<{
|
|
356
|
+
slug: string;
|
|
357
|
+
breakdown: UsageBreakdown;
|
|
358
|
+
cost: UsageCost;
|
|
359
|
+
}>;
|
|
360
|
+
generations: number;
|
|
361
|
+
/** The segment's STARTING generation index — distinct per run/resume segment AND retry-stable (it's the
|
|
362
|
+
* monotonic, snapshot-persisted generation counter, not a fresh per-append seq). A host keys a PER-TURN
|
|
363
|
+
* billing debit on it so each segment of a suspend/resume run is charged exactly once (run segment = 0;
|
|
364
|
+
* each resume = the snapshot's next genIndex). The engine never prices — this is just a stable turn id. */
|
|
365
|
+
segmentIndex: number;
|
|
366
|
+
};
|
|
113
367
|
}) | (EvBase & {
|
|
114
368
|
type: "swarm.run_failed";
|
|
115
369
|
data: {
|
|
@@ -142,6 +396,118 @@ interface AgentMemoryOverride {
|
|
|
142
396
|
};
|
|
143
397
|
observationalMemory?: boolean | Record<string, unknown>;
|
|
144
398
|
}
|
|
399
|
+
/** Enforcement level a rule declares. `advise` = prompt-injected guidance; `enforce` = decision-hook veto. */
|
|
400
|
+
type RuleLevel = "advise" | "enforce";
|
|
401
|
+
/** Declarative match over a hook event. Empty = match-all within the resolved seam. */
|
|
402
|
+
interface RuleCondition {
|
|
403
|
+
/** agentSlug glob/exact. For a delegation the gate sees the PARENT (orchestrator) slug. */
|
|
404
|
+
agent?: string | string[];
|
|
405
|
+
/** toolName glob/exact (tool seam). */
|
|
406
|
+
tool?: string | string[];
|
|
407
|
+
/** tool provenance. */
|
|
408
|
+
origin?: "first-party" | "mcp";
|
|
409
|
+
/** modelId glob/exact (generation seam). */
|
|
410
|
+
model?: string | string[];
|
|
411
|
+
}
|
|
412
|
+
interface RuleAction {
|
|
413
|
+
do: "deny" | "ask";
|
|
414
|
+
reason?: string;
|
|
415
|
+
}
|
|
416
|
+
interface RuleSpec {
|
|
417
|
+
id: string;
|
|
418
|
+
statement: string;
|
|
419
|
+
when: RuleCondition;
|
|
420
|
+
level: RuleLevel;
|
|
421
|
+
/** REQUIRED when level==="enforce". `ask` is TOOL-SEAM ONLY (preGeneration cannot suspend). */
|
|
422
|
+
action?: RuleAction;
|
|
423
|
+
/** Seam; inferred from `when` when omitted (model ⇒ generation, else tool). */
|
|
424
|
+
on?: "tool" | "generation";
|
|
425
|
+
}
|
|
426
|
+
/** A normalized, engine-held rule (the output of `defineRule`). */
|
|
427
|
+
interface RuleDef {
|
|
428
|
+
id: string;
|
|
429
|
+
statement: string;
|
|
430
|
+
when: RuleCondition;
|
|
431
|
+
level: RuleLevel;
|
|
432
|
+
action?: RuleAction;
|
|
433
|
+
/** Resolved seam. */
|
|
434
|
+
seam: "tool" | "generation";
|
|
435
|
+
/** Set when authored via `defineAgent` (per-agent scope); undefined ⇒ swarm-wide. */
|
|
436
|
+
scopeAgent?: string;
|
|
437
|
+
}
|
|
438
|
+
/** Workflow compliance. v1: `advisory` (prompt) | `strict` (driver, Phase B — rejected by defineSwarm in Phase A). */
|
|
439
|
+
type WorkflowCompliance = "advisory" | "strict";
|
|
440
|
+
/** A flat data reference resolved at runtime: `{ $ref: "input" }` | `{ $ref: "steps.<id>" }`. */
|
|
441
|
+
type WorkflowRef = {
|
|
442
|
+
$ref: string;
|
|
443
|
+
};
|
|
444
|
+
interface WorkflowTransition {
|
|
445
|
+
to: string;
|
|
446
|
+
when?: {
|
|
447
|
+
$ref: string;
|
|
448
|
+
eq?: unknown;
|
|
449
|
+
exists?: boolean;
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
interface WorkflowStep {
|
|
453
|
+
id: string;
|
|
454
|
+
/** exactly ONE kind: */
|
|
455
|
+
agent?: string;
|
|
456
|
+
tool?: string;
|
|
457
|
+
human?: {
|
|
458
|
+
prompt: string;
|
|
459
|
+
field?: AskField;
|
|
460
|
+
};
|
|
461
|
+
/** agent steps: */
|
|
462
|
+
instruction?: string;
|
|
463
|
+
/** tool steps (values may be a WorkflowRef): */
|
|
464
|
+
args?: Record<string, unknown>;
|
|
465
|
+
/** agent steps (values may be a WorkflowRef): */
|
|
466
|
+
input?: Record<string, unknown>;
|
|
467
|
+
next?: string | WorkflowTransition[];
|
|
468
|
+
onError?: "fail" | {
|
|
469
|
+
to: string;
|
|
470
|
+
} | {
|
|
471
|
+
retry: number;
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
interface WorkflowSpec {
|
|
475
|
+
name: string;
|
|
476
|
+
compliance: WorkflowCompliance;
|
|
477
|
+
description?: string;
|
|
478
|
+
steps: WorkflowStep[];
|
|
479
|
+
start?: string;
|
|
480
|
+
}
|
|
481
|
+
/** A normalized, engine-held workflow (the output of `defineWorkflow`). */
|
|
482
|
+
interface WorkflowDef {
|
|
483
|
+
name: string;
|
|
484
|
+
compliance: WorkflowCompliance;
|
|
485
|
+
description?: string;
|
|
486
|
+
steps: WorkflowStep[];
|
|
487
|
+
/** Resolved start step id (default: steps[0].id). */
|
|
488
|
+
start: string;
|
|
489
|
+
/** Set when authored via `defineAgent.workflow` (per-agent scope). */
|
|
490
|
+
scopeAgent?: string;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* The durable state of an in-flight STRICT workflow run (Phase B). Rides the existing run snapshot payload
|
|
494
|
+
* (not a new table). `cursor` = the step to run next; `outputs` = accumulated step results (for `$ref`);
|
|
495
|
+
* `generationIndex` = the monotonic per-run reserve counter continued across steps; `pending` marks a
|
|
496
|
+
* human/approval suspend so `resume()` re-enters the driver (synthetic `followupId`+`toolCallId` satisfy the
|
|
497
|
+
* resume-auth cross-check). Progress snapshotting only — NOT crash-mid-step replay.
|
|
498
|
+
*/
|
|
499
|
+
interface WorkflowRunState {
|
|
500
|
+
workflow: string;
|
|
501
|
+
cursor: string;
|
|
502
|
+
outputs: Record<string, unknown>;
|
|
503
|
+
generationIndex: number;
|
|
504
|
+
pending?: {
|
|
505
|
+
kind: "human" | "approval";
|
|
506
|
+
stepId: string;
|
|
507
|
+
followupId: string;
|
|
508
|
+
toolCallId: string;
|
|
509
|
+
};
|
|
510
|
+
}
|
|
145
511
|
interface AgentDef {
|
|
146
512
|
slug: string;
|
|
147
513
|
head: AgentVersion;
|
|
@@ -154,6 +520,82 @@ interface AgentDef {
|
|
|
154
520
|
skills?: {
|
|
155
521
|
name: string;
|
|
156
522
|
}[];
|
|
523
|
+
/** Per-agent rules (engine-local, v1) — `defineAgent` stamps `scopeAgent` so `defineSwarm` can collect them. */
|
|
524
|
+
rules?: RuleDef[];
|
|
525
|
+
/** Per-agent workflow/procedure (engine-local, v1) — `scopeAgent`-stamped by `defineAgent`. */
|
|
526
|
+
workflow?: WorkflowDef;
|
|
527
|
+
}
|
|
528
|
+
/** A closure dependency satisfied by ANOTHER bundle: a delegate slug that is not a member of this bundle. Carried
|
|
529
|
+
* as a flat min-version floor (no transitive resolution in v1). */
|
|
530
|
+
interface BundleDep {
|
|
531
|
+
slug: string;
|
|
532
|
+
minVersion: number;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* BN1 — a declarative connector grant: a member may invoke a connector's actions. The action names are folded into
|
|
536
|
+
* that member's `skillNames`, so the host's connector-tools resolver (`SwarmConfig.connectorTools`) materializes
|
|
537
|
+
* them per-tenant at CALL time, gated by the SP5 approval floor. The bundle carries NAMES ONLY — never a token,
|
|
538
|
+
* connection id, or backend; the host wires the connector + per-tenant credentials. A granted action need not have
|
|
539
|
+
* a first-party skill handle (it is connector-backed), so it is the explicit, validated exception to BN0's
|
|
540
|
+
* "every skill must resolve to a handle" closure rule.
|
|
541
|
+
*/
|
|
542
|
+
interface ConnectorGrant {
|
|
543
|
+
/** The bundle member granted these actions (must be one of the bundle's `agents`). */
|
|
544
|
+
agentSlug: string;
|
|
545
|
+
/** The connector provider, e.g. `"slack"` — informational, and the prefix used to expand a short action name. */
|
|
546
|
+
provider: string;
|
|
547
|
+
/** Connector action names — full (`"slack.post_message"`) or short (`"post_message"`, expanded to `provider.action`). */
|
|
548
|
+
actions: string[];
|
|
549
|
+
}
|
|
550
|
+
/** Authoring input for `defineBundle`. It composes `defineAgent` OUTPUTS — it does not replace them. */
|
|
551
|
+
interface BundleSpec {
|
|
552
|
+
slug: string;
|
|
553
|
+
title?: string;
|
|
554
|
+
/** The composed members — exactly the output of `defineAgent` (skill handles ride along on each `AgentDef`). */
|
|
555
|
+
agents: AgentDef[];
|
|
556
|
+
/** SWARM-scoped rules (per-agent rules ride on the `AgentDef`s via `defineAgent`'s `scopeAgent` stamp). */
|
|
557
|
+
rules?: RuleDef[];
|
|
558
|
+
/** SWARM-scoped workflows (per-agent workflows ride on the `AgentDef`s). */
|
|
559
|
+
workflows?: WorkflowDef[];
|
|
560
|
+
/** BN1 — connector grants: a member may invoke a provider's actions (names only; the host materializes them). */
|
|
561
|
+
connectorGrants?: ConnectorGrant[];
|
|
562
|
+
/** Delegates that live in ANOTHER bundle (a delegate slug not among `agents`) — declared so closure validation
|
|
563
|
+
* can distinguish a legitimate external dependency from a typo. */
|
|
564
|
+
requires?: BundleDep[];
|
|
565
|
+
}
|
|
566
|
+
/** The validated, closure-checked composition `mergeBundle` folds into a `SwarmConfig`. The members carry any
|
|
567
|
+
* BN1 connector-grant action names folded into their `skillNames`. */
|
|
568
|
+
interface BundleDef {
|
|
569
|
+
slug: string;
|
|
570
|
+
title?: string;
|
|
571
|
+
agents: AgentDef[];
|
|
572
|
+
rules: RuleDef[];
|
|
573
|
+
workflows: WorkflowDef[];
|
|
574
|
+
connectorGrants: ConnectorGrant[];
|
|
575
|
+
requires: BundleDep[];
|
|
576
|
+
}
|
|
577
|
+
/** The publishable bundle payload (version is derived, not authored — mirrors `AgentVersionContent`). */
|
|
578
|
+
interface BundleVersionContent {
|
|
579
|
+
slug: string;
|
|
580
|
+
title?: string;
|
|
581
|
+
/** Composed members as serializable heads (no skill handles). */
|
|
582
|
+
agents: AgentVersionContent[];
|
|
583
|
+
rules: RuleDef[];
|
|
584
|
+
workflows: WorkflowDef[];
|
|
585
|
+
connectorGrants: ConnectorGrant[];
|
|
586
|
+
requires: BundleDep[];
|
|
587
|
+
}
|
|
588
|
+
/** One immutable, append-only bundle version (mirrors `AgentVersion` one level up). */
|
|
589
|
+
interface BundleVersion extends BundleVersionContent {
|
|
590
|
+
version: number;
|
|
591
|
+
}
|
|
592
|
+
/** A bundle version's summary row (for `listVersions` — mirrors `AgentVersionInfo`). */
|
|
593
|
+
interface BundleVersionInfo {
|
|
594
|
+
version: number;
|
|
595
|
+
title: string;
|
|
596
|
+
status: string;
|
|
597
|
+
isCurrent: boolean;
|
|
598
|
+
memberCount: number;
|
|
157
599
|
}
|
|
158
600
|
interface RunRow {
|
|
159
601
|
runId: string;
|
|
@@ -210,6 +652,11 @@ interface EventStore {
|
|
|
210
652
|
append(e: SwarmEvent): Promise<number>;
|
|
211
653
|
/** Tenant-scoped (R11): a forged cross-org runId returns []. The store enforces tenancy, not just the caller. */
|
|
212
654
|
list(tenantId: string, runId: string, sinceSeq: number): Promise<SwarmEvent[]>;
|
|
655
|
+
/** The full event log for a CONTAINER — every run in the conversation (the root thread + lane sub-threads
|
|
656
|
+
* `<container>:<slug>`), globally ordered by the generated `seq`. Lets a host rebuild a thread's RICH timeline
|
|
657
|
+
* (tool calls + delegation cards) on reload, where message-history is text-only. Tenant-scoped. Optional: a
|
|
658
|
+
* store without an events table may omit it (the host then falls back to message history). */
|
|
659
|
+
listForContainer?(tenantId: string, container: string): Promise<SwarmEvent[]>;
|
|
213
660
|
subscribe(runId: string): AsyncIterable<SwarmEvent>;
|
|
214
661
|
}
|
|
215
662
|
interface RunStore {
|
|
@@ -276,8 +723,75 @@ interface AgentRepo {
|
|
|
276
723
|
getVersion(tenantId: string, slug: string, version: number): Promise<AgentVersion | null>;
|
|
277
724
|
listSlugs(tenantId: string): Promise<string[]>;
|
|
278
725
|
}
|
|
726
|
+
/** The publishable content of one agent version — everything an `AgentVersion` carries EXCEPT the `version`
|
|
727
|
+
* number, which is derived (append-only `max+1`) by the repo, never supplied by the caller. */
|
|
728
|
+
type AgentVersionContent = Omit<AgentVersion, "version">;
|
|
729
|
+
/** One row of an agent's version history (for the no-code builder's rollback UX). Plain/@mastra-free. */
|
|
730
|
+
interface AgentVersionInfo {
|
|
731
|
+
version: number;
|
|
732
|
+
role: string;
|
|
733
|
+
modelId: string;
|
|
734
|
+
status: string;
|
|
735
|
+
isCurrent: boolean;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* The WRITABLE agent definition contract (SP6) — "the one true framework extension" the no-code builder hangs
|
|
739
|
+
* off. Extends the read-only `AgentRepo` with the append-only mutation surface:
|
|
740
|
+
* - `publish` → commit a new head version of `content`; returns the derived version number.
|
|
741
|
+
* - `rollback` → republish a prior version's content as a NEW head (append-only, `git revert` not
|
|
742
|
+
* `reset`); returns the new version + the source it was restored from.
|
|
743
|
+
* - `listVersions` → the agent's version history (oldest→newest), flagging the current head.
|
|
744
|
+
*
|
|
745
|
+
* Every mutation takes a `SwarmActor` (the principal) — NOT a bare string — and the implementation MUST:
|
|
746
|
+
* 1. call `assertActorMayMutateDefinition(actor)` FIRST (the non-bypassable agent-bar), then
|
|
747
|
+
* 2. consult the configured `guardDefinitionMutation` policy hook (if any),
|
|
748
|
+
* before committing. `listVersions` takes the actor too so an impl can authorize reads consistently (and so
|
|
749
|
+
* the agent-bar applies uniformly); reads do not mutate, so an impl MAY allow an `agent` actor to list, but
|
|
750
|
+
* the shipped impls apply the same bar for symmetry.
|
|
751
|
+
*/
|
|
752
|
+
interface VersionedRepo extends AgentRepo {
|
|
753
|
+
publish(tenantId: string, slug: string, content: AgentVersionContent, actor: SwarmActor): Promise<{
|
|
754
|
+
version: number;
|
|
755
|
+
}>;
|
|
756
|
+
rollback(tenantId: string, slug: string, toVersion: number, actor: SwarmActor): Promise<{
|
|
757
|
+
version: number;
|
|
758
|
+
restoredFrom: number;
|
|
759
|
+
}>;
|
|
760
|
+
listVersions(tenantId: string, slug: string, actor: SwarmActor): Promise<AgentVersionInfo[]>;
|
|
761
|
+
}
|
|
762
|
+
/** BN2 — the read-only bundle version repo (head/getVersion/listSlugs). Structurally identical to `AgentRepo`,
|
|
763
|
+
* one level up: a `BundleVersion` is the unit, not an agent. */
|
|
764
|
+
interface BundleRepo {
|
|
765
|
+
head(tenantId: string, slug: string): Promise<BundleVersion | null>;
|
|
766
|
+
getVersion(tenantId: string, slug: string, version: number): Promise<BundleVersion | null>;
|
|
767
|
+
listSlugs(tenantId: string): Promise<string[]>;
|
|
768
|
+
}
|
|
769
|
+
/** BN2 — the WRITABLE bundle version repo (append-only publish/rollback/listVersions). Mirrors `VersionedRepo`;
|
|
770
|
+
* every mutation enforces the same non-bypassable actor-bar (an `agent` principal can never publish a bundle). */
|
|
771
|
+
interface BundleWritableRepo extends BundleRepo {
|
|
772
|
+
publish(tenantId: string, slug: string, content: BundleVersionContent, actor: SwarmActor): Promise<{
|
|
773
|
+
version: number;
|
|
774
|
+
}>;
|
|
775
|
+
rollback(tenantId: string, slug: string, toVersion: number, actor: SwarmActor): Promise<{
|
|
776
|
+
version: number;
|
|
777
|
+
restoredFrom: number;
|
|
778
|
+
}>;
|
|
779
|
+
listVersions(tenantId: string, slug: string, actor: SwarmActor): Promise<BundleVersionInfo[]>;
|
|
780
|
+
}
|
|
279
781
|
interface StorageAdapter {
|
|
782
|
+
/** Read-only agent definitions (always present). The supabase adapter's `agents` is also a `VersionedRepo`,
|
|
783
|
+
* but the base contract stays `AgentRepo` so a read-only adapter compiles without growing a write path. */
|
|
280
784
|
agents: AgentRepo;
|
|
785
|
+
/** Opt-in WRITABLE agent definitions (SP6). Present on adapters that back a definition store (the supabase
|
|
786
|
+
* adapter); omitted by read-only adapters (storage-local, the in-memory dev store) — a no-code builder
|
|
787
|
+
* requires an adapter that provides it. When present it is the SAME object as `agents` (a `VersionedRepo`
|
|
788
|
+
* IS an `AgentRepo`), surfaced under a distinct field so the writable surface is explicit + tree-checkable. */
|
|
789
|
+
agentsWritable?: VersionedRepo;
|
|
790
|
+
/** BN2 — opt-in WRITABLE bundle versions (append-only, same actor-bar). Present on adapters that back a bundle
|
|
791
|
+
* store (the supabase adapter); omitted by read-only/in-memory adapters. `bundles` is the read surface,
|
|
792
|
+
* `bundlesWritable` the same object's write surface — mirrors the `agents`/`agentsWritable` pattern. */
|
|
793
|
+
bundles?: BundleRepo;
|
|
794
|
+
bundlesWritable?: BundleWritableRepo;
|
|
281
795
|
runs: RunStore;
|
|
282
796
|
events: EventStore;
|
|
283
797
|
messages: MessageStore;
|
|
@@ -292,11 +806,16 @@ interface StorageAdapter {
|
|
|
292
806
|
*/
|
|
293
807
|
recordSuspend?(runId: string, tenantId: string, followupId: string, toolCallId: string): void | Promise<void>;
|
|
294
808
|
/**
|
|
295
|
-
* Mark a followup
|
|
296
|
-
*
|
|
297
|
-
*
|
|
809
|
+
* Mark a followup answered so it can no longer be resumed (the engine calls this when a `resume` begins).
|
|
810
|
+
* Closes a replay hole: without it, `findSuspended` keeps returning the followup and the same answer can be
|
|
811
|
+
* replayed indefinitely. Tenant-scoped.
|
|
812
|
+
*
|
|
813
|
+
* **Compare-and-set (K4):** returns `true` if THIS call transitioned the followup unanswered→answered, `false`
|
|
814
|
+
* if it was already answered. An out-of-band reply path uses this as the single answer-once guard — two
|
|
815
|
+
* distinct inbound replies for one followup race here, and only the `true` winner resumes (the loser ACKs).
|
|
816
|
+
* The engine's own resume ignores the return (it already passed `findSuspended`).
|
|
298
817
|
*/
|
|
299
|
-
markFollowupAnswered?(followupId: string, tenantId: string):
|
|
818
|
+
markFollowupAnswered?(followupId: string, tenantId: string): boolean | Promise<boolean>;
|
|
300
819
|
/**
|
|
301
820
|
* Cross-process cache invalidation (R12). Subscribe to agent-republish notifications so an engine on ANY
|
|
302
821
|
* instance can evict its agent-row cache immediately instead of waiting out the TTL. `onInvalidate` receives
|
|
@@ -357,7 +876,29 @@ interface RunInput {
|
|
|
357
876
|
* (those come from `SwarmContext`); treat its contents as opaque, attacker-controllable input.
|
|
358
877
|
*/
|
|
359
878
|
context?: Record<string, unknown>;
|
|
879
|
+
/**
|
|
880
|
+
* Phase B: run a named STRICT workflow via the step-driver instead of the free-form agent turn. Engine/host-
|
|
881
|
+
* side seam — set by a host calling the runner/engine directly; NOT exposed on the public chat route or MCP
|
|
882
|
+
* (the wall promise). An unknown name throws before the run row is created.
|
|
883
|
+
*/
|
|
884
|
+
workflow?: string;
|
|
885
|
+
}
|
|
886
|
+
/** The verdict from a {@link CompletionVerifier}: was the user's request actually satisfied, and if not, a
|
|
887
|
+
* short description of what's still missing (used to build a targeted continue-nudge). */
|
|
888
|
+
interface CompletionVerdict {
|
|
889
|
+
complete: boolean;
|
|
890
|
+
missing?: string;
|
|
360
891
|
}
|
|
892
|
+
/**
|
|
893
|
+
* Completion supervisor hook (reliability) — see EngineOpts.verifyCompletion. Given the original request + a
|
|
894
|
+
* transcript of what the run produced, decide whether the task is genuinely done. Host-supplied (typically a
|
|
895
|
+
* cheap LLM judge). FAIL-SAFE at the call site: a throw/timeout is treated as "complete" (never trap a run).
|
|
896
|
+
*/
|
|
897
|
+
type CompletionVerifier = (args: {
|
|
898
|
+
request: string;
|
|
899
|
+
transcript: string;
|
|
900
|
+
ctx: SwarmContext;
|
|
901
|
+
}) => Promise<CompletionVerdict> | CompletionVerdict;
|
|
361
902
|
interface Runner {
|
|
362
903
|
run(input: RunInput, ctx: SwarmContext): AsyncIterable<SwarmEvent>;
|
|
363
904
|
enqueue(input: RunInput, ctx: SwarmContext): Promise<{
|
|
@@ -369,6 +910,22 @@ interface Runner {
|
|
|
369
910
|
followupId: string;
|
|
370
911
|
answer: unknown;
|
|
371
912
|
}, ctx: SwarmContext): AsyncIterable<SwarmEvent>;
|
|
913
|
+
/**
|
|
914
|
+
* Durable resume (symmetric with `enqueue`): wake a suspended run and return its `runId` WITHOUT streaming the
|
|
915
|
+
* continuation — the resumed events reach the client over its EXISTING Realtime subscription. A streaming-only
|
|
916
|
+
* runner omits this; the durable runner provides it. Streaming the resume instead would never close (the
|
|
917
|
+
* Realtime subscribe has no terminal), so the client's `answer()` would hang. Also re-wakes a continuation lost
|
|
918
|
+
* to a process restart, from the durable snapshot (see the background runner).
|
|
919
|
+
*/
|
|
920
|
+
resumeEnqueue?(args: {
|
|
921
|
+
runId: string;
|
|
922
|
+
toolCallId: string;
|
|
923
|
+
followupId: string;
|
|
924
|
+
answer: unknown;
|
|
925
|
+
context?: Record<string, unknown>;
|
|
926
|
+
}, ctx: SwarmContext): Promise<{
|
|
927
|
+
runId: string;
|
|
928
|
+
}>;
|
|
372
929
|
}
|
|
373
930
|
|
|
374
931
|
type ByType<T extends SwarmEvent["type"]> = Extract<SwarmEvent, {
|
|
@@ -422,88 +979,116 @@ declare class InMemoryContainerFloor implements ContainerFloor {
|
|
|
422
979
|
/** Process-wide singleton. In-memory → single-process only (serverless instances don't share it). */
|
|
423
980
|
declare const containerFloor: ContainerFloor;
|
|
424
981
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
private prices;
|
|
435
|
-
constructor(opts: {
|
|
436
|
-
maxSteps: number;
|
|
437
|
-
maxCostUsd: number;
|
|
438
|
-
prices?: Record<string, Price>;
|
|
439
|
-
});
|
|
440
|
-
step(): void;
|
|
441
|
-
/** Price a single usage WITHOUT accumulating it (for per-generation telemetry cost). */
|
|
442
|
-
priceOf(modelId: string, u: {
|
|
443
|
-
inputTokens: number;
|
|
444
|
-
outputTokens: number;
|
|
445
|
-
}): number;
|
|
446
|
-
addUsage(modelId: string, u: {
|
|
447
|
-
inputTokens: number;
|
|
448
|
-
outputTokens: number;
|
|
449
|
-
}): void;
|
|
450
|
-
costUsd(): number;
|
|
451
|
-
shouldStop(): {
|
|
452
|
-
stop: boolean;
|
|
453
|
-
reason?: string;
|
|
454
|
-
};
|
|
982
|
+
type ModelTier = "swift" | "genius";
|
|
983
|
+
type ModelRef = string;
|
|
984
|
+
/** Context handed to the per-task escalation hook so it can decide whether a specific generation needs Genius. */
|
|
985
|
+
interface TierEscalationContext {
|
|
986
|
+
tenantId: string;
|
|
987
|
+
/** The agent the generation is for. */
|
|
988
|
+
agentSlug: string;
|
|
989
|
+
/** The agent's declared modelId (a tier sentinel when it opted into routing; the concrete pin otherwise). */
|
|
990
|
+
pinnedModelId?: string;
|
|
455
991
|
}
|
|
456
992
|
/**
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
993
|
+
* The tier configuration (lives on `SwarmConfig.models.tier` → `EngineOpts.tier`). @mastra-free.
|
|
994
|
+
* - `tiers.swift` — the cheap DEFAULT model. REQUIRED (the floor every non-pinned agent lands on).
|
|
995
|
+
* - `tiers.genius` — the frontier model. OPTIONAL; reachable ONLY through the premium gate below.
|
|
996
|
+
* - `default` — the tier the bare `"tier:"` sentinel resolves to. Default `"swift"` (cheap-default).
|
|
997
|
+
* - `allowGenius` — the SERVER-ENFORCED OPT-IN GATE. Default `false`. This is NOT a user-facing "smart"
|
|
998
|
+
* slider: it lives in the platform-set EngineOpts so a pack/agent config CANNOT grant itself
|
|
999
|
+
* Genius. With it false, any Genius request (a `tier:genius` agent OR an escalation) is
|
|
1000
|
+
* DOWNGRADED to Swift so the run still proceeds cheaply (deny-vs-downgrade: we DOWNGRADE).
|
|
1001
|
+
* - `escalate` — optional per-task hook (configurable per pack): given the generation's context it may bump
|
|
1002
|
+
* the chosen tier to `"genius"` (e.g. an ambiguous case). STILL subject to `allowGenius` —
|
|
1003
|
+
* escalation cannot bypass the premium gate.
|
|
462
1004
|
*/
|
|
463
|
-
interface
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
1005
|
+
interface TierConfig {
|
|
1006
|
+
tiers: {
|
|
1007
|
+
swift: ModelRef;
|
|
1008
|
+
genius?: ModelRef;
|
|
1009
|
+
};
|
|
1010
|
+
default?: ModelTier;
|
|
1011
|
+
allowGenius?: boolean;
|
|
1012
|
+
escalate?: (ctx: TierEscalationContext) => ModelTier | undefined;
|
|
470
1013
|
}
|
|
471
|
-
/**
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
addUsage(slug: string, modelId: string, u: {
|
|
484
|
-
inputTokens: number;
|
|
485
|
-
outputTokens: number;
|
|
486
|
-
}): void;
|
|
487
|
-
/** The first delegate that has met or exceeded its USD cap, or null. */
|
|
488
|
-
exceeded(): {
|
|
489
|
-
slug: string;
|
|
490
|
-
reason: string;
|
|
491
|
-
} | null;
|
|
1014
|
+
/** The outcome of routing a single generation's model. `modelId` is always the EFFECTIVE id to use next. */
|
|
1015
|
+
interface TierResolution {
|
|
1016
|
+
/** The effective modelId to hand to the allow-list + factory. */
|
|
1017
|
+
modelId: string;
|
|
1018
|
+
/** The tier actually landed on. Undefined when the agent pinned a concrete model (a pin is not a tier). */
|
|
1019
|
+
tier?: ModelTier;
|
|
1020
|
+
/** True when a Genius request was downgraded to Swift (gate closed, or no genius model configured). */
|
|
1021
|
+
downgraded: boolean;
|
|
1022
|
+
/** The tier that was REQUESTED before gating (set when it differs from `tier`, i.e. on a downgrade). */
|
|
1023
|
+
requestedTier?: ModelTier;
|
|
1024
|
+
/** True when the escalation hook bumped the tier (to genius) and that bump survived the gate. */
|
|
1025
|
+
escalated?: boolean;
|
|
492
1026
|
}
|
|
1027
|
+
/** Is this declared modelId a tier sentinel (opting into routing) rather than a concrete pin? */
|
|
1028
|
+
declare function isTierSentinel(modelId: string | undefined): boolean;
|
|
1029
|
+
/**
|
|
1030
|
+
* Resolve the effective model for ONE generation, applying tier routing + the premium Genius gate + escalation.
|
|
1031
|
+
*
|
|
1032
|
+
* Order of precedence:
|
|
1033
|
+
* 1. A CONCRETE PIN (non-sentinel modelId) is returned verbatim — routing/escalation never touch a pin.
|
|
1034
|
+
* 2. Otherwise the requested tier = the sentinel's tier (or the config default for a bare `"tier:"`).
|
|
1035
|
+
* 3. The optional `escalate` hook may bump the request to `"genius"`.
|
|
1036
|
+
* 4. THE GATE: a `"genius"` request survives ONLY when `allowGenius` is true AND a `genius` model is
|
|
1037
|
+
* configured; otherwise it DOWNGRADES to `"swift"` (the run proceeds cheaply; `downgraded` is flagged).
|
|
1038
|
+
*/
|
|
1039
|
+
declare function resolveTier(modelId: string, cfg: TierConfig, ctx: TierEscalationContext): TierResolution;
|
|
1040
|
+
/**
|
|
1041
|
+
* The engine-facing convenience: given an agent's declared modelId, return the EFFECTIVE modelId after tier
|
|
1042
|
+
* routing. When no tier config is configured this is the identity function (today's behaviour). The resulting
|
|
1043
|
+
* modelId then flows through the SAME allow-list validation + factory mapping as before — a tier model is never
|
|
1044
|
+
* exempt from the allow-list.
|
|
1045
|
+
*/
|
|
1046
|
+
declare function tierModelId(modelId: string, cfg: TierConfig | undefined, ctx: TierEscalationContext): string;
|
|
493
1047
|
|
|
494
1048
|
interface EngineOpts {
|
|
495
1049
|
storage: StorageAdapter;
|
|
496
1050
|
model: ModelProvider;
|
|
497
1051
|
modelFactory: (modelId: string, agentSlug?: string) => unknown;
|
|
498
|
-
/**
|
|
499
|
-
*
|
|
1052
|
+
/**
|
|
1053
|
+
* SP10 — the cheap-model router (Swift/Genius tiers). @mastra-free. When set, the per-agent model resolver
|
|
1054
|
+
* routes a tier-sentinel `modelId` (`"tier:swift"` / `"tier:genius"` / bare `"tier:"`) to the configured tier
|
|
1055
|
+
* model; a concrete pinned `modelId` is kept verbatim (routing layers OVER pinning). `tiers.swift` is the
|
|
1056
|
+
* cheap DEFAULT every non-pinned agent lands on; `tiers.genius` is reachable ONLY through the server-enforced
|
|
1057
|
+
* `allowGenius` premium gate (default false) — set HERE in EngineOpts so a pack/agent config cannot grant itself
|
|
1058
|
+
* Genius. An optional per-task `escalate` hook may bump a generation to Genius, still subject to the gate. When
|
|
1059
|
+
* a Genius request is denied by the gate it DOWNGRADES to Swift so the run proceeds cheaply. Omit ⇒ no routing
|
|
1060
|
+
* (identical to today: the agent's own `modelId` flows through, still allow-list-validated). The routed model
|
|
1061
|
+
* is ALWAYS re-validated by the allow-list `model` provider — a tier model is never exempt.
|
|
1062
|
+
*/
|
|
1063
|
+
tier?: TierConfig;
|
|
1064
|
+
/** Global per-run caps + metering config (SP1) + optional per-delegate sub-budgets (R5: `perDelegate` stops
|
|
1065
|
+
* one runaway sub-agent from burning the whole turn before the global `maxCostUsd` notices).
|
|
1066
|
+
* - `prices`: static per-model overrides, merged over PRICE_TABLE.
|
|
1067
|
+
* - `priceFeed`: optional live price seam (numbers only — engine wall). Merged over `prices`.
|
|
1068
|
+
* - `failOnUnknownModel` (default false): an unpriced model THROWS instead of pricing at $0.
|
|
1069
|
+
* - `onCapHit` (SP9-core, default "stop"): the GLOBAL run-level cost/step cap's behaviour when reached.
|
|
1070
|
+
* "stop" (DEFAULT) = today's terminal `run_failed` stage "cost" — byte-identical to before.
|
|
1071
|
+
* "ask" = PAUSE the run and ASK the user "Budget cap reached — continue?" (a CONSUMER-context opt-in),
|
|
1072
|
+
* reusing SP5's suspend→swarm.question→resume machinery. Resume-approve RAISES the budget by
|
|
1073
|
+
* `capIncrementUsd` and continues; resume-reject terminally stops (stage "cost"). The
|
|
1074
|
+
* per-delegate cap (`perDelegate`) is NOT folded into the ask flow — it stays terminal.
|
|
1075
|
+
* - `capIncrementUsd` (SP9-core): the additional USD headroom an "ask" APPROVE grants (`maxCostUsd +=`).
|
|
1076
|
+
* Defaults to the original `maxCostUsd` ("another budget's worth"). Only meaningful with `onCapHit:"ask"`. */
|
|
500
1077
|
cost: {
|
|
501
1078
|
maxSteps: number;
|
|
502
1079
|
maxCostUsd: number;
|
|
503
1080
|
perDelegate?: PerDelegateBudget;
|
|
1081
|
+
prices?: Record<string, Price>;
|
|
1082
|
+
priceFeed?: PriceFeed;
|
|
1083
|
+
failOnUnknownModel?: boolean;
|
|
1084
|
+
onCapHit?: "stop" | "ask";
|
|
1085
|
+
capIncrementUsd?: number;
|
|
504
1086
|
};
|
|
505
1087
|
/** Per-swarm skill resolver. When omitted, agents expose only built-in tools. */
|
|
506
1088
|
resolveSkill?: (name: string) => SwarmSkill | undefined;
|
|
1089
|
+
/** PR2 — opt-in per-request connector-tools resolver (tenant-scoped). Forwarded verbatim to `buildMastraAgent`
|
|
1090
|
+
* for the orchestrator + every sub-agent. Connector-agnostic: core never imports `@nightowlsdev/connectors`. */
|
|
1091
|
+
connectorTools?: (ctx: SwarmContext) => Promise<SwarmTool[]>;
|
|
507
1092
|
/**
|
|
508
1093
|
* Mastra storage backend for suspend/resume snapshots. Resume is storage-gated
|
|
509
1094
|
* (SPIKE-FINDINGS item 5): the in-memory default cannot survive process death.
|
|
@@ -531,9 +1116,58 @@ interface EngineOpts {
|
|
|
531
1116
|
};
|
|
532
1117
|
/** Opt-in `recall_lane` tool (Part E). Read-only peer-lane transcript read. */
|
|
533
1118
|
recallLane?: boolean;
|
|
1119
|
+
/** Phase A soft tier: per-agent soft-policy lines (advise rules + advisory-workflow summaries) appended to
|
|
1120
|
+
* each agent's system prompt. Built by `defineSwarm` from the swarm's rules/workflows. Omit ⇒ no policy. */
|
|
1121
|
+
softPolicy?: (slug: string) => string[];
|
|
1122
|
+
/** Phase B: swarm-level STRICT workflows, runnable by name via `RunInput.workflow`. Built by `defineSwarm`. */
|
|
1123
|
+
workflows?: WorkflowDef[];
|
|
1124
|
+
/** Phase B: per-agent STRICT workflows (keyed by agent slug) — replace that agent's turn when it owns the run. */
|
|
1125
|
+
agentWorkflows?: Record<string, WorkflowDef>;
|
|
534
1126
|
/** Injectable per-lane floor (Part C / E3). Default: the in-memory process singleton. Pass a Postgres-backed
|
|
535
1127
|
* floor (createPostgresFloor) for serverless / multi-instance deploys. */
|
|
536
1128
|
floor?: ContainerFloor;
|
|
1129
|
+
/** Decision/observer hook dispatcher (SP2). `defineSwarm` always supplies one (allow-all when no hooks are
|
|
1130
|
+
* configured). When omitted (e.g. an engine built directly in a unit test), the engine defaults to an
|
|
1131
|
+
* allow-all dispatcher — behaviour identical to today. The engine AWAITS `preGeneration` before every model
|
|
1132
|
+
* launch; a `deny` vetoes the generation (terminal `run_failed` stage `"reserve"`). The same dispatcher's
|
|
1133
|
+
* `preToolCall` powers SP5's action-approval gate. */
|
|
1134
|
+
hooks?: HookDispatcher;
|
|
1135
|
+
/**
|
|
1136
|
+
* SP5 — the NON-REMOVABLE tool-approval policy (a P0 SAFETY control: spend caps limit cost, not harm). Forces
|
|
1137
|
+
* human approval on side-effecting tools regardless of the per-tool `needsApproval` flag, so a consumer pack
|
|
1138
|
+
* can't ship a `needsApproval:false` $0.50 action that causes $50k of damage. `defineSwarm` bakes this into the
|
|
1139
|
+
* `hooks` dispatcher (which combines policy + flag + the `preToolCall` hook), so when `hooks` is supplied THAT
|
|
1140
|
+
* dispatcher's policy is authoritative. This standalone field lets a DIRECT engine builder (e.g. a unit test
|
|
1141
|
+
* that passes no dispatcher) set the policy; the engine then builds an allow-all-hooks dispatcher WITH it.
|
|
1142
|
+
* Default `{ mode: "flag" }` — today's behaviour (only `needsApproval:true` tools gate).
|
|
1143
|
+
*/
|
|
1144
|
+
toolApproval?: ToolApprovalPolicy;
|
|
1145
|
+
/**
|
|
1146
|
+
* SP15 — the optional SecretResolver the platform vault (SP15-platform) implements. When set, the engine
|
|
1147
|
+
* injects it on every run's RequestContext (SAME seam as SP5's ToolGate) so a first-party tool body can
|
|
1148
|
+
* `await ctx.secrets.resolve(ref)` to fetch a tenant-scoped secret at execution time. @mastra-free. Omit ⇒
|
|
1149
|
+
* `ctx.secrets.resolve(...)` yields `undefined` (no vault) and the no-secrets path is unchanged from today.
|
|
1150
|
+
*/
|
|
1151
|
+
secrets?: SecretResolver;
|
|
1152
|
+
/**
|
|
1153
|
+
* SP3 — best-effort per-event OBSERVER, fired by the engine AFTER each event is persisted (in `emit`), for
|
|
1154
|
+
* BOTH `run` and `resume`. Transport-agnostic: it sees every event regardless of how it reaches the client
|
|
1155
|
+
* (interactive SSE vs durable + realtime), so platform metering (debit on `swarm.turn_usage`, settle on a
|
|
1156
|
+
* terminal) can live HERE rather than teeing the route's stream. Awaited but FAIL-SAFE — a throwing observer
|
|
1157
|
+
* is swallowed (the host logs its own errors), NEVER breaking the run, exactly like `telemetry`. Omit ⇒ no-op.
|
|
1158
|
+
*/
|
|
1159
|
+
onEvent?: (ev: SwarmEvent, ctx: SwarmContext) => void | Promise<void>;
|
|
1160
|
+
/**
|
|
1161
|
+
* Completion supervisor (reliability) — an optional host check fired when a turn would END, to decide whether
|
|
1162
|
+
* the user's request was actually SATISFIED. The engine passes the original request + a transcript of what the
|
|
1163
|
+
* run produced; it returns `{ complete, missing? }`. When not complete the engine re-invokes the orchestrator
|
|
1164
|
+
* with a TARGETED nudge built from `missing` (same thread, full context), up to MAX_CONTINUE_NUDGES. If still
|
|
1165
|
+
* incomplete, the run ends `run_failed` stage "incomplete" (retryable) — a clear non-delivery the host can
|
|
1166
|
+
* refund — instead of a silent `done`. FAIL-SAFE: a throwing/rejecting verifier is treated as "complete"
|
|
1167
|
+
* (fail-open — never trap a run in a verify loop). Omit ⇒ the cheap structural "did the root speak last?"
|
|
1168
|
+
* fallback nudge is used instead.
|
|
1169
|
+
*/
|
|
1170
|
+
verifyCompletion?: CompletionVerifier;
|
|
537
1171
|
}
|
|
538
1172
|
declare class SwarmEngine {
|
|
539
1173
|
private opts;
|
|
@@ -541,11 +1175,56 @@ declare class SwarmEngine {
|
|
|
541
1175
|
private rowCache;
|
|
542
1176
|
private memory;
|
|
543
1177
|
private floor;
|
|
1178
|
+
private hooks;
|
|
544
1179
|
constructor(opts: EngineOpts);
|
|
1180
|
+
/** SP1: the swarm's metering config, in the shape DelegateBudgets/priceUsage expect. CostGovernor reads the
|
|
1181
|
+
* same fields directly off `opts.cost`; this packs them for the per-delegate tracker so both caps price
|
|
1182
|
+
* tokens identically (built-in PRICE_TABLE ← static `prices` ← live `priceFeed`, with `failOnUnknownModel`). */
|
|
1183
|
+
private pricingOpts;
|
|
1184
|
+
/** Fire the best-effort per-event observer (`EngineOpts.onEvent`). Awaited so an async observer (e.g. a
|
|
1185
|
+
* metering debit) completes in order, but FAIL-SAFE: a throw is swallowed (the host logs its own), never
|
|
1186
|
+
* breaking the run — same contract as the telemetry exporter. No-op when no observer is configured. */
|
|
1187
|
+
private notifyEvent;
|
|
1188
|
+
/** Run the completion supervisor (`EngineOpts.verifyCompletion`), FAIL-OPEN: no verifier, or a throwing one,
|
|
1189
|
+
* yields `{ complete: true }` so a missing/broken judge never traps a run in a verify loop. */
|
|
1190
|
+
private safeVerify;
|
|
1191
|
+
/** Best-effort recall of the run's ORIGINAL request (first user message on the thread) for the completion
|
|
1192
|
+
* verifier on RESUME, where the engine doesn't hold the opening message. Empty on any failure / no verifier. */
|
|
1193
|
+
private recallRequest;
|
|
545
1194
|
/** Cached agent-row load shared by the three dynamic agent fns AND run/resume. */
|
|
546
1195
|
private loadRow;
|
|
1196
|
+
/** Resolve an agent's STORED modelId — which may be a tier sentinel (`"tier:"` / `"tier:swift"`) — to the
|
|
1197
|
+
* CONCRETE model id the generation actually runs on, so metering/pricing + the preGeneration event see the
|
|
1198
|
+
* real model, not the sentinel (which has no price → every tier-routed turn would meter at $0). Mirrors
|
|
1199
|
+
* mastra-map's modelFor routing; with no tier config it returns the id unchanged. (SP10 pricing follow-up.) */
|
|
1200
|
+
private priceModelId;
|
|
547
1201
|
private agent;
|
|
548
1202
|
private requestContext;
|
|
1203
|
+
/**
|
|
1204
|
+
* SP5 — the action-approval gate handed to every gated tool via the RequestContext. Bound once (stable
|
|
1205
|
+
* reference). Delegates to the dispatcher's `preToolCall`, which is fail-closed (a throwing configured hook ⇒
|
|
1206
|
+
* deny) and applies the non-removable policy. The defineTool wrapper turns the returned `ToolDecision` into:
|
|
1207
|
+
* allow → run; deny → blocked result; ask → suspend-and-ask (the existing `swarm.question`/resume machinery).
|
|
1208
|
+
*/
|
|
1209
|
+
private readonly toolGate;
|
|
1210
|
+
/**
|
|
1211
|
+
* SP5 truth-fix — resolve whether a tool WILL require approval, for the `swarm.tool_call` event's
|
|
1212
|
+
* `needsApproval` (the react reducer reads it to render an approval card). The mapChunk emit currently
|
|
1213
|
+
* hardcodes `false` (the truth-bug). This computes the truthful value from the SAME policy + per-tool flag the
|
|
1214
|
+
* gate uses: the tool's resolved `needsApproval` (its own flag, defaulting by origin) run through the
|
|
1215
|
+
* dispatcher's SYNC `policyDecision` — `ask` ⇒ true (it will gate), else false. The async `preToolCall` hook
|
|
1216
|
+
* can still escalate a specific call at execute time, but the policy-derived baseline is the truthful default
|
|
1217
|
+
* the UI needs without speculatively running the hook for every tool_call event.
|
|
1218
|
+
*/
|
|
1219
|
+
private gatesApproval;
|
|
1220
|
+
/**
|
|
1221
|
+
* SP2: the preGeneration DECISION seam. Awaited immediately before each model launch (run + resume). The
|
|
1222
|
+
* dispatcher is fail-closed (a throwing hook ⇒ deny), so this only ever sees a clean `allow`/`deny`; a `deny`
|
|
1223
|
+
* THROWS `ReserveDenied` so the model call below never happens and the run/resume catch-all maps it to a
|
|
1224
|
+
* terminal `run_failed` stage "reserve" (NOT the generic "exception"). Allow-all + zero-overhead when no
|
|
1225
|
+
* hooks are configured (the default dispatcher returns allow synchronously-ish without invoking anything).
|
|
1226
|
+
*/
|
|
1227
|
+
private guardGeneration;
|
|
549
1228
|
/** Per-call Mastra memory ids + delegation, only when memory is configured (else stream is unchanged). */
|
|
550
1229
|
private memoryOpts;
|
|
551
1230
|
/**
|
|
@@ -606,11 +1285,28 @@ declare class SwarmEngine {
|
|
|
606
1285
|
scratchpadPublic(container: string, ctx: SwarmContext): Promise<ScratchpadEntry[]>;
|
|
607
1286
|
/** In-flight runs (running|suspended) for a container + its lanes — powers cross-lane background presence (E5). */
|
|
608
1287
|
activeRuns(container: string, ctx: SwarmContext): Promise<ActiveRun[]>;
|
|
1288
|
+
/** The full, globally-ordered event log for a thread's CONTAINER (all its runs + lane sub-threads) — lets a host
|
|
1289
|
+
* rebuild the RICH timeline (tool calls + delegation cards) on reload, since message history is text-only.
|
|
1290
|
+
* Returns [] when the store has no events table (`listForContainer` unset). */
|
|
1291
|
+
threadEvents(threadId: string, ctx: SwarmContext): Promise<SwarmEvent[]>;
|
|
609
1292
|
/** The tenant's agent roster (slug, title-cased display name, role, delegate graph) as wall-safe
|
|
610
1293
|
* AgentSummary[]. Sourced from the agent rows; no vendor type in the signature or result. Powers
|
|
611
1294
|
* the multi-agent pile / @mention UI. */
|
|
612
1295
|
listAgents(ctx: SwarmContext): Promise<AgentSummary[]>;
|
|
613
1296
|
run(input: RunInput, ctx: SwarmContext): AsyncIterable<SwarmEvent>;
|
|
1297
|
+
/**
|
|
1298
|
+
* Phase B — drive a STRICT workflow IN PLACE OF the free-form continue-nudge loop. Shared by `run()` (fresh)
|
|
1299
|
+
* and `resume()` (re-entry after a human/approval suspend). An `agent` step reuses `this.agent().stream()`
|
|
1300
|
+
* with a per-step requestContext (agentSlug = the step's agent) so it inherits persona/tools/gate/model/cost;
|
|
1301
|
+
* a `tool` step runs `executeToolWithGate`; a `human`/approval pause suspends SP9-style. Reserve, usage, and
|
|
1302
|
+
* the terminal turn_usage flow through the caller's machinery (`m`). Handles the terminal status/setStatus.
|
|
1303
|
+
*/
|
|
1304
|
+
private driveWorkflow;
|
|
1305
|
+
/** A workflow `agent` step: stream `slug` with `message` (a per-step requestContext so it inherits the agent's
|
|
1306
|
+
* persona/tools/gate/model), reserving + metering through the caller's machinery, returning the final text. */
|
|
1307
|
+
private streamWorkflowAgentStep;
|
|
1308
|
+
/** A workflow `tool` step: run the gate-free tool body through `executeToolWithGate` (the engine-owned gate). */
|
|
1309
|
+
private runWorkflowToolStep;
|
|
614
1310
|
resume(args: {
|
|
615
1311
|
runId: string;
|
|
616
1312
|
toolCallId: string;
|
|
@@ -619,6 +1315,24 @@ declare class SwarmEngine {
|
|
|
619
1315
|
context?: Record<string, unknown>;
|
|
620
1316
|
}, ctx: SwarmContext): AsyncIterable<SwarmEvent>;
|
|
621
1317
|
}
|
|
1318
|
+
/**
|
|
1319
|
+
* SP2: a typed veto thrown when the `preGeneration` decision hook DENIES a model launch. Caught explicitly in
|
|
1320
|
+
* the run/resume catch-all so it maps to a TERMINAL `run_failed` stage `"reserve"` (mirroring the cost cap's
|
|
1321
|
+
* `"cost"` stage) instead of falling through to the generic `"exception"` stage. The model call has NOT
|
|
1322
|
+
* happened when this throws — the seam is BEFORE `stream`/`resumeStream`.
|
|
1323
|
+
*/
|
|
1324
|
+
declare class ReserveDenied extends Error {
|
|
1325
|
+
readonly stage: "reserve";
|
|
1326
|
+
constructor(reason: string);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/** The bound, run-scoped resolver handed to a tool body via `ctx.secrets`. `resolve(ref)` carries no ctx arg —
|
|
1330
|
+
* the run's tenant/auth scope is captured by the binding, so a tool can never resolve another tenant's secret
|
|
1331
|
+
* (the scope comes from the trusted RequestContext, NOT from tool args). Returns `undefined` when no resolver is
|
|
1332
|
+
* configured or the ref is unknown. */
|
|
1333
|
+
interface BoundSecrets {
|
|
1334
|
+
resolve(ref: string): Promise<string | undefined>;
|
|
1335
|
+
}
|
|
622
1336
|
|
|
623
1337
|
interface ToolSpec<I, O> {
|
|
624
1338
|
name: string;
|
|
@@ -633,9 +1347,15 @@ interface SwarmToolContext {
|
|
|
633
1347
|
tenantId: string;
|
|
634
1348
|
userId: string;
|
|
635
1349
|
runId: string;
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
1350
|
+
/**
|
|
1351
|
+
* SP15 — a run-scoped secret resolver (always present; bound to THIS run's tenant/auth ctx). A first-party
|
|
1352
|
+
* tool body calls `await ctx.secrets.resolve(ref)` to fetch a scoped secret value at execution time, mirroring
|
|
1353
|
+
* how the MCP connector resolves a credentialRef. The run's tenant scope is captured by the binding (NOT passed
|
|
1354
|
+
* by the tool), so a tool can never resolve another tenant's secret. Resolves to `undefined` when the swarm has
|
|
1355
|
+
* no SecretResolver configured (no vault) or the ref is unknown — never throws. Optional in the type only for
|
|
1356
|
+
* back-compat with code that constructs a bare ctx; the engine always populates it.
|
|
1357
|
+
*/
|
|
1358
|
+
secrets?: BoundSecrets;
|
|
639
1359
|
}
|
|
640
1360
|
interface SwarmTool {
|
|
641
1361
|
name: string;
|
|
@@ -655,22 +1375,100 @@ interface AgentSpec {
|
|
|
655
1375
|
modelId?: string;
|
|
656
1376
|
/** Per-agent memory OPTIONS override (R9), merged over the swarm `memory` config. Infra stays swarm-wide. */
|
|
657
1377
|
memory?: AgentMemoryOverride;
|
|
1378
|
+
/** Per-agent rules (additive over swarm rules for THIS agent). Engine-local in v1 (not persisted/versioned). */
|
|
1379
|
+
rules?: RuleDef[];
|
|
1380
|
+
/** Per-agent workflow/procedure. Engine-local in v1. A strict one is rejected by `defineSwarm` until Phase B. */
|
|
1381
|
+
workflow?: WorkflowDef;
|
|
658
1382
|
}
|
|
659
1383
|
declare function defineAgent(spec: AgentSpec): AgentDef;
|
|
1384
|
+
/**
|
|
1385
|
+
* Normalize + validate a `RuleSpec` into a `RuleDef`. Plain data (no engine types) — the compiled rule the
|
|
1386
|
+
* engine holds. Validation is compile-time (throws): `enforce` requires an `action`; `ask` is TOOL-SEAM ONLY
|
|
1387
|
+
* (preGeneration is binary allow/deny — it cannot suspend); an `ask` cannot explicitly target a delegation
|
|
1388
|
+
* (`agent-*`) because `gateDelegation` defers `ask` to the sub-agent's inner gates (use `deny`).
|
|
1389
|
+
*/
|
|
1390
|
+
declare function defineRule(spec: RuleSpec): RuleDef;
|
|
1391
|
+
/**
|
|
1392
|
+
* Normalize + validate a `WorkflowSpec` into a `WorkflowDef`. Validation is GRAPH-ONLY (pure code): unique step
|
|
1393
|
+
* ids, exactly one kind per step, `start`/`next`/`to`/`$ref` reference known steps, no cycles. Agent/tool
|
|
1394
|
+
* EXISTENCE is NOT validated here (it's runtime — `defineSwarm` doesn't validate delegate slugs and tenant DB
|
|
1395
|
+
* rows make it impossible; an unknown agent/tool surfaces as a runtime `run_failed` stage "workflow").
|
|
1396
|
+
*/
|
|
1397
|
+
declare function defineWorkflow(spec: WorkflowSpec): WorkflowDef;
|
|
660
1398
|
/** Build a per-swarm skill resolver from the agents' attached skill handles. */
|
|
661
1399
|
declare function buildSkillResolver(agents: AgentDef[]): (name: string) => SwarmSkill | undefined;
|
|
1400
|
+
/**
|
|
1401
|
+
* Compose + CLOSURE-VALIDATE a capability bundle from `defineAgent` outputs (BN0 static composition + BN1 connector
|
|
1402
|
+
* grants). Pure normalizer/validator (no storage, no Mastra) — same posture as `defineRule`/`defineWorkflow`.
|
|
1403
|
+
* Validates, at author time, that the bundle is self-contained:
|
|
1404
|
+
* - every member `skillName` resolves to a first-party **handle** present on the bundle, OR is a declared
|
|
1405
|
+
* **connector grant** for that member (BN1 — connector-backed, materialized per-tenant by the host at runtime);
|
|
1406
|
+
* - every member delegate is a bundle member or a declared `requires` dependency;
|
|
1407
|
+
* - every tool-seam rule ref and every workflow `step.tool` resolves to a handle or any declared grant;
|
|
1408
|
+
* - no workflow step embeds a credential/connection ref.
|
|
1409
|
+
* So a missing handle/grant fails LOUD here, not as a runtime `run_failed`. Connector grants fold their action names
|
|
1410
|
+
* into the granted member's `skillNames` so the host's `connectorTools` resolver grants them by membership at call time.
|
|
1411
|
+
*/
|
|
1412
|
+
declare function defineBundle(spec: BundleSpec): BundleDef;
|
|
1413
|
+
/**
|
|
1414
|
+
* Fold a validated bundle into a `SwarmConfig` (its agents + swarm-scoped rules/workflows) so the result is a
|
|
1415
|
+
* drop-in `defineSwarm` input. Per-agent rules/workflows ride on the merged `AgentDef`s and are collected by
|
|
1416
|
+
* `defineSwarm` exactly as for hand-authored agents — the bundle is a FRONT-END to `defineSwarm`, not a parallel
|
|
1417
|
+
* engine, so it adds no new runtime path. A bundle that re-declares an existing agent slug is a conflict (fail loud).
|
|
1418
|
+
*/
|
|
1419
|
+
declare function mergeBundle(cfg: SwarmConfig, bundle: BundleDef): SwarmConfig;
|
|
1420
|
+
/**
|
|
1421
|
+
* Project a `BundleDef` (in-process, carrying skill HANDLES) into its SERIALIZABLE `BundleVersionContent` for
|
|
1422
|
+
* persistence (BN2): each member becomes its head's `AgentVersionContent` (the `version` + the handles dropped),
|
|
1423
|
+
* and the rules/workflows/connector-grants/deps carry through as the plain data they already are. This is the
|
|
1424
|
+
* bridge from BN0/BN1 (compose in-process) to BN2 (persist + version). The result has `skillNames` but no
|
|
1425
|
+
* handles — re-hydrating it into a live swarm (BN3 apply) needs a host-supplied handle manifest.
|
|
1426
|
+
*/
|
|
1427
|
+
declare function toBundleContent(def: BundleDef): BundleVersionContent;
|
|
662
1428
|
declare const ASK_TOOL_NAME = "ask";
|
|
663
1429
|
interface SwarmConfig {
|
|
664
1430
|
storage: StorageAdapter;
|
|
665
1431
|
agents: AgentDef[];
|
|
1432
|
+
/**
|
|
1433
|
+
* The model allow-list + optional SP10 cheap-model router. `allow` is the per-tenant allow-set every
|
|
1434
|
+
* resolved model (incl. a tier model) must pass. `tier` (optional) enables Swift/Genius routing: a non-pinning
|
|
1435
|
+
* agent (tier-sentinel `modelId`) lands on the cheap `swift` model by default; `genius` is reachable ONLY via
|
|
1436
|
+
* the server-enforced `allowGenius` premium gate (a pack/agent config cannot grant itself Genius). See TierConfig.
|
|
1437
|
+
*/
|
|
666
1438
|
models: {
|
|
667
1439
|
allow: string[];
|
|
1440
|
+
tier?: TierConfig;
|
|
668
1441
|
};
|
|
669
1442
|
modelFactory: (modelId: string, agentSlug?: string) => unknown;
|
|
1443
|
+
/**
|
|
1444
|
+
* PR2 — opt-in connector tools. Build with `materializeConnectors(connectors, backend)` from
|
|
1445
|
+
* `@nightowlsdev/connectors`; an agent gets a connector action only if the action `name` is in its
|
|
1446
|
+
* `skillNames` (i.e. it listed the action among its `skills`). Tenant-scoped + materialized per request.
|
|
1447
|
+
* Omit ⇒ no connector tools.
|
|
1448
|
+
*/
|
|
1449
|
+
connectorTools?: (ctx: SwarmContext) => Promise<SwarmTool[]>;
|
|
1450
|
+
/**
|
|
1451
|
+
* Global per-run caps + metering config (SP1). `prices` statically overrides the built-in PRICE_TABLE;
|
|
1452
|
+
* `priceFeed` is an optional live seam supplying NUMBERS only (engine wall); `failOnUnknownModel` (default
|
|
1453
|
+
* false) makes an unpriced model THROW instead of pricing at $0. `perDelegate` adds optional per-delegate
|
|
1454
|
+
* USD sub-budgets (R5). All metering fields are threaded into the engine's CostGovernor + DelegateBudgets.
|
|
1455
|
+
*
|
|
1456
|
+
* SP9-core — the cap-that-asks (`onCapHit` + `capIncrementUsd`). `onCapHit:"ask"` (DEFAULT "stop") turns a
|
|
1457
|
+
* GLOBAL cost/step-cap hit into a PAUSE-and-ASK ("Budget cap reached — continue?") instead of a terminal
|
|
1458
|
+
* `run_failed`, so a consumer run can be granted more budget mid-task rather than dying. A pack sets this
|
|
1459
|
+
* SERVER-SIDE (it is not a per-agent flag). Resume-approve raises `maxCostUsd` by `capIncrementUsd` (default
|
|
1460
|
+
* = the original `maxCostUsd`) and continues; resume-reject terminally stops (stage "cost"). The per-delegate
|
|
1461
|
+
* cap is unaffected — it stays terminal. Leave `onCapHit` unset for today's behaviour (terminal stop).
|
|
1462
|
+
*/
|
|
670
1463
|
cost: {
|
|
671
1464
|
maxSteps: number;
|
|
672
1465
|
maxCostUsd: number;
|
|
673
1466
|
perDelegate?: PerDelegateBudget;
|
|
1467
|
+
prices?: Record<string, Price>;
|
|
1468
|
+
priceFeed?: PriceFeed;
|
|
1469
|
+
failOnUnknownModel?: boolean;
|
|
1470
|
+
onCapHit?: "stop" | "ask";
|
|
1471
|
+
capIncrementUsd?: number;
|
|
674
1472
|
};
|
|
675
1473
|
/**
|
|
676
1474
|
* Telemetry exporter(s). One or many — many are composed best-effort
|
|
@@ -701,6 +1499,62 @@ interface SwarmConfig {
|
|
|
701
1499
|
/** Opt-in `recall_lane` tool (Part E): lets an agent read a peer agent's lane transcript in the same
|
|
702
1500
|
* conversation. Read-only; reuses the engine's history (best-effort — empty without memory). */
|
|
703
1501
|
recallLane?: boolean;
|
|
1502
|
+
/**
|
|
1503
|
+
* Opt-in decision/observer hooks (SP2). Types-only / engine-free (from `@nightowlsdev/hooks`). Today exposes a
|
|
1504
|
+
* `preGeneration` DECISION hook that the engine AWAITS immediately before EACH model launch (run + resume):
|
|
1505
|
+
* an `allow` proceeds, a `deny` VETOES the generation (terminal `run_failed` stage `"reserve"`, no model call).
|
|
1506
|
+
* Decision hooks are FAIL-CLOSED — a throwing hook is treated as a deny (a billing/safety veto must never
|
|
1507
|
+
* silently allow on error). Omit ⇒ allow-all, identical to prior behaviour with zero overhead. This is the
|
|
1508
|
+
* seam the platform's per-generation billing RESERVE (SP3) plugs into.
|
|
1509
|
+
*/
|
|
1510
|
+
hooks?: SwarmHooks;
|
|
1511
|
+
/**
|
|
1512
|
+
* SP5 — the NON-REMOVABLE action-approval policy (a P0 SAFETY control). Forces human approval on
|
|
1513
|
+
* side-effecting tools regardless of the per-tool `needsApproval` flag, so a consumer pack cannot ship a
|
|
1514
|
+
* `needsApproval:false` $0.50 action that causes $50k of damage (spend caps limit cost, not harm). Two modes:
|
|
1515
|
+
* - `{ mode: "flag" }` (DEFAULT): today's behaviour — only `needsApproval:true` tools gate.
|
|
1516
|
+
* - `{ mode: "all-side-effecting" }`: force-ask EVERY non-read-only tool (every MCP tool + every first-party
|
|
1517
|
+
* tool not on the read-only allowlist), regardless of the per-tool flag. The safe default for an untrusted
|
|
1518
|
+
* consumer pack. Optionally override `readOnly` to customise the exempt set.
|
|
1519
|
+
* Baked into the `hooks` dispatcher, which combines policy + flag + the optional `preToolCall` hook into the
|
|
1520
|
+
* effective decision (allow / deny / ask-the-human). A `preToolCall` hook (in `hooks`) can add a richer gate.
|
|
1521
|
+
*/
|
|
1522
|
+
toolApproval?: ToolApprovalPolicy;
|
|
1523
|
+
/**
|
|
1524
|
+
* SP15 — opt-in secret resolution for first-party tools. Pass a `SecretResolver` (the platform vault,
|
|
1525
|
+
* SP15-platform) and the engine scopes it per-run so a tool body can `await ctx.secrets.resolve(ref)` to fetch
|
|
1526
|
+
* a tenant-scoped secret at execution time — the same security posture as the MCP connector's credentialRef
|
|
1527
|
+
* resolution (resolve at execution, scoped by the live ctx, never from tool args). @mastra-free. Omit for
|
|
1528
|
+
* today's behaviour: `ctx.secrets.resolve(...)` yields `undefined` (no vault).
|
|
1529
|
+
*/
|
|
1530
|
+
secrets?: SecretResolver;
|
|
1531
|
+
/**
|
|
1532
|
+
* SP3 — best-effort per-event OBSERVER, fired by the engine after each event is persisted (run + resume).
|
|
1533
|
+
* Transport-agnostic (sees every event regardless of interactive-SSE vs durable+realtime delivery), so the
|
|
1534
|
+
* platform's metering — debit on `swarm.turn_usage`, settle on a terminal — lives HERE instead of teeing the
|
|
1535
|
+
* route's stream. Awaited but FAIL-SAFE: a throwing observer is swallowed (log your own), never breaking the
|
|
1536
|
+
* run. The `preGeneration` reserve hook (above) + this observer are the two halves of the credit ledger.
|
|
1537
|
+
*/
|
|
1538
|
+
onEvent?: (ev: SwarmEvent, ctx: SwarmContext) => void | Promise<void>;
|
|
1539
|
+
/**
|
|
1540
|
+
* Completion supervisor (reliability) — see EngineOpts.verifyCompletion. When set, the engine asks this at a
|
|
1541
|
+
* turn's end whether the user's request was actually satisfied, nudges the orchestrator with the specific gap
|
|
1542
|
+
* if not, and ends `run_failed:incomplete` (refundable) instead of a silent `done` if it still can't finish.
|
|
1543
|
+
* Omit ⇒ the cheap structural "did the root speak last?" fallback nudge.
|
|
1544
|
+
*/
|
|
1545
|
+
verifyCompletion?: CompletionVerifier;
|
|
1546
|
+
/**
|
|
1547
|
+
* Declarative conditional policy (Phase A). `advise` rules are injected into the system prompt; `enforce`
|
|
1548
|
+
* rules compile into the decision hooks (deny/ask), folding in the non-removable SP5 policy floor. Per-agent
|
|
1549
|
+
* rules are authored via `defineAgent({ rules })` and applied additively.
|
|
1550
|
+
*/
|
|
1551
|
+
rules?: RuleDef[];
|
|
1552
|
+
/**
|
|
1553
|
+
* Authorable procedures (Phase A: `advisory` only — an `advisory` workflow's `description` is injected as a
|
|
1554
|
+
* suggested procedure; `compliance: "strict"` is REJECTED until the Phase-B step-driver). Per-agent
|
|
1555
|
+
* procedures are authored via `defineAgent({ workflow })`.
|
|
1556
|
+
*/
|
|
1557
|
+
workflows?: WorkflowDef[];
|
|
704
1558
|
}
|
|
705
1559
|
interface Swarm {
|
|
706
1560
|
engine: SwarmEngine;
|
|
@@ -748,10 +1602,7 @@ declare class SpanCollector {
|
|
|
748
1602
|
* Close the open generation with this step's usage + its own per-call cost (already priced from
|
|
749
1603
|
* the step usage by the engine). `costUsd` is per-generation — never a cumulative running total.
|
|
750
1604
|
*/
|
|
751
|
-
closeGeneration(usage:
|
|
752
|
-
inputTokens: number;
|
|
753
|
-
outputTokens: number;
|
|
754
|
-
}, costUsd: number): void;
|
|
1605
|
+
closeGeneration(usage: UsageBreakdown, costUsd: number): void;
|
|
755
1606
|
openTool(toolCallId: string, name: string): void;
|
|
756
1607
|
closeTool(toolCallId: string, ok: boolean): void;
|
|
757
1608
|
/**
|
|
@@ -791,7 +1642,7 @@ declare class InMemoryStorage implements StorageAdapter {
|
|
|
791
1642
|
private pads;
|
|
792
1643
|
seedAgent(v: AgentVersion, tenantId?: string): void;
|
|
793
1644
|
recordSuspend(runId: string, tenantId: string, followupId: string, toolCallId: string): void;
|
|
794
|
-
markFollowupAnswered(followupId: string, tenantId: string):
|
|
1645
|
+
markFollowupAnswered(followupId: string, tenantId: string): boolean;
|
|
795
1646
|
/** Test/host helper: read a run row (the RunStore interface is write-mostly). */
|
|
796
1647
|
getRun(runId: string): RunRow | undefined;
|
|
797
1648
|
events: EventStore;
|
|
@@ -806,9 +1657,57 @@ declare function composeSystemPrompt(row: AgentVersion): {
|
|
|
806
1657
|
role: "system";
|
|
807
1658
|
content: string;
|
|
808
1659
|
}[];
|
|
1660
|
+
/**
|
|
1661
|
+
* Render the soft-policy lines for an agent (advise-rule statements + advisory-workflow summaries, from
|
|
1662
|
+
* `softPolicyFor`) as a single system message. Returns `[]` when there are none (zero overhead / no message).
|
|
1663
|
+
*/
|
|
1664
|
+
declare function composePolicyPrompt(lines: string[]): {
|
|
1665
|
+
role: "system";
|
|
1666
|
+
content: string;
|
|
1667
|
+
}[];
|
|
809
1668
|
|
|
810
1669
|
declare const customAuth: (fn: AuthProvider["authenticate"]) => AuthProvider;
|
|
811
1670
|
|
|
1671
|
+
interface RateLimitConfig {
|
|
1672
|
+
/** Window length in seconds. */
|
|
1673
|
+
windowSec: number;
|
|
1674
|
+
/** Max allowed events per window. */
|
|
1675
|
+
max: number;
|
|
1676
|
+
}
|
|
1677
|
+
interface RateLimitState {
|
|
1678
|
+
count: number;
|
|
1679
|
+
windowStartSec: number;
|
|
1680
|
+
}
|
|
1681
|
+
interface RateLimitDecision {
|
|
1682
|
+
allow: boolean;
|
|
1683
|
+
/** Remaining allowance in the current window (0 when denied). */
|
|
1684
|
+
remaining: number;
|
|
1685
|
+
/** Seconds until the window resets (when the count clears). */
|
|
1686
|
+
resetSec: number;
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* Pure fixed-window rate-limit decision. If there's no prior state or the window has elapsed, a fresh window
|
|
1690
|
+
* starts at count 1 (this event). Otherwise the count increments. Allow while count ≤ max. Returns the decision
|
|
1691
|
+
* AND the next state to persist. Fixed-window is chosen for simplicity + O(1) state (one counter); its known
|
|
1692
|
+
* burst-at-boundary tradeoff is acceptable for an abuse backstop (not a billing meter).
|
|
1693
|
+
*/
|
|
1694
|
+
declare function decideFixedWindow(prev: RateLimitState | null, cfg: RateLimitConfig, nowSec: number): {
|
|
1695
|
+
decision: RateLimitDecision;
|
|
1696
|
+
state: RateLimitState;
|
|
1697
|
+
};
|
|
1698
|
+
interface RateLimitStore {
|
|
1699
|
+
/** Record one event for `key` under `cfg` and return the decision. */
|
|
1700
|
+
hit(key: string, cfg: RateLimitConfig, nowSec: number): Promise<RateLimitDecision>;
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1703
|
+
* In-memory fixed-window store — a REAL limiter for a SINGLE instance. Keeps one window per key in a Map and
|
|
1704
|
+
* prunes expired keys opportunistically so memory stays bounded. NOT shared across instances: a horizontally
|
|
1705
|
+
* scaled deploy must back this with Redis/Postgres (same interface) or limits are per-instance.
|
|
1706
|
+
*/
|
|
1707
|
+
declare function createInMemoryRateLimitStore(): RateLimitStore;
|
|
1708
|
+
/** Parse a "max/window" config from env (e.g. "60" with a default window), clamped to sane positive values. */
|
|
1709
|
+
declare function rateConfig(max: number | undefined, windowSec: number, fallbackMax: number): RateLimitConfig;
|
|
1710
|
+
|
|
812
1711
|
declare const VERSION = "0.0.0";
|
|
813
1712
|
|
|
814
|
-
export { ASK_TOOL_NAME, type ActiveRun, type AgentDef, type AgentMemoryOverride, type AgentRepo, type AgentSpec, type AgentSummary, type AgentVersion, type AskField, type AskFieldOption, type AuthContext, type AuthProvider, CapturingExporter, type ContainerFloor, CostGovernor, DelegateBudgets, type EngineOpts, type EventStore, type FloorHolder, GUARDRAILS, InMemoryContainerFloor, InMemoryStorage, type MemoryConfig, type MessageStore, type ModelProvider, type NewRun, PRICE_TABLE, type PerDelegateBudget, type Price, type Release, RowCache, type RunInput, type RunRow, type RunStatus, type RunStore, type Runner, SCRATCHPAD_MAX_ENTRY_CHARS, SCRATCHPAD_MAX_KEYS, type ScratchpadEntry, type ScratchpadStore, type SecretResolver, SpanCollector, type StorageAdapter, type Swarm, type SwarmConfig, type SwarmContext, SwarmEngine, type SwarmEvent, type SwarmMessage, type SwarmSkill, type SwarmSpan, type SwarmTool, type SwarmToolContext, type TelemetryExporter, type ThreadSummary, type ToolSpec, VERSION, allowListModelProvider, buildSkillResolver, composeSystemPrompt, compositeTelemetry, containerFloor, customAuth, customTelemetry, defineAgent, defineSkill, defineSwarm, defineTool, ev, isEvent, resolveTelemetry };
|
|
1713
|
+
export { ASK_TOOL_NAME, type ActiveRun, type AgentDef, type AgentMemoryOverride, AgentMutationForbidden, type AgentRepo, type AgentSpec, type AgentSummary, type AgentVersion, type AgentVersionContent, type AgentVersionInfo, type AskField, type AskFieldOption, type AuthContext, type AuthProvider, type BoundSecrets, type BundleDef, type BundleDep, type BundleRepo, type BundleSpec, type BundleVersion, type BundleVersionContent, type BundleVersionInfo, type BundleWritableRepo, CapturingExporter, type CompletionVerdict, type CompletionVerifier, type ConnectorGrant, type ContainerFloor, CostGovernor, DelegateBudgets, type EngineOpts, type EventStore, type FloorHolder, GUARDRAILS, InMemoryContainerFloor, InMemoryStorage, type MemoryConfig, type MessageStore, type ModelProvider, type ModelRef, type ModelTier, type NewRun, PRICE_TABLE, type PerDelegateBudget, type Price, type PriceFeed, type PricingOpts, type RateLimitConfig, type RateLimitDecision, type RateLimitState, type RateLimitStore, type Release, ReserveDenied, RowCache, type RuleAction, type RuleCondition, type RuleDef, type RuleLevel, type RuleSpec, type RunInput, type RunRow, type RunStatus, type RunStore, type Runner, SCRATCHPAD_MAX_ENTRY_CHARS, SCRATCHPAD_MAX_KEYS, type ScratchpadEntry, type ScratchpadStore, type SecretResolver, type SlugUsage, SpanCollector, type StorageAdapter, type Swarm, type SwarmActor, type SwarmConfig, type SwarmContext, SwarmEngine, type SwarmEvent, type SwarmMessage, type SwarmSkill, type SwarmSpan, type SwarmTool, type SwarmToolContext, type TelemetryExporter, type ThreadSummary, type TierConfig, type TierEscalationContext, type TierResolution, type ToolSpec, type TurnUsage, type UsageBreakdown, type UsageCost, VERSION, type VersionedRepo, type WorkflowCompliance, type WorkflowDef, type WorkflowRef, type WorkflowRunState, type WorkflowSpec, type WorkflowStep, type WorkflowTransition, allowListModelProvider, assertActorMayMutateDefinition, buildSkillResolver, composePolicyPrompt, composeSystemPrompt, compositeTelemetry, containerFloor, createInMemoryRateLimitStore, customAuth, customTelemetry, decideFixedWindow, defineAgent, defineBundle, defineRule, defineSkill, defineSwarm, defineTool, defineWorkflow, ev, isEvent, isTierSentinel, mergeBundle, priceUsage, rateConfig, resolveTelemetry, resolveTier, sumBreakdowns, sumTurnUsage, tierModelId, toBundleContent };
|