@openbox-ai/openbox-mastra-sdk 0.1.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.
Files changed (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +158 -0
  3. package/dist/client/index.d.ts +4 -0
  4. package/dist/client/index.js +2 -0
  5. package/dist/client/index.js.map +1 -0
  6. package/dist/client/openbox-client.d.ts +42 -0
  7. package/dist/client/openbox-client.js +405 -0
  8. package/dist/client/openbox-client.js.map +1 -0
  9. package/dist/config/index.d.ts +5 -0
  10. package/dist/config/index.js +2 -0
  11. package/dist/config/index.js.map +1 -0
  12. package/dist/config/openbox-config.d.ts +54 -0
  13. package/dist/config/openbox-config.js +162 -0
  14. package/dist/config/openbox-config.js.map +1 -0
  15. package/dist/governance/activity-runtime.d.ts +42 -0
  16. package/dist/governance/activity-runtime.js +712 -0
  17. package/dist/governance/activity-runtime.js.map +1 -0
  18. package/dist/governance/approval-registry.d.ts +17 -0
  19. package/dist/governance/approval-registry.js +32 -0
  20. package/dist/governance/approval-registry.js.map +1 -0
  21. package/dist/governance/context.d.ts +16 -0
  22. package/dist/governance/context.js +13 -0
  23. package/dist/governance/context.js.map +1 -0
  24. package/dist/governance/index.d.ts +2 -0
  25. package/dist/governance/index.js +1 -0
  26. package/dist/governance/index.js.map +1 -0
  27. package/dist/index.d.ts +18 -0
  28. package/dist/index.js +8 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/mastra/index.d.ts +16 -0
  31. package/dist/mastra/index.js +5 -0
  32. package/dist/mastra/index.js.map +1 -0
  33. package/dist/mastra/with-openbox.d.ts +30 -0
  34. package/dist/mastra/with-openbox.js +243 -0
  35. package/dist/mastra/with-openbox.js.map +1 -0
  36. package/dist/mastra/wrap-agent.d.ts +14 -0
  37. package/dist/mastra/wrap-agent.js +1744 -0
  38. package/dist/mastra/wrap-agent.js.map +1 -0
  39. package/dist/mastra/wrap-tool.d.ts +18 -0
  40. package/dist/mastra/wrap-tool.js +49 -0
  41. package/dist/mastra/wrap-tool.js.map +1 -0
  42. package/dist/mastra/wrap-workflow.d.ts +14 -0
  43. package/dist/mastra/wrap-workflow.js +386 -0
  44. package/dist/mastra/wrap-workflow.js.map +1 -0
  45. package/dist/otel/index.d.ts +11 -0
  46. package/dist/otel/index.js +2 -0
  47. package/dist/otel/index.js.map +1 -0
  48. package/dist/otel/setup-openbox-opentelemetry.d.ts +38 -0
  49. package/dist/otel/setup-openbox-opentelemetry.js +2249 -0
  50. package/dist/otel/setup-openbox-opentelemetry.js.map +1 -0
  51. package/dist/span/index.d.ts +5 -0
  52. package/dist/span/index.js +2 -0
  53. package/dist/span/index.js.map +1 -0
  54. package/dist/span/openbox-span-processor.d.ts +90 -0
  55. package/dist/span/openbox-span-processor.js +580 -0
  56. package/dist/span/openbox-span-processor.js.map +1 -0
  57. package/dist/types/errors.d.ts +25 -0
  58. package/dist/types/errors.js +40 -0
  59. package/dist/types/errors.js.map +1 -0
  60. package/dist/types/governance-verdict-response.d.ts +57 -0
  61. package/dist/types/governance-verdict-response.js +84 -0
  62. package/dist/types/governance-verdict-response.js.map +1 -0
  63. package/dist/types/guardrails.d.ts +23 -0
  64. package/dist/types/guardrails.js +27 -0
  65. package/dist/types/guardrails.js.map +1 -0
  66. package/dist/types/index.d.ts +6 -0
  67. package/dist/types/index.js +7 -0
  68. package/dist/types/index.js.map +1 -0
  69. package/dist/types/verdict.d.ts +22 -0
  70. package/dist/types/verdict.js +55 -0
  71. package/dist/types/verdict.js.map +1 -0
  72. package/dist/types/workflow-event-type.d.ts +10 -0
  73. package/dist/types/workflow-event-type.js +13 -0
  74. package/dist/types/workflow-event-type.js.map +1 -0
  75. package/dist/types/workflow-span-buffer.d.ts +31 -0
  76. package/dist/types/workflow-span-buffer.js +42 -0
  77. package/dist/types/workflow-span-buffer.js.map +1 -0
  78. package/docs/README.md +66 -0
  79. package/docs/api-reference.md +348 -0
  80. package/docs/approvals-and-guardrails.md +163 -0
  81. package/docs/architecture.md +186 -0
  82. package/docs/configuration.md +214 -0
  83. package/docs/event-model.md +215 -0
  84. package/docs/installation.md +108 -0
  85. package/docs/integration-patterns.md +214 -0
  86. package/docs/security-and-privacy.md +174 -0
  87. package/docs/telemetry.md +196 -0
  88. package/docs/troubleshooting.md +210 -0
  89. package/package.json +136 -0
@@ -0,0 +1,214 @@
1
+ # Integration Patterns
2
+
3
+ This SDK supports three integration patterns:
4
+
5
+ 1. `withOpenBox()` for standard application bootstrap
6
+ 2. manual wrapping with `wrapTool()`, `wrapWorkflow()`, and `wrapAgent()`
7
+ 3. telemetry-only setup with `setupOpenBoxOpenTelemetry()` and optional `traced()`
8
+
9
+ ## Choose The Right Pattern
10
+
11
+ | Pattern | Use it when |
12
+ | --- | --- |
13
+ | `withOpenBox()` | you want one production runtime to govern the whole Mastra instance |
14
+ | manual wrappers | you need explicit startup order or selective adoption |
15
+ | telemetry-only | you want OpenBox span capture without full Mastra wrapping |
16
+
17
+ ## `withOpenBox()`
18
+
19
+ `withOpenBox()` is the recommended integration path for most applications.
20
+
21
+ ```ts
22
+ import { withOpenBox } from "@openbox-ai/openbox-mastra-sdk";
23
+
24
+ const governedMastra = await withOpenBox(mastra, {
25
+ apiKey: process.env.OPENBOX_API_KEY,
26
+ apiUrl: process.env.OPENBOX_URL
27
+ });
28
+ ```
29
+
30
+ What it does:
31
+
32
+ - parses and validates configuration
33
+ - creates an `OpenBoxClient`
34
+ - creates an `OpenBoxSpanProcessor`
35
+ - installs process-wide telemetry
36
+ - wraps current top-level tools, workflows, and agents
37
+ - patches future `addTool()`, `addWorkflow()`, and `addAgent()` calls
38
+ - hydrates agent-local tool and workflow registries where Mastra exposes them
39
+
40
+ ### Idempotency
41
+
42
+ Calling `withOpenBox()` again on the same Mastra instance reuses the existing runtime instead of creating a second one.
43
+
44
+ ### Accepted Targets
45
+
46
+ `withOpenBox()` accepts either:
47
+
48
+ - a Mastra instance
49
+ - an object with a `.mastra` property
50
+
51
+ Example:
52
+
53
+ ```ts
54
+ await withOpenBox({ mastra }, {
55
+ apiKey: process.env.OPENBOX_API_KEY,
56
+ apiUrl: process.env.OPENBOX_URL
57
+ });
58
+ ```
59
+
60
+ ## Manual Wrapping
61
+
62
+ Use manual wiring when you need to control bootstrap order or govern only a subset of components.
63
+
64
+ ```ts
65
+ import {
66
+ OpenBoxClient,
67
+ OpenBoxSpanProcessor,
68
+ parseOpenBoxConfig,
69
+ setupOpenBoxOpenTelemetry,
70
+ wrapAgent,
71
+ wrapTool,
72
+ wrapWorkflow
73
+ } from "@openbox-ai/openbox-mastra-sdk";
74
+
75
+ const config = parseOpenBoxConfig({
76
+ apiKey: process.env.OPENBOX_API_KEY,
77
+ apiUrl: process.env.OPENBOX_URL
78
+ });
79
+
80
+ const client = new OpenBoxClient({
81
+ apiKey: config.apiKey,
82
+ apiUrl: config.apiUrl,
83
+ evaluateMaxRetries: config.evaluateMaxRetries,
84
+ evaluateRetryBaseDelayMs: config.evaluateRetryBaseDelayMs,
85
+ onApiError: config.onApiError,
86
+ timeoutSeconds: config.governanceTimeout
87
+ });
88
+
89
+ const spanProcessor = new OpenBoxSpanProcessor({
90
+ ignoredUrlPrefixes: [config.apiUrl]
91
+ });
92
+
93
+ const telemetry = setupOpenBoxOpenTelemetry({
94
+ captureHttpBodies: config.httpCapture,
95
+ governanceClient: client,
96
+ ignoredUrls: [config.apiUrl],
97
+ instrumentDatabases: config.instrumentDatabases,
98
+ instrumentFileIo: config.instrumentFileIo,
99
+ spanProcessor
100
+ });
101
+
102
+ const governedTool = wrapTool(tool, {
103
+ client,
104
+ config,
105
+ spanProcessor
106
+ });
107
+
108
+ const governedWorkflow = wrapWorkflow(workflow, {
109
+ client,
110
+ config,
111
+ spanProcessor
112
+ });
113
+
114
+ const governedAgent = wrapAgent(agent, {
115
+ client,
116
+ config,
117
+ spanProcessor
118
+ });
119
+
120
+ await telemetry.shutdown();
121
+ ```
122
+
123
+ Use this pattern when:
124
+
125
+ - another subsystem owns telemetry bootstrap order
126
+ - you only want to govern selected tools, workflows, or agents
127
+ - you need a custom `OpenBoxClient` or `OpenBoxSpanProcessor`
128
+
129
+ ## Telemetry-Only Setup
130
+
131
+ If you want telemetry capture without automatic Mastra patching, install the telemetry layer directly:
132
+
133
+ ```ts
134
+ import {
135
+ OpenBoxClient,
136
+ OpenBoxSpanProcessor,
137
+ setupOpenBoxOpenTelemetry,
138
+ traced
139
+ } from "@openbox-ai/openbox-mastra-sdk";
140
+
141
+ const client = new OpenBoxClient({
142
+ apiKey: process.env.OPENBOX_API_KEY!,
143
+ apiUrl: process.env.OPENBOX_URL!
144
+ });
145
+
146
+ const spanProcessor = new OpenBoxSpanProcessor({
147
+ ignoredUrlPrefixes: [process.env.OPENBOX_URL!]
148
+ });
149
+
150
+ const telemetry = setupOpenBoxOpenTelemetry({
151
+ governanceClient: client,
152
+ ignoredUrls: [process.env.OPENBOX_URL!],
153
+ spanProcessor
154
+ });
155
+
156
+ const governedFn = traced(
157
+ async function sendEmail(input: { to: string }) {
158
+ return { delivered: true, to: input.to };
159
+ },
160
+ {
161
+ captureArgs: true,
162
+ captureResult: true,
163
+ module: "email"
164
+ }
165
+ );
166
+
167
+ await telemetry.shutdown();
168
+ ```
169
+
170
+ This pattern is useful when:
171
+
172
+ - orchestration is not fully Mastra-managed
173
+ - you want function-level tracing without wrapping all Mastra boundaries
174
+ - you intend to emit lifecycle events yourself
175
+
176
+ ## Wrapper Behavior Summary
177
+
178
+ ### `wrapTool()`
179
+
180
+ `wrapTool()` adds:
181
+
182
+ - `ActivityStarted` and `ActivityCompleted`
183
+ - verdict enforcement
184
+ - guardrail input and output handling
185
+ - approval suspension or polling
186
+ - telemetry association for work performed during the tool call
187
+
188
+ ### `wrapWorkflow()`
189
+
190
+ `wrapWorkflow()` adds:
191
+
192
+ - `WorkflowStarted`, `WorkflowCompleted`, and `WorkflowFailed`
193
+ - `SignalReceived` on resume
194
+ - governed execution for non-tool workflow steps
195
+
196
+ Tool component steps are intentionally not double-wrapped.
197
+
198
+ ### `wrapAgent()`
199
+
200
+ `wrapAgent()` models an agent run as a workflow-like unit and adds:
201
+
202
+ - workflow lifecycle events for the agent run
203
+ - `SignalReceived` for `user_input`, `resume`, and `agent_output`
204
+ - agent goal propagation
205
+ - agent-only LLM spans routed through `agent_output`
206
+
207
+ ## Bootstrap Guidance
208
+
209
+ For production services:
210
+
211
+ 1. initialize the SDK once during process startup
212
+ 2. keep a reference to the governed Mastra instance or runtime
213
+ 3. shut telemetry down during process termination
214
+ 4. avoid calling `setupOpenBoxOpenTelemetry()` multiple times in the same process unless you intentionally want to replace the active controller
@@ -0,0 +1,174 @@
1
+ # Security And Privacy
2
+
3
+ This SDK is designed for governance-sensitive workloads. The defaults are conservative where transport, capture, and failure behavior matter most.
4
+
5
+ ## Transport Security
6
+
7
+ The SDK rejects insecure non-localhost OpenBox URLs.
8
+
9
+ Allowed:
10
+
11
+ - `https://openbox.example.com`
12
+ - `http://localhost:8086`
13
+ - `http://127.0.0.1:8086`
14
+ - `http://[::1]:8086`
15
+
16
+ Rejected:
17
+
18
+ - `http://openbox.example.com`
19
+
20
+ Reason:
21
+
22
+ - API keys must not be sent over plaintext HTTP outside local development
23
+
24
+ ## API Key Validation
25
+
26
+ API keys must match:
27
+
28
+ - `obx_live_*`
29
+ - `obx_test_*`
30
+
31
+ When `validate` is `true`, startup also verifies the key against OpenBox Core using `/api/v1/auth/validate`.
32
+
33
+ Use `validate: false` only for:
34
+
35
+ - tests
36
+ - mock servers
37
+ - deliberately offline local development
38
+
39
+ ## Capture Boundary
40
+
41
+ The SDK can capture request and response bodies for HTTP telemetry, but it does so inside its own runtime rather than exposing that data as ordinary OTel span attributes.
42
+
43
+ Key properties:
44
+
45
+ - HTTP bodies are not stored as ordinary OTel span attributes
46
+ - bodies and headers are buffered inside the SDK span processor
47
+ - the data is merged into governance payloads only when required
48
+
49
+ This reduces accidental leakage through generic tracing exporters.
50
+
51
+ ## Default Capture Posture
52
+
53
+ | Setting | Default | Why |
54
+ | --- | --- | --- |
55
+ | `httpCapture` | `true` | useful governance and incident context |
56
+ | `instrumentDatabases` | `true` | low-friction visibility into data access |
57
+ | `instrumentFileIo` | `false` | file telemetry can be noisy and sensitive |
58
+
59
+ If your environment is highly sensitive:
60
+
61
+ - consider disabling `httpCapture`
62
+ - enable it selectively in lower environments first
63
+ - review OpenBox retention and policy posture before broad rollout
64
+
65
+ ## Text-Only HTTP Body Capture
66
+
67
+ The SDK only treats text-like content as body-capturable text.
68
+
69
+ Examples:
70
+
71
+ - `text/*`
72
+ - `application/json`
73
+ - `application/xml`
74
+ - `application/javascript`
75
+ - `application/x-www-form-urlencoded`
76
+
77
+ This avoids nonsensical capture of binary payloads.
78
+
79
+ ## File I/O Is Opt-In
80
+
81
+ File instrumentation is disabled by default.
82
+
83
+ When enabled, the SDK still skips common system and binary paths such as:
84
+
85
+ - `/dev/`
86
+ - `/proc/`
87
+ - `/sys/`
88
+ - `__pycache__`
89
+ - `.so`
90
+ - `.dylib`
91
+
92
+ Do not enable file telemetry broadly unless you have a concrete policy or audit requirement for it.
93
+
94
+ ## Ignore Internal Service URLs
95
+
96
+ Always ignore service URLs that should not be governed.
97
+
98
+ Minimum recommendation:
99
+
100
+ - ignore your OpenBox Core URL
101
+
102
+ Why:
103
+
104
+ - prevents the SDK from tracing and governing its own API calls
105
+ - reduces noise
106
+ - avoids governance loops
107
+
108
+ `withOpenBox()` already adds `apiUrl` to the ignored URL set.
109
+
110
+ ## API Failure Policy
111
+
112
+ `onApiError` controls what happens if OpenBox cannot be reached.
113
+
114
+ ### `fail_open`
115
+
116
+ Use when:
117
+
118
+ - service availability is more important than strict governance enforcement
119
+ - governance outages must not stop production traffic
120
+
121
+ Tradeoff:
122
+
123
+ - requests can continue without a live governance decision
124
+
125
+ ### `fail_closed`
126
+
127
+ Use when:
128
+
129
+ - governance enforcement is mandatory
130
+ - ungoverned execution is unacceptable
131
+
132
+ Tradeoff:
133
+
134
+ - OpenBox outages become execution blockers
135
+
136
+ ## Payload Size And Data Minimization
137
+
138
+ The SDK limits large governance payloads through `maxEvaluatePayloadBytes`.
139
+
140
+ When agent completion payloads are too large:
141
+
142
+ - the SDK retries with a compact version
143
+ - then retries with an ultra-minimal version if needed
144
+
145
+ This keeps governance requests bounded without requiring unbounded payload growth.
146
+
147
+ ## Debug Logging
148
+
149
+ `OPENBOX_DEBUG=true` enables summarized request and response logging.
150
+
151
+ It logs:
152
+
153
+ - event type
154
+ - activity and workflow identity
155
+ - presence of inputs, outputs, spans, and errors
156
+ - retry attempts
157
+ - verdict metadata summary
158
+
159
+ It does not try to print full raw governance payloads by default.
160
+
161
+ Recommendation:
162
+
163
+ - enable it in development, staging, and incident response
164
+ - keep it disabled by default in steady-state production unless your logging posture explicitly allows it
165
+
166
+ ## Production Hardening Checklist
167
+
168
+ 1. Use HTTPS for OpenBox Core.
169
+ 2. Keep `validate` enabled in production.
170
+ 3. Keep OpenBox Core ignored in telemetry capture.
171
+ 4. Decide explicitly between `fail_open` and `fail_closed`.
172
+ 5. Enable file I/O capture only if you need it.
173
+ 6. Review policy so hook-triggered telemetry is not mistaken for a second user action.
174
+ 7. Use OpenBox guardrails and retention controls for sensitive prompts or outputs.
@@ -0,0 +1,196 @@
1
+ # Telemetry
2
+
3
+ This SDK uses OpenTelemetry internally, but it does not simply forward raw spans to OpenBox. It captures, buffers, enriches, and normalizes telemetry into governance-ready payloads.
4
+
5
+ ## What The SDK Captures
6
+
7
+ ### HTTP
8
+
9
+ Enabled by default through `httpCapture: true`.
10
+
11
+ The SDK installs:
12
+
13
+ - Node HTTP instrumentation
14
+ - Undici instrumentation
15
+ - fetch patching for request and response body capture
16
+
17
+ This covers:
18
+
19
+ - `fetch`
20
+ - Node `http` and `https`
21
+ - Undici-based clients
22
+
23
+ Captured fields can include:
24
+
25
+ - method
26
+ - URL
27
+ - request headers
28
+ - response headers
29
+ - request body
30
+ - response body
31
+ - status code
32
+
33
+ Only text-like content types are treated as body-capturable text. Binary payloads are not captured as text.
34
+
35
+ ### Databases
36
+
37
+ Enabled by default through `instrumentDatabases: true`.
38
+
39
+ Supported selectors:
40
+
41
+ - `pg`
42
+ - `postgres`
43
+ - `mysql`
44
+ - `mysql2`
45
+ - `mongodb`
46
+ - `mongoose`
47
+ - `redis`
48
+ - `ioredis`
49
+ - `knex`
50
+ - `oracledb`
51
+ - `cassandra`
52
+ - `tedious`
53
+
54
+ If `dbLibraries` is omitted, the SDK enables every supported DB instrumentation it can resolve.
55
+
56
+ ### File I/O
57
+
58
+ Disabled by default through `instrumentFileIo: false`.
59
+
60
+ When enabled, the SDK can emit spans for file operations such as:
61
+
62
+ - open
63
+ - read
64
+ - write
65
+ - readline
66
+ - readlines
67
+ - writelines
68
+ - close
69
+
70
+ Built-in skip patterns include:
71
+
72
+ - `/dev/`
73
+ - `/proc/`
74
+ - `/sys/`
75
+ - `\\?\pipe\`
76
+ - `__pycache__`
77
+ - `.pyc`
78
+ - `.pyo`
79
+ - `.so`
80
+ - `.dylib`
81
+
82
+ Override them with `fileSkipPatterns` when you install telemetry manually.
83
+
84
+ ### Traced Functions
85
+
86
+ You can create explicit function spans with `traced()`:
87
+
88
+ ```ts
89
+ import { traced } from "@openbox-ai/openbox-mastra-sdk";
90
+
91
+ const summarize = traced(
92
+ async function summarize(text: string) {
93
+ return text.slice(0, 120);
94
+ },
95
+ {
96
+ captureArgs: true,
97
+ captureResult: true,
98
+ module: "summary"
99
+ }
100
+ );
101
+ ```
102
+
103
+ Supported options:
104
+
105
+ - `captureArgs`
106
+ - `captureResult`
107
+ - `module`
108
+ - `name`
109
+ - `tracerName`
110
+
111
+ ## How Telemetry Reaches OpenBox
112
+
113
+ The SDK has two telemetry paths:
114
+
115
+ 1. buffered spans attached to later workflow, activity, or signal payloads
116
+ 2. hook-triggered governance payloads sent during execution
117
+
118
+ Hook-triggered payloads are used for internal operational spans such as HTTP, DB, file, and traced-function activity.
119
+
120
+ ## Hook Payload Characteristics
121
+
122
+ Hook payloads:
123
+
124
+ - include `hook_trigger: true`
125
+ - include normalized OpenBox spans under `spans`
126
+ - carry one started or completed span phase per hook event
127
+ - attach to an existing parent workflow, activity, or agent context
128
+
129
+ For agent-only LLM traffic with no separate business activity parent:
130
+
131
+ - spans are queued
132
+ - they are later emitted on `SignalReceived(agent_output)`
133
+
134
+ ## Privacy Boundary
135
+
136
+ Bodies and headers are not stored as ordinary OTel span attributes. Instead:
137
+
138
+ 1. the SDK captures them into its internal span processor
139
+ 2. the SDK merges them into governance payloads when required
140
+ 3. generic OTel exporters are not relied on to carry those bodies
141
+
142
+ This keeps OpenBox-specific governance context separate from generic tracing infrastructure.
143
+
144
+ ## Ignored URLs
145
+
146
+ Always ignore URLs that should not be governed. At minimum, ignore your OpenBox Core URL.
147
+
148
+ `withOpenBox()` already does this automatically by adding `apiUrl` to the ignored URL set.
149
+
150
+ If you install telemetry manually, do the same:
151
+
152
+ ```ts
153
+ const telemetry = setupOpenBoxOpenTelemetry({
154
+ governanceClient: client,
155
+ ignoredUrls: [config.apiUrl],
156
+ spanProcessor
157
+ });
158
+ ```
159
+
160
+ ## Payload Budgeting
161
+
162
+ Agent `WorkflowCompleted` payloads can grow large because they may include:
163
+
164
+ - workflow output
165
+ - model metadata
166
+ - usage metrics
167
+ - buffered spans
168
+
169
+ The SDK handles this by attempting progressively smaller payloads:
170
+
171
+ 1. full payload
172
+ 2. compact payload
173
+ 3. ultra-minimal payload
174
+
175
+ The threshold is controlled by `maxEvaluatePayloadBytes`.
176
+
177
+ ## Operational Recommendations
178
+
179
+ - Keep `httpCapture` enabled unless payload sensitivity or volume makes that unacceptable.
180
+ - Keep `instrumentDatabases` enabled in most environments.
181
+ - Enable `instrumentFileIo` only when you need file-governance visibility.
182
+ - Keep `ignoredUrls` aligned with internal service endpoints that should not be governed.
183
+ - Do not initialize telemetry twice in the same process unless you intentionally want to replace the active controller.
184
+
185
+ ## Common Policy Interaction
186
+
187
+ If policy treats hook-triggered telemetry as a second user action, you can see:
188
+
189
+ - duplicate approvals
190
+ - noisy `http_request`, `db_query`, `file_operation`, or `function_call` rows
191
+ - approval loops while a parent activity is already pending approval
192
+
193
+ Recommended policy behavior:
194
+
195
+ - govern workflow and activity boundary events
196
+ - treat hook-triggered payloads as internal telemetry unless you have a specific reason to gate them directly