network-ai 5.12.7 → 5.13.1
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/INTEGRATION_GUIDE.md +2 -2
- package/README.md +67 -11
- package/SKILL.md +9 -6
- package/dist/adapters/anthropic-messages-adapter.d.ts +141 -0
- package/dist/adapters/anthropic-messages-adapter.d.ts.map +1 -0
- package/dist/adapters/anthropic-messages-adapter.js +218 -0
- package/dist/adapters/anthropic-messages-adapter.js.map +1 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +6 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/esm/adapters/anthropic-messages-adapter.js +218 -0
- package/dist/esm/adapters/anthropic-messages-adapter.js.map +1 -0
- package/dist/esm/adapters/index.js +6 -1
- package/dist/esm/adapters/index.js.map +1 -1
- package/dist/esm/index.js +30 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/effort-policy.js +107 -0
- package/dist/esm/lib/effort-policy.js.map +1 -0
- package/dist/esm/lib/fan-out.js +47 -14
- package/dist/esm/lib/fan-out.js.map +1 -1
- package/dist/esm/lib/goal-decomposer.js +79 -10
- package/dist/esm/lib/goal-decomposer.js.map +1 -1
- package/dist/esm/lib/model-budget.js +167 -0
- package/dist/esm/lib/model-budget.js.map +1 -0
- package/dist/esm/lib/model-gateway.js +243 -0
- package/dist/esm/lib/model-gateway.js.map +1 -0
- package/dist/esm/lib/owasp-compliance.js +167 -0
- package/dist/esm/lib/owasp-compliance.js.map +1 -0
- package/dist/esm/lib/retry-budget.js +75 -0
- package/dist/esm/lib/retry-budget.js.map +1 -0
- package/dist/esm/lib/telemetry-provider.js +109 -1
- package/dist/esm/lib/telemetry-provider.js.map +1 -1
- package/dist/esm/lib/thinking-blocks.js +106 -0
- package/dist/esm/lib/thinking-blocks.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/effort-policy.d.ts +88 -0
- package/dist/lib/effort-policy.d.ts.map +1 -0
- package/dist/lib/effort-policy.js +107 -0
- package/dist/lib/effort-policy.js.map +1 -0
- package/dist/lib/fan-out.d.ts +17 -0
- package/dist/lib/fan-out.d.ts.map +1 -1
- package/dist/lib/fan-out.js +47 -14
- package/dist/lib/fan-out.js.map +1 -1
- package/dist/lib/goal-decomposer.d.ts +18 -0
- package/dist/lib/goal-decomposer.d.ts.map +1 -1
- package/dist/lib/goal-decomposer.js +79 -10
- package/dist/lib/goal-decomposer.js.map +1 -1
- package/dist/lib/model-budget.d.ts +144 -0
- package/dist/lib/model-budget.d.ts.map +1 -0
- package/dist/lib/model-budget.js +167 -0
- package/dist/lib/model-budget.js.map +1 -0
- package/dist/lib/model-gateway.d.ts +252 -0
- package/dist/lib/model-gateway.d.ts.map +1 -0
- package/dist/lib/model-gateway.js +243 -0
- package/dist/lib/model-gateway.js.map +1 -0
- package/dist/lib/owasp-compliance.d.ts +57 -0
- package/dist/lib/owasp-compliance.d.ts.map +1 -0
- package/dist/lib/owasp-compliance.js +167 -0
- package/dist/lib/owasp-compliance.js.map +1 -0
- package/dist/lib/retry-budget.d.ts +51 -0
- package/dist/lib/retry-budget.d.ts.map +1 -0
- package/dist/lib/retry-budget.js +75 -0
- package/dist/lib/retry-budget.js.map +1 -0
- package/dist/lib/telemetry-provider.d.ts +72 -0
- package/dist/lib/telemetry-provider.d.ts.map +1 -1
- package/dist/lib/telemetry-provider.js +109 -1
- package/dist/lib/telemetry-provider.js.map +1 -1
- package/dist/lib/thinking-blocks.d.ts +77 -0
- package/dist/lib/thinking-blocks.d.ts.map +1 -0
- package/dist/lib/thinking-blocks.js +106 -0
- package/dist/lib/thinking-blocks.js.map +1 -0
- package/package.json +3 -3
- package/socket.json +8 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GovernedModelGateway — model-interaction lifecycle governance.
|
|
4
|
+
*
|
|
5
|
+
* Absorbs a frontier model's refusal → fallback → billing complexity and
|
|
6
|
+
* presents one governed, budgeted, audited interface. When the primary model
|
|
7
|
+
* declines a request with a classifier refusal (`stop_reason: "refusal"`), the
|
|
8
|
+
* gateway:
|
|
9
|
+
*
|
|
10
|
+
* 1. records the refusal (which classifier `category` fired) to the audit sink,
|
|
11
|
+
* 2. emits a refusal signal to telemetry (a refusal is an HTTP 200, *not* an
|
|
12
|
+
* error — it is invisible to error-rate monitoring),
|
|
13
|
+
* 3. retries on the next model in the fallback chain, redeeming the one-time
|
|
14
|
+
* fallback-credit token so the retry is repriced as a cache read,
|
|
15
|
+
* 4. strips model-specific thinking blocks when it switches models (unless a
|
|
16
|
+
* credit is being redeemed, which requires an exact body match),
|
|
17
|
+
* 5. accounts each attempt's cost against the cross-model budget.
|
|
18
|
+
*
|
|
19
|
+
* This layer is **provider-agnostic**. It operates on a normalized
|
|
20
|
+
* {@link ModelCaller}; the concrete provider binding (e.g. the Anthropic
|
|
21
|
+
* Messages adapter) maps the native API to these shapes. Unlike
|
|
22
|
+
* `AdapterRegistry.fallbackChain` — which fails over on adapter *health*
|
|
23
|
+
* (`CircuitOpenError`) — this gateway falls over on a *classifier refusal*.
|
|
24
|
+
*
|
|
25
|
+
* @module GovernedModelGateway
|
|
26
|
+
* @version 1.0.0
|
|
27
|
+
* @license MIT
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.GovernedModelGateway = void 0;
|
|
31
|
+
exports.isRefusal = isRefusal;
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// HELPERS
|
|
34
|
+
// ============================================================================
|
|
35
|
+
/** Whether a response is a classifier refusal. Branch on this, not on content. */
|
|
36
|
+
function isRefusal(response) {
|
|
37
|
+
return response.stopReason === 'refusal';
|
|
38
|
+
}
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// GATEWAY
|
|
41
|
+
// ============================================================================
|
|
42
|
+
/**
|
|
43
|
+
* Governs the full model-interaction lifecycle: refusal detection, cross-model
|
|
44
|
+
* fallback, fallback-credit redemption, thinking-block handoff, and per-attempt
|
|
45
|
+
* cost accounting — behind one {@link GovernedModelGateway.send} call.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const gateway = new GovernedModelGateway({
|
|
50
|
+
* caller, // provider binding (e.g. Anthropic adapter)
|
|
51
|
+
* primaryModel: 'claude-fable-5',
|
|
52
|
+
* fallbackModels: ['claude-opus-4-8'],
|
|
53
|
+
* budget, audit, telemetry, thinking, effort,
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* const r = await gateway.send({ messages: [{ role: 'user', content: 'hi' }] });
|
|
57
|
+
* console.log(r.servedModel, r.servedByFallback, r.totalCostUsd);
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
class GovernedModelGateway {
|
|
61
|
+
caller;
|
|
62
|
+
primaryModel;
|
|
63
|
+
fallbackModels;
|
|
64
|
+
defaultEffort;
|
|
65
|
+
maxFallbacks;
|
|
66
|
+
allowedFallbacks;
|
|
67
|
+
budget;
|
|
68
|
+
audit;
|
|
69
|
+
telemetry;
|
|
70
|
+
thinking;
|
|
71
|
+
effortPolicy;
|
|
72
|
+
constructor(config) {
|
|
73
|
+
if (typeof config.caller !== 'function') {
|
|
74
|
+
throw new TypeError('GovernedModelGateway: caller must be a function');
|
|
75
|
+
}
|
|
76
|
+
if (!config.primaryModel || typeof config.primaryModel !== 'string') {
|
|
77
|
+
throw new TypeError('GovernedModelGateway: primaryModel must be a non-empty string');
|
|
78
|
+
}
|
|
79
|
+
this.caller = config.caller;
|
|
80
|
+
this.primaryModel = config.primaryModel;
|
|
81
|
+
this.fallbackModels = (config.fallbackModels ?? []).filter((m) => m && m !== config.primaryModel);
|
|
82
|
+
this.defaultEffort = config.defaultEffort;
|
|
83
|
+
this.maxFallbacks = config.maxFallbacks ?? this.fallbackModels.length;
|
|
84
|
+
this.allowedFallbacks = config.allowedFallbacks;
|
|
85
|
+
this.budget = config.budget;
|
|
86
|
+
this.audit = config.audit;
|
|
87
|
+
this.telemetry = config.telemetry;
|
|
88
|
+
this.thinking = config.thinking;
|
|
89
|
+
this.effortPolicy = config.effort;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Send a request through the governed lifecycle.
|
|
93
|
+
*
|
|
94
|
+
* Tries the primary model, then walks the fallback chain on each refusal,
|
|
95
|
+
* redeeming fallback credit and stripping cross-model thinking blocks as it
|
|
96
|
+
* goes. Returns once a model serves the turn or the chain is exhausted.
|
|
97
|
+
*
|
|
98
|
+
* @param request The request. The gateway selects the model(s).
|
|
99
|
+
* @returns A {@link GovernedModelResult} describing every attempt.
|
|
100
|
+
*/
|
|
101
|
+
async send(request) {
|
|
102
|
+
if (!request || !Array.isArray(request.messages)) {
|
|
103
|
+
throw new TypeError('GovernedModelGateway.send: request.messages must be an array');
|
|
104
|
+
}
|
|
105
|
+
const agentId = request.agentId;
|
|
106
|
+
const effort = this.resolveEffort(request.effort, agentId);
|
|
107
|
+
const chain = this.buildChain();
|
|
108
|
+
const attempts = [];
|
|
109
|
+
const categories = [];
|
|
110
|
+
let totalCostUsd;
|
|
111
|
+
let lastResponse;
|
|
112
|
+
let creditToken;
|
|
113
|
+
for (let i = 0; i < chain.length; i++) {
|
|
114
|
+
const model = chain[i];
|
|
115
|
+
const isFallback = i > 0;
|
|
116
|
+
const redeeming = Boolean(creditToken);
|
|
117
|
+
// When redeeming a credit the body must match the refused request exactly,
|
|
118
|
+
// so thinking blocks stay. Otherwise, strip them on a model switch.
|
|
119
|
+
const messages = isFallback && !redeeming && this.thinking
|
|
120
|
+
? this.thinking.stripForModelSwitch(request.messages)
|
|
121
|
+
: request.messages;
|
|
122
|
+
const req = {
|
|
123
|
+
model,
|
|
124
|
+
messages,
|
|
125
|
+
system: request.system,
|
|
126
|
+
maxTokens: request.maxTokens,
|
|
127
|
+
tools: request.tools,
|
|
128
|
+
effort,
|
|
129
|
+
fallbackCreditToken: creditToken,
|
|
130
|
+
metadata: request.metadata,
|
|
131
|
+
};
|
|
132
|
+
const response = await this.caller(req);
|
|
133
|
+
lastResponse = response;
|
|
134
|
+
const refusal = isRefusal(response);
|
|
135
|
+
const category = refusal ? response.refusal?.category ?? null : null;
|
|
136
|
+
// Cost accounting (per attempt — never sum tokens across models).
|
|
137
|
+
let costUsd;
|
|
138
|
+
let budgetAllowed = true;
|
|
139
|
+
if (this.budget) {
|
|
140
|
+
const rec = this.budget.recordAttempt(response.model, response.usage, {
|
|
141
|
+
creditRedeemed: redeeming,
|
|
142
|
+
agentId,
|
|
143
|
+
});
|
|
144
|
+
costUsd = rec.costUsd;
|
|
145
|
+
budgetAllowed = rec.allowed;
|
|
146
|
+
totalCostUsd = (totalCostUsd ?? 0) + rec.costUsd;
|
|
147
|
+
}
|
|
148
|
+
attempts.push({
|
|
149
|
+
model: response.model,
|
|
150
|
+
stopReason: response.stopReason,
|
|
151
|
+
refusalCategory: category,
|
|
152
|
+
usage: response.usage,
|
|
153
|
+
costUsd,
|
|
154
|
+
servedByFallback: isFallback,
|
|
155
|
+
creditRedeemed: redeeming,
|
|
156
|
+
});
|
|
157
|
+
await this.auditAttempt(agentId, response, isFallback, redeeming, costUsd);
|
|
158
|
+
if (!refusal) {
|
|
159
|
+
// A model served the turn.
|
|
160
|
+
if (isFallback) {
|
|
161
|
+
this.telemetry?.recordFallbackServed({
|
|
162
|
+
requestedModel: this.primaryModel,
|
|
163
|
+
servedModel: response.model,
|
|
164
|
+
agentId,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
response,
|
|
169
|
+
servedModel: response.model,
|
|
170
|
+
refused: false,
|
|
171
|
+
servedByFallback: isFallback,
|
|
172
|
+
attempts,
|
|
173
|
+
refusalCategories: categories,
|
|
174
|
+
totalCostUsd,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// Refusal: record the signal (refusal !== error), capture credit, continue.
|
|
178
|
+
categories.push(category);
|
|
179
|
+
this.telemetry?.recordRefusal({ model: response.model, category, agentId });
|
|
180
|
+
creditToken = response.refusal?.fallbackCreditToken ?? undefined;
|
|
181
|
+
if (!budgetAllowed)
|
|
182
|
+
break; // budget exhausted — stop trying fallbacks
|
|
183
|
+
}
|
|
184
|
+
// Every model in the chain refused (or the budget stopped us).
|
|
185
|
+
const finalResponse = lastResponse ?? {
|
|
186
|
+
model: this.primaryModel,
|
|
187
|
+
content: [],
|
|
188
|
+
stopReason: 'refusal',
|
|
189
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
190
|
+
};
|
|
191
|
+
return {
|
|
192
|
+
response: finalResponse,
|
|
193
|
+
servedModel: finalResponse.model,
|
|
194
|
+
refused: true,
|
|
195
|
+
servedByFallback: false,
|
|
196
|
+
attempts,
|
|
197
|
+
refusalCategories: categories,
|
|
198
|
+
totalCostUsd,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/** The ordered model chain: primary followed by permitted fallbacks. */
|
|
202
|
+
buildChain() {
|
|
203
|
+
let fallbacks = this.fallbackModels;
|
|
204
|
+
if (this.allowedFallbacks) {
|
|
205
|
+
const permitted = this.allowedFallbacks[this.primaryModel];
|
|
206
|
+
if (permitted) {
|
|
207
|
+
fallbacks = fallbacks.filter((m) => permitted.includes(m));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return [this.primaryModel, ...fallbacks.slice(0, Math.max(0, this.maxFallbacks))];
|
|
211
|
+
}
|
|
212
|
+
resolveEffort(requested, agentId) {
|
|
213
|
+
if (this.effortPolicy) {
|
|
214
|
+
return this.effortPolicy.resolve(requested ?? this.defaultEffort, { agentId });
|
|
215
|
+
}
|
|
216
|
+
return requested ?? this.defaultEffort;
|
|
217
|
+
}
|
|
218
|
+
async auditAttempt(agentId, response, isFallback, creditRedeemed, costUsd) {
|
|
219
|
+
if (!this.audit)
|
|
220
|
+
return;
|
|
221
|
+
const refusal = isRefusal(response);
|
|
222
|
+
try {
|
|
223
|
+
await this.audit.log({
|
|
224
|
+
event: refusal ? 'model.refusal' : 'model.attempt',
|
|
225
|
+
agentId: agentId ?? null,
|
|
226
|
+
model: response.model,
|
|
227
|
+
stopReason: response.stopReason,
|
|
228
|
+
refusalCategory: refusal ? response.refusal?.category ?? null : null,
|
|
229
|
+
refusalExplanation: refusal ? response.refusal?.explanation ?? null : undefined,
|
|
230
|
+
servedByFallback: isFallback,
|
|
231
|
+
creditRedeemed,
|
|
232
|
+
costUsd: costUsd ?? null,
|
|
233
|
+
usage: response.usage,
|
|
234
|
+
ts: new Date().toISOString(),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// Audit is best-effort — never break the model lifecycle for logging.
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
exports.GovernedModelGateway = GovernedModelGateway;
|
|
243
|
+
//# sourceMappingURL=model-gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-gateway.js","sourceRoot":"","sources":["../../lib/model-gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OWASP Agentic AI Top 10 (2026) — coverage matrix and verifier.
|
|
3
|
+
*
|
|
4
|
+
* Maps each OWASP Agentic risk category to the deterministic Network-AI control
|
|
5
|
+
* that addresses it, and provides an `agt verify`-style coverage report. Unlike
|
|
6
|
+
* the Python-skill MAESTRO/ASI table in `SKILL.md` (which scopes the bundled
|
|
7
|
+
* scripts), this matrix covers the **TypeScript orchestration engine**.
|
|
8
|
+
*
|
|
9
|
+
* The controls referenced here are existing engine features — not aspirational
|
|
10
|
+
* roadmap items. This module is a single source of truth for the README/SKILL
|
|
11
|
+
* compliance section and for programmatic verification in CI.
|
|
12
|
+
*
|
|
13
|
+
* @module OwaspCompliance
|
|
14
|
+
* @version 1.0.0
|
|
15
|
+
* @license MIT
|
|
16
|
+
*/
|
|
17
|
+
/** Coverage state for a single risk. */
|
|
18
|
+
export type CoverageStatus = 'covered' | 'partial' | 'not-applicable';
|
|
19
|
+
/** One OWASP Agentic risk and the controls that address it. */
|
|
20
|
+
export interface OwaspControl {
|
|
21
|
+
/** Risk identifier, e.g. `ASI-01`. */
|
|
22
|
+
id: string;
|
|
23
|
+
/** Risk name. */
|
|
24
|
+
risk: string;
|
|
25
|
+
/** Coverage state. */
|
|
26
|
+
status: CoverageStatus;
|
|
27
|
+
/** Deterministic controls (with the modules that implement them). */
|
|
28
|
+
controls: string[];
|
|
29
|
+
}
|
|
30
|
+
/** Aggregate coverage report from {@link verifyOwaspCoverage}. */
|
|
31
|
+
export interface OwaspCoverageReport {
|
|
32
|
+
total: number;
|
|
33
|
+
covered: number;
|
|
34
|
+
partial: number;
|
|
35
|
+
notApplicable: number;
|
|
36
|
+
controls: OwaspControl[];
|
|
37
|
+
/** `true` when every applicable risk is at least `partial` (no gaps). */
|
|
38
|
+
allAddressed: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The OWASP Agentic AI Top 10 (2026) mapped to Network-AI engine controls.
|
|
42
|
+
*/
|
|
43
|
+
export declare const OWASP_AGENTIC_TOP10_2026: readonly OwaspControl[];
|
|
44
|
+
/**
|
|
45
|
+
* Build a coverage report from a control matrix.
|
|
46
|
+
*
|
|
47
|
+
* @param controls The matrix to verify (defaults to {@link OWASP_AGENTIC_TOP10_2026}).
|
|
48
|
+
* @returns Counts plus `allAddressed` (no `not-applicable`-excluded gaps).
|
|
49
|
+
*/
|
|
50
|
+
export declare function verifyOwaspCoverage(controls?: readonly OwaspControl[]): OwaspCoverageReport;
|
|
51
|
+
/**
|
|
52
|
+
* Render a coverage report as an `agt verify`-style text block.
|
|
53
|
+
*
|
|
54
|
+
* @param report A report from {@link verifyOwaspCoverage}.
|
|
55
|
+
*/
|
|
56
|
+
export declare function formatOwaspReport(report: OwaspCoverageReport): string;
|
|
57
|
+
//# sourceMappingURL=owasp-compliance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owasp-compliance.d.ts","sourceRoot":"","sources":["../../lib/owasp-compliance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,wCAAwC;AACxC,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,gBAAgB,CAAC;AAEtE,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,yEAAyE;IACzE,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,YAAY,EAqG1D,CAAC;AAEH;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,GAAE,SAAS,YAAY,EAA6B,GAC3D,mBAAmB,CAYrB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAYrE"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OWASP Agentic AI Top 10 (2026) — coverage matrix and verifier.
|
|
4
|
+
*
|
|
5
|
+
* Maps each OWASP Agentic risk category to the deterministic Network-AI control
|
|
6
|
+
* that addresses it, and provides an `agt verify`-style coverage report. Unlike
|
|
7
|
+
* the Python-skill MAESTRO/ASI table in `SKILL.md` (which scopes the bundled
|
|
8
|
+
* scripts), this matrix covers the **TypeScript orchestration engine**.
|
|
9
|
+
*
|
|
10
|
+
* The controls referenced here are existing engine features — not aspirational
|
|
11
|
+
* roadmap items. This module is a single source of truth for the README/SKILL
|
|
12
|
+
* compliance section and for programmatic verification in CI.
|
|
13
|
+
*
|
|
14
|
+
* @module OwaspCompliance
|
|
15
|
+
* @version 1.0.0
|
|
16
|
+
* @license MIT
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.OWASP_AGENTIC_TOP10_2026 = void 0;
|
|
20
|
+
exports.verifyOwaspCoverage = verifyOwaspCoverage;
|
|
21
|
+
exports.formatOwaspReport = formatOwaspReport;
|
|
22
|
+
/**
|
|
23
|
+
* The OWASP Agentic AI Top 10 (2026) mapped to Network-AI engine controls.
|
|
24
|
+
*/
|
|
25
|
+
exports.OWASP_AGENTIC_TOP10_2026 = Object.freeze([
|
|
26
|
+
{
|
|
27
|
+
id: 'ASI-01',
|
|
28
|
+
risk: 'Agent Goal Hijack',
|
|
29
|
+
status: 'covered',
|
|
30
|
+
controls: [
|
|
31
|
+
'AuthGuardian weighted permission gating before sensitive resource access (index.ts)',
|
|
32
|
+
'JourneyFSM behavioral control plane with ToolAuthorizationMatrix (lib/fsm-journey.ts)',
|
|
33
|
+
'Scope guard responds directly for simple requests instead of decomposing',
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'ASI-02',
|
|
38
|
+
risk: 'Tool Misuse & Exploitation',
|
|
39
|
+
status: 'covered',
|
|
40
|
+
controls: [
|
|
41
|
+
'AgentRuntime SandboxPolicy: command allow-list, shell:false argv execution (lib/agent-runtime.ts)',
|
|
42
|
+
'ApprovalGate for approval-required operations',
|
|
43
|
+
'AdapterHookManager beforeExecute matcher-based gating (lib/adapter-hooks.ts)',
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'ASI-03',
|
|
48
|
+
risk: 'Identity & Privilege Abuse',
|
|
49
|
+
status: 'covered',
|
|
50
|
+
controls: [
|
|
51
|
+
'SecureTokenManager HMAC-SHA256 / Ed25519 signed tokens with TTL (security.ts)',
|
|
52
|
+
'AuthGuardian trust scoring (trust 30%) keyed to data/trust_levels.json',
|
|
53
|
+
'Grant tokens are advisory-only; platform auth required for destructive actions',
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'ASI-04',
|
|
58
|
+
risk: 'Supply Chain Risks',
|
|
59
|
+
status: 'covered',
|
|
60
|
+
controls: [
|
|
61
|
+
'Single runtime dependency (commander); BYOC adapters add none',
|
|
62
|
+
'socket.json capability declarations + scripts/socket-check.js pre-publish gate',
|
|
63
|
+
'scripts/clawhub-check.js bundle hygiene + scripts/codeql-check.js alert gate',
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'ASI-05',
|
|
68
|
+
risk: 'Unsafe Code Execution',
|
|
69
|
+
status: 'covered',
|
|
70
|
+
controls: [
|
|
71
|
+
'AgentRuntime ShellExecutor: tokenized argv, shell-metacharacter rejection, no /bin/sh -c (lib/agent-runtime.ts)',
|
|
72
|
+
'SourceProtectionError + FileAccessor path-traversal protection',
|
|
73
|
+
'Per-adapter CircuitBreaker bounds blast radius (lib/circuit-breaker.ts)',
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'ASI-06',
|
|
78
|
+
risk: 'Memory & Context Poisoning',
|
|
79
|
+
status: 'covered',
|
|
80
|
+
controls: [
|
|
81
|
+
'LockedBlackboard atomic propose → validate → commit with file-system mutex (lib/locked-blackboard.ts)',
|
|
82
|
+
'Blackboard validator injection-pattern detection on untrusted content',
|
|
83
|
+
'ComplianceMonitor real-time behavior surveillance (lib/compliance-monitor.ts)',
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'ASI-07',
|
|
88
|
+
risk: 'Insecure Inter-Agent Communication',
|
|
89
|
+
status: 'partial',
|
|
90
|
+
controls: [
|
|
91
|
+
'LockedBlackboard file-system mutex for local coordination origin',
|
|
92
|
+
'Signed grant tokens (security.ts) authenticate cross-agent handoffs',
|
|
93
|
+
'Documented boundary: run in a trusted workspace; restrict data/ permissions',
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'ASI-08',
|
|
98
|
+
risk: 'Cascading Failures',
|
|
99
|
+
status: 'covered',
|
|
100
|
+
controls: [
|
|
101
|
+
'CircuitBreaker CLOSED/OPEN/HALF_OPEN per adapter with fallbackChain (lib/circuit-breaker.ts)',
|
|
102
|
+
'FederatedBudget + ModelBudget ceilings bound runaway spend (lib/federated-budget.ts, lib/model-budget.ts)',
|
|
103
|
+
'GovernedModelGateway + RetryBudget contain refusal-driven retry storms (lib/model-gateway.ts, lib/retry-budget.ts)',
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 'ASI-09',
|
|
108
|
+
risk: 'Human-Agent Trust Exploitation',
|
|
109
|
+
status: 'covered',
|
|
110
|
+
controls: [
|
|
111
|
+
'ApprovalGate requires human acknowledgment for high-risk actions (lib/agent-runtime.ts)',
|
|
112
|
+
'AuthGuardian justification scoring (justification 40%) on every grant',
|
|
113
|
+
'SecureAuditLogger tamper-evident trail of grants and state transitions (security.ts)',
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'ASI-10',
|
|
118
|
+
risk: 'Rogue Agents',
|
|
119
|
+
status: 'covered',
|
|
120
|
+
controls: [
|
|
121
|
+
'ComplianceMonitor surveillance flags out-of-policy behavior (lib/compliance-monitor.ts)',
|
|
122
|
+
'CircuitBreaker isolation + budget ceilings act as a kill switch',
|
|
123
|
+
'Every write and state transition logged to data/audit_log.jsonl (security.ts)',
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
]);
|
|
127
|
+
/**
|
|
128
|
+
* Build a coverage report from a control matrix.
|
|
129
|
+
*
|
|
130
|
+
* @param controls The matrix to verify (defaults to {@link OWASP_AGENTIC_TOP10_2026}).
|
|
131
|
+
* @returns Counts plus `allAddressed` (no `not-applicable`-excluded gaps).
|
|
132
|
+
*/
|
|
133
|
+
function verifyOwaspCoverage(controls = exports.OWASP_AGENTIC_TOP10_2026) {
|
|
134
|
+
let covered = 0;
|
|
135
|
+
let partial = 0;
|
|
136
|
+
let notApplicable = 0;
|
|
137
|
+
for (const c of controls) {
|
|
138
|
+
if (c.status === 'covered')
|
|
139
|
+
covered++;
|
|
140
|
+
else if (c.status === 'partial')
|
|
141
|
+
partial++;
|
|
142
|
+
else
|
|
143
|
+
notApplicable++;
|
|
144
|
+
}
|
|
145
|
+
const applicable = controls.filter((c) => c.status !== 'not-applicable');
|
|
146
|
+
const allAddressed = applicable.every((c) => c.status === 'covered' || c.status === 'partial');
|
|
147
|
+
return { total: controls.length, covered, partial, notApplicable, controls: [...controls], allAddressed };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Render a coverage report as an `agt verify`-style text block.
|
|
151
|
+
*
|
|
152
|
+
* @param report A report from {@link verifyOwaspCoverage}.
|
|
153
|
+
*/
|
|
154
|
+
function formatOwaspReport(report) {
|
|
155
|
+
const lines = [];
|
|
156
|
+
lines.push('Network-AI — OWASP Agentic AI Top 10 (2026) Coverage');
|
|
157
|
+
lines.push('─'.repeat(56));
|
|
158
|
+
for (const c of report.controls) {
|
|
159
|
+
const mark = c.status === 'covered' ? '✅' : c.status === 'partial' ? '🟡' : '⚪';
|
|
160
|
+
lines.push(` ${c.id} ${mark} ${c.risk}`);
|
|
161
|
+
}
|
|
162
|
+
lines.push('─'.repeat(56));
|
|
163
|
+
lines.push(` ${report.covered}/${report.total} covered, ${report.partial} partial`);
|
|
164
|
+
lines.push(` ${report.allAddressed ? 'PASS — all risks addressed' : 'FAIL — gaps present'}`);
|
|
165
|
+
return lines.join('\n');
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=owasp-compliance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owasp-compliance.js","sourceRoot":"","sources":["../../lib/owasp-compliance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RetryBudget — per-request retry accounting.
|
|
3
|
+
*
|
|
4
|
+
* Frontier models can refuse independently on the same turn (an orchestrator
|
|
5
|
+
* agent and each of its sub-agents), so retries must be budgeted **per request,
|
|
6
|
+
* not per session**. `RetryBudget` enforces a maximum number of retries for
|
|
7
|
+
* each distinct key (a request id, task id, or sub-agent id) and never shares
|
|
8
|
+
* the allowance across keys — one runaway request cannot starve the others.
|
|
9
|
+
*
|
|
10
|
+
* @module RetryBudget
|
|
11
|
+
* @version 1.0.0
|
|
12
|
+
* @license MIT
|
|
13
|
+
*/
|
|
14
|
+
/** Construction options for {@link RetryBudget}. */
|
|
15
|
+
export interface RetryBudgetOptions {
|
|
16
|
+
/** Maximum retries allowed per key. Must be a non-negative integer. */
|
|
17
|
+
maxPerKey: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Per-key retry allowance.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const budget = new RetryBudget({ maxPerKey: 2 });
|
|
25
|
+
* while (budget.tryConsume(requestId)) {
|
|
26
|
+
* const r = await attempt();
|
|
27
|
+
* if (r.ok) break;
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare class RetryBudget {
|
|
32
|
+
private readonly max;
|
|
33
|
+
private readonly consumed;
|
|
34
|
+
constructor(options: RetryBudgetOptions);
|
|
35
|
+
/**
|
|
36
|
+
* Consume one retry for `key`. Returns `false` (without consuming) once the
|
|
37
|
+
* per-key budget is exhausted.
|
|
38
|
+
*
|
|
39
|
+
* @param key Request/task/sub-agent identifier. Must be a non-empty string.
|
|
40
|
+
*/
|
|
41
|
+
tryConsume(key: string): boolean;
|
|
42
|
+
/** Retries already consumed for `key`. */
|
|
43
|
+
used(key: string): number;
|
|
44
|
+
/** Retries still available for `key`. */
|
|
45
|
+
remaining(key: string): number;
|
|
46
|
+
/** The configured per-key maximum. */
|
|
47
|
+
getMax(): number;
|
|
48
|
+
/** Reset one key, or all keys when `key` is omitted. */
|
|
49
|
+
reset(key?: string): void;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=retry-budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-budget.d.ts","sourceRoot":"","sources":["../../lib/retry-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;gBAE/C,OAAO,EAAE,kBAAkB;IAOvC;;;;;OAKG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAUhC,0CAA0C;IAC1C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAIzB,yCAAyC;IACzC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAI9B,sCAAsC;IACtC,MAAM,IAAI,MAAM;IAIhB,wDAAwD;IACxD,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;CAI1B"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RetryBudget — per-request retry accounting.
|
|
4
|
+
*
|
|
5
|
+
* Frontier models can refuse independently on the same turn (an orchestrator
|
|
6
|
+
* agent and each of its sub-agents), so retries must be budgeted **per request,
|
|
7
|
+
* not per session**. `RetryBudget` enforces a maximum number of retries for
|
|
8
|
+
* each distinct key (a request id, task id, or sub-agent id) and never shares
|
|
9
|
+
* the allowance across keys — one runaway request cannot starve the others.
|
|
10
|
+
*
|
|
11
|
+
* @module RetryBudget
|
|
12
|
+
* @version 1.0.0
|
|
13
|
+
* @license MIT
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.RetryBudget = void 0;
|
|
17
|
+
/**
|
|
18
|
+
* Per-key retry allowance.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const budget = new RetryBudget({ maxPerKey: 2 });
|
|
23
|
+
* while (budget.tryConsume(requestId)) {
|
|
24
|
+
* const r = await attempt();
|
|
25
|
+
* if (r.ok) break;
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
class RetryBudget {
|
|
30
|
+
max;
|
|
31
|
+
consumed = new Map();
|
|
32
|
+
constructor(options) {
|
|
33
|
+
if (typeof options.maxPerKey !== 'number' || options.maxPerKey < 0 || !Number.isInteger(options.maxPerKey)) {
|
|
34
|
+
throw new RangeError('RetryBudget: maxPerKey must be a non-negative integer');
|
|
35
|
+
}
|
|
36
|
+
this.max = options.maxPerKey;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Consume one retry for `key`. Returns `false` (without consuming) once the
|
|
40
|
+
* per-key budget is exhausted.
|
|
41
|
+
*
|
|
42
|
+
* @param key Request/task/sub-agent identifier. Must be a non-empty string.
|
|
43
|
+
*/
|
|
44
|
+
tryConsume(key) {
|
|
45
|
+
if (!key || typeof key !== 'string') {
|
|
46
|
+
throw new TypeError('RetryBudget.tryConsume: key must be a non-empty string');
|
|
47
|
+
}
|
|
48
|
+
const used = this.consumed.get(key) ?? 0;
|
|
49
|
+
if (used >= this.max)
|
|
50
|
+
return false;
|
|
51
|
+
this.consumed.set(key, used + 1);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
/** Retries already consumed for `key`. */
|
|
55
|
+
used(key) {
|
|
56
|
+
return this.consumed.get(key) ?? 0;
|
|
57
|
+
}
|
|
58
|
+
/** Retries still available for `key`. */
|
|
59
|
+
remaining(key) {
|
|
60
|
+
return Math.max(0, this.max - (this.consumed.get(key) ?? 0));
|
|
61
|
+
}
|
|
62
|
+
/** The configured per-key maximum. */
|
|
63
|
+
getMax() {
|
|
64
|
+
return this.max;
|
|
65
|
+
}
|
|
66
|
+
/** Reset one key, or all keys when `key` is omitted. */
|
|
67
|
+
reset(key) {
|
|
68
|
+
if (key === undefined)
|
|
69
|
+
this.consumed.clear();
|
|
70
|
+
else
|
|
71
|
+
this.consumed.delete(key);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.RetryBudget = RetryBudget;
|
|
75
|
+
//# sourceMappingURL=retry-budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-budget.js","sourceRoot":"","sources":["../../lib/retry-budget.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AAQH;;;;;;;;;;;GAWG;AACH,MAAa,WAAW;IACL,GAAG,CAAS;IACZ,QAAQ,GAAwB,IAAI,GAAG,EAAE,CAAC;IAE3D,YAAY,OAA2B;QACrC,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,IAAI,OAAO,CAAC,SAAS,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3G,MAAM,IAAI,UAAU,CAAC,uDAAuD,CAAC,CAAC;QAChF,CAAC;QACD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,GAAW;QACpB,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,SAAS,CAAC,wDAAwD,CAAC,CAAC;QAChF,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO,KAAK,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,0CAA0C;IAC1C,IAAI,CAAC,GAAW;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,yCAAyC;IACzC,SAAS,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,sCAAsC;IACtC,MAAM;QACJ,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;IAED,wDAAwD;IACxD,KAAK,CAAC,GAAY;QAChB,IAAI,GAAG,KAAK,SAAS;YAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;;YACxC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;CACF;AA/CD,kCA+CC"}
|