watchmyagents 1.2.1 → 1.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 +72 -0
- package/docs/adapters/openai-agents-js.md +230 -0
- package/examples/adapters/capture-fixtures.ts +193 -0
- package/examples/adapters/openai-agents-js-quickstart.ts +118 -0
- package/package.json +23 -1
- package/src/anonymizer.js +48 -0
- package/src/index.js +52 -0
- package/src/logger.js +17 -1
- package/src/openai-agents.js +202 -0
- package/src/shield/decisions.js +12 -2
- package/src/sources/contract.js +31 -1
- package/src/sources/openai-agents-js.d.ts +278 -0
- package/src/sources/openai-agents-js.js +872 -0
package/src/anonymizer.js
CHANGED
|
@@ -156,17 +156,65 @@ export function normalizeToolInput(rawInput, aliases = {}) {
|
|
|
156
156
|
// programmatically reference the contract.
|
|
157
157
|
export { HASHABLE_INPUT_FIELDS };
|
|
158
158
|
|
|
159
|
+
// ── Heuristic field-name patterns (v1.3.1 F-30 Codex audit on v1.3.0) ───
|
|
160
|
+
//
|
|
161
|
+
// HASHABLE_INPUT_FIELDS is the canonical set; adapter authors are also
|
|
162
|
+
// encouraged to call `normalizeToolInput()` to map their native names.
|
|
163
|
+
// But OpenAI Agents SDK customers define their own tool argument names
|
|
164
|
+
// (`endpoint_url`, `shell_cmd`, `requestUrl`, …) and rarely think to
|
|
165
|
+
// register aliases. The result before v1.3.1: those fields silently
|
|
166
|
+
// disappeared from Fortress signals — not a leak (the raw payload stays
|
|
167
|
+
// LOCAL), but a detection blind spot.
|
|
168
|
+
//
|
|
169
|
+
// This heuristic catches the long tail. For each non-canonical input
|
|
170
|
+
// field, if its NAME matches a common suffix/word pattern, hash its
|
|
171
|
+
// value as if it were the corresponding canonical field. Conservative
|
|
172
|
+
// by design: regex anchored to word/suffix boundaries (`_url` matches,
|
|
173
|
+
// `urlsafe` does NOT) so we err toward false positives (hashing a
|
|
174
|
+
// harmless field is fine) over false negatives (missing a sensitive
|
|
175
|
+
// field is the bug we're fixing).
|
|
176
|
+
//
|
|
177
|
+
// Add new patterns here when adapters surface new common names.
|
|
178
|
+
const HEURISTIC_FIELD_PATTERNS = Object.freeze({
|
|
179
|
+
url: /(?:^|[_-])(?:url|uri|endpoint|webhook|address)$/i,
|
|
180
|
+
command: /(?:^|[_-])(?:cmd|command|shell|exec|script)$/i,
|
|
181
|
+
path: /(?:^|[_-])(?:path|file|filename|filepath|dir|folder)$/i,
|
|
182
|
+
query: /(?:^|[_-])(?:query|search|q|prompt|term)$/i,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
function fieldMatchesCanonicalByHeuristic(fieldName, canonical) {
|
|
186
|
+
const pat = HEURISTIC_FIELD_PATTERNS[canonical];
|
|
187
|
+
return pat ? pat.test(fieldName) : false;
|
|
188
|
+
}
|
|
189
|
+
|
|
159
190
|
// ── Single-entry extractor: what hashable IoCs are in this entry? ────────
|
|
160
191
|
|
|
161
192
|
function extractIocs(entry, salt) {
|
|
162
193
|
const out = [];
|
|
163
194
|
if (!entry.input || typeof entry.input !== 'object') return out;
|
|
195
|
+
|
|
196
|
+
// Exact canonical match (original v1.0+ behavior — backwards compat).
|
|
164
197
|
for (const field of HASHABLE_INPUT_FIELDS) {
|
|
165
198
|
const v = entry.input[field];
|
|
166
199
|
if (typeof v === 'string' && v.length > 0) {
|
|
167
200
|
out.push(hashWithSalt(v, salt));
|
|
168
201
|
}
|
|
169
202
|
}
|
|
203
|
+
|
|
204
|
+
// v1.3.1 F-30 — heuristic suffix matching for non-canonical field
|
|
205
|
+
// names common to OpenAI Agents SDK / LangGraph / CrewAI tools. Skip
|
|
206
|
+
// fields already handled above so we never double-hash.
|
|
207
|
+
for (const [key, v] of Object.entries(entry.input)) {
|
|
208
|
+
if (HASHABLE_INPUT_FIELDS.includes(key)) continue;
|
|
209
|
+
if (typeof v !== 'string' || v.length === 0) continue;
|
|
210
|
+
for (const canonical of HASHABLE_INPUT_FIELDS) {
|
|
211
|
+
if (fieldMatchesCanonicalByHeuristic(key, canonical)) {
|
|
212
|
+
out.push(hashWithSalt(v, salt));
|
|
213
|
+
break; // one canonical bucket per field — first match wins
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
170
218
|
return out;
|
|
171
219
|
}
|
|
172
220
|
|
package/src/index.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// WatchMyAgents — main entry point (v1.4 Codex #10).
|
|
2
|
+
//
|
|
3
|
+
// `import { ... } from 'watchmyagents'` resolves here via the package.json
|
|
4
|
+
// `exports['.']` field. For adapter-specific imports (the recommended
|
|
5
|
+
// path for v1.4+), use the sub-entries:
|
|
6
|
+
//
|
|
7
|
+
// import { openaiAgents } from 'watchmyagents/openai-agents';
|
|
8
|
+
//
|
|
9
|
+
// This root entry exposes the most stable cross-adapter primitives:
|
|
10
|
+
// - the source-adapter contract (PROVIDERS, ACTION_TYPES, COMPOSITION_PATTERNS,
|
|
11
|
+
// ENFORCEMENT_MODES, validateWMAAction)
|
|
12
|
+
// - the policy engine + context tracker + decision chain that EVERY
|
|
13
|
+
// adapter shares
|
|
14
|
+
// - the Logger
|
|
15
|
+
//
|
|
16
|
+
// Internal modules (`src/sources/openai-agents-js.js`,
|
|
17
|
+
// `src/shield/*.js`, etc.) remain accessible to advanced consumers via
|
|
18
|
+
// deep imports, but those paths are NOT part of the stable surface;
|
|
19
|
+
// they may move between minor releases. Stick to the sub-entries and
|
|
20
|
+
// this root export for production code.
|
|
21
|
+
|
|
22
|
+
// ── Source-adapter contract ─────────────────────────────────────────
|
|
23
|
+
export {
|
|
24
|
+
PROVIDERS,
|
|
25
|
+
ACTION_TYPES,
|
|
26
|
+
STATUS_VALUES,
|
|
27
|
+
ENFORCEMENT_MODES,
|
|
28
|
+
COMPOSITION_PATTERNS,
|
|
29
|
+
validateWMAAction,
|
|
30
|
+
} from './sources/contract.js';
|
|
31
|
+
|
|
32
|
+
// ── Shield engine (policy eval + context + audit chain) ─────────────
|
|
33
|
+
export {
|
|
34
|
+
evaluate,
|
|
35
|
+
matchesPolicy,
|
|
36
|
+
loadPolicies,
|
|
37
|
+
} from './shield/policy.js';
|
|
38
|
+
|
|
39
|
+
export { createContextTracker, defaultIsError } from './shield/context.js';
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
createDecisionChain,
|
|
43
|
+
verifyDecisionChain,
|
|
44
|
+
buildGenesisMarker,
|
|
45
|
+
newChainId,
|
|
46
|
+
CHAIN_FIELDS,
|
|
47
|
+
} from './shield/decision-chain.js';
|
|
48
|
+
|
|
49
|
+
export { DecisionLogger } from './shield/decisions.js';
|
|
50
|
+
|
|
51
|
+
// ── Logger ──────────────────────────────────────────────────────────
|
|
52
|
+
export { Logger } from './logger.js';
|
package/src/logger.js
CHANGED
|
@@ -33,6 +33,10 @@ const EXPORT_FIELDS = [
|
|
|
33
33
|
'cost_usd', 'model',
|
|
34
34
|
'session_tokens', 'session_cost_usd',
|
|
35
35
|
'status', 'error', 'sequence_number', 'session_id',
|
|
36
|
+
// v1.3.0 — team correlation across cooperating agents. Populated by
|
|
37
|
+
// adapters that observe handoffs (OpenAI Agents SDK, future Claude
|
|
38
|
+
// Code workflow runId). Null when unknown / no team scope.
|
|
39
|
+
'team_id',
|
|
36
40
|
];
|
|
37
41
|
|
|
38
42
|
export class Logger {
|
|
@@ -78,7 +82,15 @@ export class Logger {
|
|
|
78
82
|
const path = this._pathForToday();
|
|
79
83
|
const full = {
|
|
80
84
|
id: e.id || randomUUID(),
|
|
81
|
-
agent_id
|
|
85
|
+
// v1.3.0 — prefer the event's agent_id when present so multi-agent
|
|
86
|
+
// adapters (OpenAI Agents SDK handoffs, future LangGraph subgraphs)
|
|
87
|
+
// can attribute each event to its actual emitter. Falls back to
|
|
88
|
+
// the Logger's constructor agentId for single-agent adapters
|
|
89
|
+
// (Anthropic Managed) where every event from this logger belongs
|
|
90
|
+
// to the same agent. Backwards compatible: existing Anthropic
|
|
91
|
+
// adapter doesn't populate e.agent_id on every yield, so the
|
|
92
|
+
// fallback path keeps its behavior intact.
|
|
93
|
+
agent_id: e.agent_id || this.agentId,
|
|
82
94
|
// PR-C: sub-agent fields. Defaults are honest for solo / root agents.
|
|
83
95
|
// An adapter that detects hierarchy (e.g. OpenAI Agents handoffs)
|
|
84
96
|
// populates these on the event, and the Logger threads them through.
|
|
@@ -107,6 +119,10 @@ export class Logger {
|
|
|
107
119
|
session_id: this.sessionId,
|
|
108
120
|
session_tokens: e.session_tokens ?? null,
|
|
109
121
|
session_cost_usd: e.session_cost_usd ?? null,
|
|
122
|
+
// v1.3.0 — team correlation. Adapters that observe multi-agent
|
|
123
|
+
// groupings (OpenAI Agents SDK handoffs, future Claude Code
|
|
124
|
+
// workflow runId) populate this. Null when unknown.
|
|
125
|
+
team_id: e.team_id ?? null,
|
|
110
126
|
input: e.input ?? null,
|
|
111
127
|
output: e.output ?? null,
|
|
112
128
|
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Public entry point for the OpenAI Agents SDK adapter (v1.4 Codex #1).
|
|
2
|
+
//
|
|
3
|
+
// Customers import the SDK like this:
|
|
4
|
+
//
|
|
5
|
+
// import { openaiAgents } from 'watchmyagents/openai-agents';
|
|
6
|
+
//
|
|
7
|
+
// const wma = openaiAgents({
|
|
8
|
+
// policiesPath: './policies.json',
|
|
9
|
+
// logDir: './watchmyagents-logs',
|
|
10
|
+
// mode: 'enforce', // 'observe' | 'enforce' (default 'enforce')
|
|
11
|
+
// toolInputs: {
|
|
12
|
+
// fetch_url: { endpoint_url: 'url' }, // per-tool aliases
|
|
13
|
+
// shell: { shell_cmd: 'command' },
|
|
14
|
+
// },
|
|
15
|
+
// });
|
|
16
|
+
//
|
|
17
|
+
// const agent = new Agent({
|
|
18
|
+
// name: 'support_bot',
|
|
19
|
+
// tools,
|
|
20
|
+
// toolInputGuardrails: [wma.shield()], // ← Shield
|
|
21
|
+
// });
|
|
22
|
+
//
|
|
23
|
+
// wma.watch(agent); // ← Watch (auto-detects Agent vs Runner)
|
|
24
|
+
// // or:
|
|
25
|
+
// wma.watch(runner);
|
|
26
|
+
//
|
|
27
|
+
// await run(agent, 'help me');
|
|
28
|
+
//
|
|
29
|
+
// Why a factory: it lets the customer configure once (policy path,
|
|
30
|
+
// log dir, mode, tool input aliases) and get back a small object with
|
|
31
|
+
// `shield()` and `watch()` methods that share that config. The pre-v1.4
|
|
32
|
+
// pattern asked customers to import deep paths (
|
|
33
|
+
// `watchmyagents/src/sources/openai-agents-js.js`) and pass options to
|
|
34
|
+
// each call — fragile and verbose. This entry point is the stable
|
|
35
|
+
// surface; the internal module stays free to refactor.
|
|
36
|
+
|
|
37
|
+
import {
|
|
38
|
+
wmaToolInputGuardrail,
|
|
39
|
+
attachWmaWatch,
|
|
40
|
+
attachWmaWatchToAgent,
|
|
41
|
+
adapterMeta,
|
|
42
|
+
} from './sources/openai-agents-js.js';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Build the WMA OpenAI Agents SDK adapter from a single shared config.
|
|
46
|
+
*
|
|
47
|
+
* @param {object} [options]
|
|
48
|
+
* @param {string} [options.policiesPath] Local JSON policy file.
|
|
49
|
+
* @param {object} [options.ruleset] In-memory ruleset.
|
|
50
|
+
* @param {string} [options.logDir] NDJSON log dir.
|
|
51
|
+
* @param {'observe'|'enforce'} [options.mode] v1.4 Codex #2 — explicit
|
|
52
|
+
* mode. `enforce` requires
|
|
53
|
+
* a policy source; `observe`
|
|
54
|
+
* attaches Watch only and
|
|
55
|
+
* refuses to issue a Shield
|
|
56
|
+
* (calling `.shield()` throws
|
|
57
|
+
* so it's impossible to ship
|
|
58
|
+
* a build that looks armed
|
|
59
|
+
* but isn't).
|
|
60
|
+
* @param {object<string, object>} [options.toolInputs]
|
|
61
|
+
* v1.4 Codex #4 — per-tool argument aliases. Maps tool_name →
|
|
62
|
+
* { nativeFieldName: canonicalFieldName }. Applied alongside the
|
|
63
|
+
* F-30 heuristic so customers with off-canonical arg names get
|
|
64
|
+
* exact hashing without relying on the suffix heuristic.
|
|
65
|
+
* @param {boolean} [options.failOpen] Default false (closed).
|
|
66
|
+
* @param {number} [options.maxArgBytes] Default 256 KB.
|
|
67
|
+
* @param {number} [options.maxResultBytes] Default 256 KB.
|
|
68
|
+
* @param {object} [options.tracker] Inject ContextTracker.
|
|
69
|
+
* @param {object} [options.logger] Inject Logger.
|
|
70
|
+
* @param {object} [options.decisionLogger] Inject DecisionLogger.
|
|
71
|
+
* @returns {{
|
|
72
|
+
* shield: () => object,
|
|
73
|
+
* watch: (target: object) => () => void,
|
|
74
|
+
* meta: typeof adapterMeta,
|
|
75
|
+
* }}
|
|
76
|
+
*/
|
|
77
|
+
export function openaiAgents(options = {}) {
|
|
78
|
+
const mode = options.mode || 'enforce';
|
|
79
|
+
if (mode !== 'observe' && mode !== 'enforce') {
|
|
80
|
+
throw new TypeError(
|
|
81
|
+
`openaiAgents: options.mode must be 'observe' or 'enforce', got '${mode}'`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// v1.4 Codex #2 — fail-loud refusal to start in enforce mode without
|
|
86
|
+
// any policy source. Avoids the v1.3.0 footgun where missing policies
|
|
87
|
+
// silently degraded to "allow all" with only a stderr warning. The
|
|
88
|
+
// failure surfaces at config time (not on the first request) so it's
|
|
89
|
+
// impossible to deploy a build that LOOKS armed but isn't.
|
|
90
|
+
if (mode === 'enforce' && options.policiesPath == null && options.ruleset == null) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
"openaiAgents({ mode: 'enforce' }) requires policiesPath or ruleset. " +
|
|
93
|
+
"Either provide one, or switch to { mode: 'observe' } if you only " +
|
|
94
|
+
"want Watch (NDJSON capture) without Shield enforcement.",
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Shared config we thread into both shield() and watch() so the
|
|
99
|
+
// customer doesn't have to repeat it.
|
|
100
|
+
const sharedOptions = {
|
|
101
|
+
policiesPath: options.policiesPath,
|
|
102
|
+
ruleset: options.ruleset,
|
|
103
|
+
logDir: options.logDir,
|
|
104
|
+
sessionId: options.sessionId,
|
|
105
|
+
failOpen: options.failOpen,
|
|
106
|
+
recentWindowSize: options.recentWindowSize,
|
|
107
|
+
getTeamId: options.getTeamId,
|
|
108
|
+
tracker: options.tracker,
|
|
109
|
+
logger: options.logger,
|
|
110
|
+
decisionLogger: options.decisionLogger,
|
|
111
|
+
toolInputs: options.toolInputs,
|
|
112
|
+
maxArgBytes: options.maxArgBytes,
|
|
113
|
+
maxResultBytes: options.maxResultBytes,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return Object.freeze({
|
|
117
|
+
/** Returns the @openai/agents Tool Input Guardrail. In `observe`
|
|
118
|
+
* mode this throws — the customer asked for Watch-only and a
|
|
119
|
+
* guardrail would be misleading. */
|
|
120
|
+
shield() {
|
|
121
|
+
if (mode === 'observe') {
|
|
122
|
+
throw new Error(
|
|
123
|
+
"openaiAgents({ mode: 'observe' }).shield() is not allowed. " +
|
|
124
|
+
"Observe mode does not produce Shield enforcement. " +
|
|
125
|
+
"Switch to { mode: 'enforce', policiesPath: '...' } if you want to block.",
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return wmaToolInputGuardrail(sharedOptions);
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
/** Attaches Watch listeners. Auto-detects whether `target` is an
|
|
132
|
+
* Agent (AgentHooks) or a Runner (RunHooks) by checking method
|
|
133
|
+
* shape and known fields. Returns a detach function. */
|
|
134
|
+
watch(target) {
|
|
135
|
+
return autoAttachWatch(target, sharedOptions);
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
/** Adapter metadata — useful for tooling that wants to introspect
|
|
139
|
+
* what's available (capabilities, peer-dep min version, etc.). */
|
|
140
|
+
meta: adapterMeta,
|
|
141
|
+
|
|
142
|
+
/** The configured mode, exposed for runtime introspection. */
|
|
143
|
+
mode,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// v1.4 Codex #7 — auto-detect Runner vs Agent and dispatch to the
|
|
148
|
+
// matching attach function. The shape difference:
|
|
149
|
+
// - @openai/agents Runner has `.run(agent, input)` AND emits the
|
|
150
|
+
// RunHooks events with the agent as an explicit arg.
|
|
151
|
+
// - @openai/agents Agent has `.name`/`.instructions`/`.tools` AND
|
|
152
|
+
// emits AgentHooks events with the agent IMPLICIT (closure-
|
|
153
|
+
// captured by attachWmaWatchToAgent).
|
|
154
|
+
//
|
|
155
|
+
// Both expose `.on()` from the EventEmitter base. We detect the
|
|
156
|
+
// runner by checking for `.run` (function) — it's the convenience the
|
|
157
|
+
// Runner class exposes that Agent doesn't. As a tiebreaker we look
|
|
158
|
+
// at the presence of `.tools` (Agent-only) and the absence of `.name`
|
|
159
|
+
// is fine as a hint but not load-bearing.
|
|
160
|
+
function autoAttachWatch(target, options) {
|
|
161
|
+
if (!target || typeof target.on !== 'function') {
|
|
162
|
+
throw new TypeError(
|
|
163
|
+
'openaiAgents().watch(target): target must expose .on(event, listener). ' +
|
|
164
|
+
'Pass an @openai/agents Runner or Agent instance.',
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
// Independent signal checks — DO NOT make `looksLikeRunner` exclusive
|
|
168
|
+
// of `looksLikeAgent`. The whole point of the ambiguity check below
|
|
169
|
+
// is to catch targets that satisfy both shapes.
|
|
170
|
+
const hasRunMethod = typeof target.run === 'function';
|
|
171
|
+
const hasAgentShape = Array.isArray(target.tools) && typeof target.name === 'string';
|
|
172
|
+
|
|
173
|
+
if (hasRunMethod && hasAgentShape) {
|
|
174
|
+
throw new TypeError(
|
|
175
|
+
'openaiAgents().watch(target): target looks like both Agent and Runner ' +
|
|
176
|
+
'(has .tools[] AND .name AND .run() method). Disambiguate by calling ' +
|
|
177
|
+
'attachWmaWatch(runner) or attachWmaWatchToAgent(agent) directly.',
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
if (hasAgentShape) {
|
|
181
|
+
return attachWmaWatchToAgent(target, options);
|
|
182
|
+
}
|
|
183
|
+
if (hasRunMethod) {
|
|
184
|
+
return attachWmaWatch(target, options);
|
|
185
|
+
}
|
|
186
|
+
// Neither shape unambiguously identified — fall back to AgentHooks
|
|
187
|
+
// since the convenience `run(agent, ...)` function dispatches
|
|
188
|
+
// AgentHooks, which is the more common path for new customers
|
|
189
|
+
// (validated against real fixtures captured 2026-06-10).
|
|
190
|
+
return attachWmaWatchToAgent(target, options);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Re-export the low-level functions so customers that need fine-grained
|
|
194
|
+
// control (e.g. test scaffolding, advanced multi-runner setups) can
|
|
195
|
+
// still reach them. The factory is the recommended entry point; these
|
|
196
|
+
// are the escape hatches.
|
|
197
|
+
export {
|
|
198
|
+
wmaToolInputGuardrail,
|
|
199
|
+
attachWmaWatch,
|
|
200
|
+
attachWmaWatchToAgent,
|
|
201
|
+
adapterMeta,
|
|
202
|
+
};
|
package/src/shield/decisions.js
CHANGED
|
@@ -15,7 +15,17 @@ import { Logger } from '../logger.js';
|
|
|
15
15
|
import { createDecisionChain, buildGenesisMarker, newChainId } from './decision-chain.js';
|
|
16
16
|
|
|
17
17
|
export class DecisionLogger {
|
|
18
|
-
|
|
18
|
+
// v1.3.1 F-29 (P1 Codex audit on v1.3.0): `provider` is now an
|
|
19
|
+
// explicit constructor option. Before v1.3.1 the provider was
|
|
20
|
+
// hard-coded to `anthropic-managed` in record() — when the OpenAI
|
|
21
|
+
// Agents SDK adapter (shipped v1.3.0) wrote shield_decision rows
|
|
22
|
+
// through this logger, those rows were mis-attributed to Anthropic.
|
|
23
|
+
// Fortress / Guardian forensic surfaces saw OpenAI blocks as
|
|
24
|
+
// Anthropic blocks. Default is preserved at `anthropic-managed` so
|
|
25
|
+
// existing v1.2.x callers behave unchanged; the OpenAI adapter
|
|
26
|
+
// explicitly passes `provider: PROVIDERS.OPENAI_AGENTS`.
|
|
27
|
+
constructor({ logDir, agentId, sessionId, provider }) {
|
|
28
|
+
this._provider = provider || 'anthropic-managed';
|
|
19
29
|
// Each DecisionLogger instance owns a single chain segment. A Shield
|
|
20
30
|
// restart creates a fresh DecisionLogger → fresh genesis. The
|
|
21
31
|
// genesis marker is self-describing (agent + session + start time +
|
|
@@ -53,7 +63,7 @@ export class DecisionLogger {
|
|
|
53
63
|
&& (decision === 'deny' || decision === 'interrupt');
|
|
54
64
|
return this._logger.write({
|
|
55
65
|
action_type: 'shield_decision',
|
|
56
|
-
provider:
|
|
66
|
+
provider: this._provider,
|
|
57
67
|
tool_name: sourceEvent?.name || sourceEvent?.tool_name || null,
|
|
58
68
|
status: enforced ? 'error' : 'ok',
|
|
59
69
|
error: enforced ? message : null,
|
package/src/sources/contract.js
CHANGED
|
@@ -140,9 +140,15 @@ export const COMPOSITION_PATTERNS = Object.freeze({
|
|
|
140
140
|
// name here as they land so consumers can build provider-specific UI.
|
|
141
141
|
export const PROVIDERS = Object.freeze({
|
|
142
142
|
ANTHROPIC_MANAGED: 'anthropic-managed',
|
|
143
|
+
// v1.3.0 (Phase 2.A) — OpenAI Agents SDK (TypeScript/JS).
|
|
144
|
+
// Customer-instrumented (not auto-discovery). Push-model via:
|
|
145
|
+
// - Tool Input Guardrails (Shield enforcement: allow/deny/interrupt)
|
|
146
|
+
// - RunHooks EventEmitter (Watch observability)
|
|
147
|
+
// See `./openai-agents-js.js` and `docs/adapters/openai-agents-js.md`.
|
|
148
|
+
OPENAI_AGENTS: 'openai-agents',
|
|
143
149
|
// Coming next:
|
|
144
|
-
// OPENAI_AGENTS: 'openai-agents',
|
|
145
150
|
// AWS_BEDROCK_AGENTCORE: 'aws-bedrock-agentcore',
|
|
151
|
+
// CLAUDE_CODE: 'claude-code',
|
|
146
152
|
// LANGGRAPH: 'langgraph',
|
|
147
153
|
// CREWAI: 'crewai',
|
|
148
154
|
});
|
|
@@ -190,6 +196,30 @@ export const PROVIDERS = Object.freeze({
|
|
|
190
196
|
// * @property {string|null} agent_name The human-named emitter of this event
|
|
191
197
|
// * (the parent agent OR a sub-agent
|
|
192
198
|
// * running inside the parent's session).
|
|
199
|
+
// *
|
|
200
|
+
// * TEAM CORRELATION (v1.3.0 — Phase 2.A OpenAI Agents SDK + future
|
|
201
|
+
// * Claude Code dynamic workflows):
|
|
202
|
+
// * @property {string|null} team_id Stable identifier shared by every
|
|
203
|
+
// * event in a logical group of
|
|
204
|
+
// * cooperating agents. Sources:
|
|
205
|
+
// * (a) customer override via
|
|
206
|
+
// * WMA_TEAM_ID env var or
|
|
207
|
+
// * programmatic setter,
|
|
208
|
+
// * (b) auto-detected from the
|
|
209
|
+
// * OpenAI Agents SDK
|
|
210
|
+
// * `agent_handoff` event —
|
|
211
|
+
// * all agents in the handoff
|
|
212
|
+
// * chain share the same
|
|
213
|
+
// * team_id,
|
|
214
|
+
// * (c) future: Claude Code dynamic
|
|
215
|
+
// * workflow `runId`.
|
|
216
|
+
// * Drives the Legions UI "Team view"
|
|
217
|
+
// * drill-down. Null when unknown.
|
|
218
|
+
// *
|
|
219
|
+
// * RUNTIME-COMPUTED CONTEXT (v1.2.0 — see src/shield/context.js):
|
|
220
|
+
// * ctx attributes (hour_of_day_utc, recent_error_rate, etc.) are NOT stored
|
|
221
|
+
// * on WMAAction — they're computed at policy eval time and live in the
|
|
222
|
+
// * ContextTracker only.
|
|
193
223
|
// */
|
|
194
224
|
|
|
195
225
|
const REQUIRED_FIELDS = ['id', 'provider', 'agent_id', 'session_id', 'action_type', 'timestamp', 'status'];
|