@openwop/openwop 1.1.1 → 1.1.3

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 (46) hide show
  1. package/README.md +4 -0
  2. package/dist/client.d.ts +80 -1
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +186 -0
  5. package/dist/client.js.map +1 -1
  6. package/dist/cost-attribution.d.ts +49 -0
  7. package/dist/cost-attribution.d.ts.map +1 -0
  8. package/dist/cost-attribution.js +65 -0
  9. package/dist/cost-attribution.js.map +1 -0
  10. package/dist/envelope-directive.d.ts +77 -0
  11. package/dist/envelope-directive.d.ts.map +1 -0
  12. package/dist/envelope-directive.js +89 -0
  13. package/dist/envelope-directive.js.map +1 -0
  14. package/dist/event-helpers.d.ts +95 -0
  15. package/dist/event-helpers.d.ts.map +1 -0
  16. package/dist/event-helpers.js +160 -0
  17. package/dist/event-helpers.js.map +1 -0
  18. package/dist/index.d.ts +14 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +34 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/parse-refusal.d.ts +114 -0
  23. package/dist/parse-refusal.d.ts.map +1 -0
  24. package/dist/parse-refusal.js +216 -0
  25. package/dist/parse-refusal.js.map +1 -0
  26. package/dist/registry-helpers.d.ts +118 -0
  27. package/dist/registry-helpers.d.ts.map +1 -0
  28. package/dist/registry-helpers.js +82 -0
  29. package/dist/registry-helpers.js.map +1 -0
  30. package/dist/types.d.ts +376 -1
  31. package/dist/types.d.ts.map +1 -1
  32. package/dist/types.js.map +1 -1
  33. package/dist/webhook-helpers.d.ts +73 -0
  34. package/dist/webhook-helpers.d.ts.map +1 -0
  35. package/dist/webhook-helpers.js +97 -0
  36. package/dist/webhook-helpers.js.map +1 -0
  37. package/package.json +1 -1
  38. package/src/client.ts +218 -0
  39. package/src/cost-attribution.ts +72 -0
  40. package/src/envelope-directive.ts +110 -0
  41. package/src/event-helpers.ts +238 -0
  42. package/src/index.ts +117 -0
  43. package/src/parse-refusal.ts +311 -0
  44. package/src/registry-helpers.ts +173 -0
  45. package/src/types.ts +424 -0
  46. package/src/webhook-helpers.ts +131 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * envelopeDirective — RFC 0030 §A `reasoning`-field prompt directive synthesis.
3
+ *
4
+ * Hosts that advertise `capabilities.envelopes.reasoning.supported: true`
5
+ * with `promptDirective: "advisory"` or `"mandatory"` inject a system-prompt
6
+ * directive instructing the model to populate the OPTIONAL `reasoning` field
7
+ * on envelope payload schemas that carry it. The directive is informational
8
+ * — hosts MUST NOT reject envelopes where `reasoning` is absent regardless
9
+ * of `promptDirective` strength (RFC 0030 §A).
10
+ *
11
+ * The directive fires only when the envelope's `responseSchema` declares a
12
+ * top-level `reasoning` property. Schemas without `reasoning` (e.g.,
13
+ * `schema.response` per RFC 0030 §A) do NOT receive the directive.
14
+ *
15
+ * Honest separation of concerns:
16
+ * - This module decides WHEN to inject (schema has `reasoning`).
17
+ * - The caller decides WHETHER to inject (read `promptDirective` from
18
+ * the host's discovery advertisement).
19
+ * - The model decides whether to ACTUALLY populate the field (the spec
20
+ * forbids rejecting envelopes where `reasoning` is absent).
21
+ *
22
+ * **Operational note on `"mandatory"`** (per RFC 0030 §A 2026-05-21
23
+ * amendment). Strict-output models may honor the mandatory wording
24
+ * literally and refuse mid-emission when reasoning would be vacuous.
25
+ * Hosts SHOULD prefer `"advisory"` unless empirical testing against the
26
+ * active model class confirms `"mandatory"` doesn't trigger refusals.
27
+ *
28
+ * @see RFCS/0030-envelope-reasoning-and-tier-one-subset.md §A
29
+ * @see spec/v1/ai-envelope.md §"Reasoning field (normative)"
30
+ */
31
+ /**
32
+ * Build the directive string to append to the system prompt, OR `null` if
33
+ * the schema does not declare a top-level `reasoning` property.
34
+ *
35
+ * Callers append the returned string to the existing system prompt with a
36
+ * separating newline. When `strength === 'off'`, callers SHOULD short-circuit
37
+ * before invoking this helper (returning `null` here is treated as "no
38
+ * applicable schema," not "directive disabled").
39
+ *
40
+ * The helper inspects only the top-level `properties.reasoning` slot.
41
+ * Nested `reasoning` fields (e.g., inside an `anyOf` branch's payload) are
42
+ * not auto-detected — vendor-kind authors who want per-branch directives
43
+ * synthesize their own.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import { buildReasoningDirective } from '@openwop/openwop';
48
+ *
49
+ * const directive = buildReasoningDirective(
50
+ * { type: 'object', properties: { reasoning: { type: 'string' }, ... } },
51
+ * 'advisory',
52
+ * );
53
+ * // directive is a string ~80 words; null when schema lacks `reasoning`
54
+ * const systemPrompt = [originalSystemPrompt, schemaHint, directive]
55
+ * .filter((s): s is string => Boolean(s))
56
+ * .join('\n\n');
57
+ * ```
58
+ */
59
+ export function buildReasoningDirective(responseSchema, strength) {
60
+ if (strength === 'off')
61
+ return null;
62
+ if (!responseSchema || typeof responseSchema !== 'object')
63
+ return null;
64
+ const schema = responseSchema;
65
+ const properties = schema.properties;
66
+ if (!properties || typeof properties !== 'object')
67
+ return null;
68
+ const reasoningProp = properties.reasoning;
69
+ if (!reasoningProp || typeof reasoningProp !== 'object')
70
+ return null;
71
+ if (strength === 'mandatory') {
72
+ return [
73
+ 'BEFORE emitting the structured fields, populate the `reasoning` property with your analytical',
74
+ 'process — explain how you derived each structured field, what assumptions you made, and what',
75
+ 'risks or alternative interpretations you considered. The `reasoning` field is REQUIRED in your',
76
+ 'output; do not skip it. (Note: the host accepts envelopes where `reasoning` is absent per',
77
+ 'RFC 0030 §A, but for this dispatch the host expects it populated.)',
78
+ ].join(' ');
79
+ }
80
+ // strength === 'advisory'
81
+ return [
82
+ 'If your response schema declares a `reasoning` property, populate it as the first field with',
83
+ 'your analytical process — explain how you derived each structured field. Per Tam et al. (arXiv',
84
+ "2408.02442), models constrained to strict JSON output suffer reasoning-quality collapse when",
85
+ 'no reasoning slot exists; use this field to think before emitting the structured payload. The',
86
+ 'host accepts envelopes where `reasoning` is absent — populate it when it improves clarity.',
87
+ ].join(' ');
88
+ }
89
+ //# sourceMappingURL=envelope-directive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope-directive.js","sourceRoot":"","sources":["../src/envelope-directive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAoBH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,uBAAuB,CACrC,cAAuB,EACvB,QAAoC;IAEpC,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,CAAC,cAAc,IAAI,OAAO,cAAc,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEvE,MAAM,MAAM,GAAG,cAA0C,CAAC;IAC1D,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IACrC,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE/D,MAAM,aAAa,GAAI,UAAsC,CAAC,SAAS,CAAC;IACxE,IAAI,CAAC,aAAa,IAAI,OAAO,aAAa,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAErE,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QAC7B,OAAO;YACL,+FAA+F;YAC/F,8FAA8F;YAC9F,gGAAgG;YAChG,2FAA2F;YAC3F,oEAAoE;SACrE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACd,CAAC;IAED,0BAA0B;IAC1B,OAAO;QACL,8FAA8F;QAC9F,gGAAgG;QAChG,8FAA8F;QAC9F,+FAA+F;QAC/F,4FAA4F;KAC7F,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Typed helpers for the `agent.*` event family (RFC 0002 §B + RFC 0024).
3
+ *
4
+ * Two layers:
5
+ *
6
+ * 1. **Type guards** (`isAgentReasoned`, `isAgentReasoningDelta`, etc.)
7
+ * — discriminator-based predicates that narrow `RunEventDoc` to a
8
+ * `TypedRunEvent<TPayload>` inside the guarded branch. Use these
9
+ * when you're iterating events yourself (e.g., inside a
10
+ * `for await (const ev of streamEvents(...))` loop) and want
11
+ * compile-time-narrowed access to the payload.
12
+ *
13
+ * 2. **High-level subscription helper** (`subscribeToAgentReasoning`)
14
+ * — fan-outs the `streamEvents()` generator into typed callbacks
15
+ * (`onDelta`, `onClosed`). Composes with the existing
16
+ * `streamEvents` SSE consumer; cleanup via the returned
17
+ * `Unsubscribe` function aborts the underlying fetch.
18
+ *
19
+ * Forward-compat: `RunEventDoc.type` is intentionally `string`-typed
20
+ * (not a closed union) per `COMPATIBILITY.md §2.1` — consumers MUST
21
+ * tolerate unknown event types. The type guards here only narrow when
22
+ * the discriminator AND the required payload fields are present; they
23
+ * return `false` for malformed or unknown events.
24
+ *
25
+ * @see schemas/run-event-payloads.schema.json
26
+ * @see RFCS/0002-agent-identity-and-reasoning-events.md
27
+ * @see RFCS/0024-agent-reasoning-streaming.md
28
+ */
29
+ import { type EventsStreamContext, type EventsStreamOptions } from './sse.js';
30
+ import type { AgentDecidedPayload, AgentHandoffPayload, AgentReasonedPayload, AgentReasoningDeltaPayload, AgentToolCalledPayload, AgentToolReturnedPayload, RunEventDoc, TypedRunEvent } from './types.js';
31
+ /** `agent.reasoned` (RFC 0002 §B). Narrows when `type` matches AND
32
+ * payload carries the required `agentId` + `reasoning` strings. */
33
+ export declare function isAgentReasoned(ev: RunEventDoc): ev is TypedRunEvent<AgentReasonedPayload>;
34
+ /** `agent.reasoning.delta` (RFC 0024). Narrows when `type` matches AND
35
+ * payload carries the required `agentId` + `delta` strings + numeric
36
+ * `sequence`. */
37
+ export declare function isAgentReasoningDelta(ev: RunEventDoc): ev is TypedRunEvent<AgentReasoningDeltaPayload>;
38
+ /** `agent.toolCalled` (RFC 0002 §B). */
39
+ export declare function isAgentToolCalled(ev: RunEventDoc): ev is TypedRunEvent<AgentToolCalledPayload>;
40
+ /** `agent.toolReturned` (RFC 0002 §B). Pairs with `agent.toolCalled`
41
+ * via `callId`; `outcome` and `error` are mutually exclusive but the
42
+ * guard doesn't enforce that (callers inspect after narrowing). */
43
+ export declare function isAgentToolReturned(ev: RunEventDoc): ev is TypedRunEvent<AgentToolReturnedPayload>;
44
+ /** `agent.handoff` (RFC 0002 §B). Note the distinct field names —
45
+ * `fromAgentId` / `toAgentId`, NOT a single `agentId`. */
46
+ export declare function isAgentHandoff(ev: RunEventDoc): ev is TypedRunEvent<AgentHandoffPayload>;
47
+ /** `agent.decided` (RFC 0002 §B). `decision` is `unknown` per the
48
+ * schema (host-specific shape); guard only validates `agentId` +
49
+ * the presence of a `decision` key. */
50
+ export declare function isAgentDecided(ev: RunEventDoc): ev is TypedRunEvent<AgentDecidedPayload>;
51
+ /** Returned by {@link subscribeToAgentReasoning}. Call to cancel the
52
+ * underlying SSE subscription. Idempotent — repeated calls are no-ops. */
53
+ export type Unsubscribe = () => void;
54
+ /** Per-callback signatures for {@link subscribeToAgentReasoning}. All
55
+ * callbacks are optional; the helper only invokes those provided.
56
+ * Callback exceptions are caught + reported via `onError` (when
57
+ * provided) so one bad handler doesn't tear down the stream. */
58
+ export interface AgentReasoningCallbacks {
59
+ /** Fired for each `agent.reasoning.delta` event in arrival order
60
+ * (RFC 0024). `sequence` starts at 0 and increments by 1 per delta
61
+ * within a single reasoning block. */
62
+ onDelta?: (payload: AgentReasoningDeltaPayload, ev: TypedRunEvent<AgentReasoningDeltaPayload>) => void | Promise<void>;
63
+ /** Fired once per closed reasoning block with the full
64
+ * authoritative content. Consumers that only care about the final
65
+ * trace can subscribe just to this. */
66
+ onClosed?: (payload: AgentReasonedPayload, ev: TypedRunEvent<AgentReasonedPayload>) => void | Promise<void>;
67
+ /** Fired when the SSE stream ends cleanly (server closed or run
68
+ * reached terminal status). */
69
+ onEnd?: () => void;
70
+ /** Fired when the stream fails OR a callback throws. The helper
71
+ * catches handler exceptions so a thrown error in `onDelta` doesn't
72
+ * tear down the whole subscription; the original error is surfaced
73
+ * here. */
74
+ onError?: (err: Error) => void;
75
+ }
76
+ /**
77
+ * Subscribe to the reasoning event sub-stream for a single run.
78
+ * Convenience wrapper over `streamEvents()` that dispatches the two
79
+ * RFC 0024 event types into typed callbacks.
80
+ *
81
+ * Returns immediately; the SSE consumption runs as a detached async
82
+ * task in the background. Call the returned `Unsubscribe` to cancel.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const stop = subscribeToAgentReasoning(ctx, runId, {
87
+ * onDelta: ({ delta, sequence }) => process.stdout.write(delta),
88
+ * onClosed: ({ reasoning }) => console.log('\nfinal:', reasoning),
89
+ * });
90
+ * // ...later
91
+ * stop();
92
+ * ```
93
+ */
94
+ export declare function subscribeToAgentReasoning(ctx: EventsStreamContext, runId: string, callbacks: AgentReasoningCallbacks, options?: Omit<EventsStreamOptions, 'signal'>): Unsubscribe;
95
+ //# sourceMappingURL=event-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-helpers.d.ts","sourceRoot":"","sources":["../src/event-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAgB,KAAK,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC5F,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,WAAW,EACX,aAAa,EACd,MAAM,YAAY,CAAC;AAepB;oEACoE;AACpE,wBAAgB,eAAe,CAC7B,EAAE,EAAE,WAAW,GACd,EAAE,IAAI,aAAa,CAAC,oBAAoB,CAAC,CAM3C;AAED;;kBAEkB;AAClB,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,WAAW,GACd,EAAE,IAAI,aAAa,CAAC,0BAA0B,CAAC,CAMjD;AAED,wCAAwC;AACxC,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,WAAW,GACd,EAAE,IAAI,aAAa,CAAC,sBAAsB,CAAC,CAO7C;AAED;;oEAEoE;AACpE,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,WAAW,GACd,EAAE,IAAI,aAAa,CAAC,wBAAwB,CAAC,CAO/C;AAED;2DAC2D;AAC3D,wBAAgB,cAAc,CAC5B,EAAE,EAAE,WAAW,GACd,EAAE,IAAI,aAAa,CAAC,mBAAmB,CAAC,CAM1C;AAED;;wCAEwC;AACxC,wBAAgB,cAAc,CAC5B,EAAE,EAAE,WAAW,GACd,EAAE,IAAI,aAAa,CAAC,mBAAmB,CAAC,CAQ1C;AAID;2EAC2E;AAC3E,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC;;;iEAGiE;AACjE,MAAM,WAAW,uBAAuB;IACtC;;2CAEuC;IACvC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,EAAE,EAAE,EAAE,aAAa,CAAC,0BAA0B,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvH;;4CAEwC;IACxC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,EAAE,EAAE,aAAa,CAAC,oBAAoB,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5G;oCACgC;IAChC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB;;;gBAGY;IACZ,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,mBAAmB,EACxB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,uBAAuB,EAClC,OAAO,GAAE,IAAI,CAAC,mBAAmB,EAAE,QAAQ,CAAM,GAChD,WAAW,CAqDb"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Typed helpers for the `agent.*` event family (RFC 0002 §B + RFC 0024).
3
+ *
4
+ * Two layers:
5
+ *
6
+ * 1. **Type guards** (`isAgentReasoned`, `isAgentReasoningDelta`, etc.)
7
+ * — discriminator-based predicates that narrow `RunEventDoc` to a
8
+ * `TypedRunEvent<TPayload>` inside the guarded branch. Use these
9
+ * when you're iterating events yourself (e.g., inside a
10
+ * `for await (const ev of streamEvents(...))` loop) and want
11
+ * compile-time-narrowed access to the payload.
12
+ *
13
+ * 2. **High-level subscription helper** (`subscribeToAgentReasoning`)
14
+ * — fan-outs the `streamEvents()` generator into typed callbacks
15
+ * (`onDelta`, `onClosed`). Composes with the existing
16
+ * `streamEvents` SSE consumer; cleanup via the returned
17
+ * `Unsubscribe` function aborts the underlying fetch.
18
+ *
19
+ * Forward-compat: `RunEventDoc.type` is intentionally `string`-typed
20
+ * (not a closed union) per `COMPATIBILITY.md §2.1` — consumers MUST
21
+ * tolerate unknown event types. The type guards here only narrow when
22
+ * the discriminator AND the required payload fields are present; they
23
+ * return `false` for malformed or unknown events.
24
+ *
25
+ * @see schemas/run-event-payloads.schema.json
26
+ * @see RFCS/0002-agent-identity-and-reasoning-events.md
27
+ * @see RFCS/0024-agent-reasoning-streaming.md
28
+ */
29
+ import { streamEvents } from './sse.js';
30
+ // ─── Type guards ────────────────────────────────────────────────────────
31
+ function hasStringField(payload, field) {
32
+ return (payload !== null &&
33
+ typeof payload === 'object' &&
34
+ typeof payload[field] === 'string');
35
+ }
36
+ /** `agent.reasoned` (RFC 0002 §B). Narrows when `type` matches AND
37
+ * payload carries the required `agentId` + `reasoning` strings. */
38
+ export function isAgentReasoned(ev) {
39
+ return (ev.type === 'agent.reasoned' &&
40
+ hasStringField(ev.payload, 'agentId') &&
41
+ hasStringField(ev.payload, 'reasoning'));
42
+ }
43
+ /** `agent.reasoning.delta` (RFC 0024). Narrows when `type` matches AND
44
+ * payload carries the required `agentId` + `delta` strings + numeric
45
+ * `sequence`. */
46
+ export function isAgentReasoningDelta(ev) {
47
+ if (ev.type !== 'agent.reasoning.delta')
48
+ return false;
49
+ if (!hasStringField(ev.payload, 'agentId'))
50
+ return false;
51
+ if (!hasStringField(ev.payload, 'delta'))
52
+ return false;
53
+ const seq = ev.payload.sequence;
54
+ return typeof seq === 'number' && Number.isInteger(seq) && seq >= 0;
55
+ }
56
+ /** `agent.toolCalled` (RFC 0002 §B). */
57
+ export function isAgentToolCalled(ev) {
58
+ return (ev.type === 'agent.toolCalled' &&
59
+ hasStringField(ev.payload, 'agentId') &&
60
+ hasStringField(ev.payload, 'toolName') &&
61
+ hasStringField(ev.payload, 'callId'));
62
+ }
63
+ /** `agent.toolReturned` (RFC 0002 §B). Pairs with `agent.toolCalled`
64
+ * via `callId`; `outcome` and `error` are mutually exclusive but the
65
+ * guard doesn't enforce that (callers inspect after narrowing). */
66
+ export function isAgentToolReturned(ev) {
67
+ return (ev.type === 'agent.toolReturned' &&
68
+ hasStringField(ev.payload, 'agentId') &&
69
+ hasStringField(ev.payload, 'toolName') &&
70
+ hasStringField(ev.payload, 'callId'));
71
+ }
72
+ /** `agent.handoff` (RFC 0002 §B). Note the distinct field names —
73
+ * `fromAgentId` / `toAgentId`, NOT a single `agentId`. */
74
+ export function isAgentHandoff(ev) {
75
+ return (ev.type === 'agent.handoff' &&
76
+ hasStringField(ev.payload, 'fromAgentId') &&
77
+ hasStringField(ev.payload, 'toAgentId'));
78
+ }
79
+ /** `agent.decided` (RFC 0002 §B). `decision` is `unknown` per the
80
+ * schema (host-specific shape); guard only validates `agentId` +
81
+ * the presence of a `decision` key. */
82
+ export function isAgentDecided(ev) {
83
+ return (ev.type === 'agent.decided' &&
84
+ hasStringField(ev.payload, 'agentId') &&
85
+ ev.payload !== null &&
86
+ typeof ev.payload === 'object' &&
87
+ 'decision' in ev.payload);
88
+ }
89
+ /**
90
+ * Subscribe to the reasoning event sub-stream for a single run.
91
+ * Convenience wrapper over `streamEvents()` that dispatches the two
92
+ * RFC 0024 event types into typed callbacks.
93
+ *
94
+ * Returns immediately; the SSE consumption runs as a detached async
95
+ * task in the background. Call the returned `Unsubscribe` to cancel.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const stop = subscribeToAgentReasoning(ctx, runId, {
100
+ * onDelta: ({ delta, sequence }) => process.stdout.write(delta),
101
+ * onClosed: ({ reasoning }) => console.log('\nfinal:', reasoning),
102
+ * });
103
+ * // ...later
104
+ * stop();
105
+ * ```
106
+ */
107
+ export function subscribeToAgentReasoning(ctx, runId, callbacks, options = {}) {
108
+ const abort = new AbortController();
109
+ let stopped = false;
110
+ const stop = () => {
111
+ if (stopped)
112
+ return;
113
+ stopped = true;
114
+ abort.abort();
115
+ };
116
+ void (async () => {
117
+ try {
118
+ // streamModes default to 'updates' — agent.* events flow there
119
+ // per `spec/v1/stream-modes.md`.
120
+ const events = streamEvents(ctx, runId, {
121
+ ...options,
122
+ streamMode: options.streamMode ?? 'updates',
123
+ signal: abort.signal,
124
+ });
125
+ for await (const ev of events) {
126
+ if (stopped)
127
+ break;
128
+ try {
129
+ if (isAgentReasoningDelta(ev)) {
130
+ await callbacks.onDelta?.(ev.payload, ev);
131
+ }
132
+ else if (isAgentReasoned(ev)) {
133
+ await callbacks.onClosed?.(ev.payload, ev);
134
+ }
135
+ // Other event types ignored — forward-compat per
136
+ // COMPATIBILITY.md §2.1. Consumers that want them iterate
137
+ // streamEvents() directly.
138
+ }
139
+ catch (handlerErr) {
140
+ // Handler exceptions: surface via onError but DO NOT tear
141
+ // down the stream. One bad handler shouldn't kill the rest.
142
+ callbacks.onError?.(handlerErr instanceof Error
143
+ ? handlerErr
144
+ : new Error(String(handlerErr)));
145
+ }
146
+ }
147
+ if (!stopped)
148
+ callbacks.onEnd?.();
149
+ }
150
+ catch (streamErr) {
151
+ if (stopped)
152
+ return; // intentional cancellation
153
+ callbacks.onError?.(streamErr instanceof Error
154
+ ? streamErr
155
+ : new Error(String(streamErr)));
156
+ }
157
+ })();
158
+ return stop;
159
+ }
160
+ //# sourceMappingURL=event-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-helpers.js","sourceRoot":"","sources":["../src/event-helpers.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -12,9 +12,22 @@
12
12
  export { OpenwopClient } from './client.js';
13
13
  export type { OpenwopClientOptions, MutationOptions } from './client.js';
14
14
  export { WopError } from './types.js';
15
- export type { AuditVerifyAnomaly, AuditVerifyCheckpoint, AuditVerifyResult, BulkCancelRunResult, BulkCancelRunsRequest, BulkCancelRunsResponse, Capabilities, CancelRunRequest, CancelRunResponse, CreateRunRequest, CreateRunResponse, ErrorEnvelope, ForkRunRequest, ForkRunResponse, InterruptByTokenInspection, PollEventsResponse, ResolveInterruptByTokenResponse, ResolveInterruptRequest, ResolveInterruptResponse, RunConfigurable, RunEventDoc, RunSnapshot, RunStatus, StreamMode, } from './types.js';
15
+ export type { AuditVerifyAnomaly, AuditVerifyCheckpoint, AuditVerifyResult, BulkCancelRunResult, BulkCancelRunsRequest, BulkCancelRunsResponse, Capabilities, CancelRunRequest, CancelRunResponse, CreateRunRequest, CreateRunResponse, ErrorEnvelope, ForkRunRequest, ForkRunResponse, DebugBundle, DebugBundleOptions, RegisterWebhookRequest, RegisterWebhookResponse, InterruptByTokenInspection, PauseRunRequest, PauseRunResponse, PollEventsResponse, ResolveInterruptByTokenResponse, ResolveInterruptRequest, ResolveInterruptResponse, ResumeRunRequest, ResumeRunResponse, RunConfigurable, RunEventDoc, RunSnapshot, RunStatus, StreamMode, TypedRunEvent, AgentReasonedPayload, AgentReasoningDeltaPayload, AgentToolCalledPayload, AgentToolReturnedPayload, AgentHandoffPayload, AgentDecidedPayload, GetPromptRequest, ListPromptsRequest, ListPromptsResponse, PromptKind, PromptRef, PromptTemplate, PromptVariable, RenderPromptRequest, RenderPromptResponse, } from './types.js';
16
16
  export { streamEvents } from './sse.js';
17
17
  export type { EventsStreamContext, EventsStreamOptions } from './sse.js';
18
+ export { isAgentReasoned, isAgentReasoningDelta, isAgentToolCalled, isAgentToolReturned, isAgentHandoff, isAgentDecided, subscribeToAgentReasoning, } from './event-helpers.js';
19
+ export type { AgentReasoningCallbacks, Unsubscribe, } from './event-helpers.js';
18
20
  export { ACTIVE_RUN_STATUSES, TERMINAL_RUN_STATUSES, isTerminalRunStatus, HTTP_ERROR_CODES, isHttpErrorCode, RUN_ERROR_CODES, isRunErrorCode, } from './run-helpers.js';
19
21
  export type { ActiveRunStatus, TerminalRunStatus, HttpErrorCode, RunErrorCode, RunError, } from './run-helpers.js';
22
+ export { OPENWOP_COST_ATTRIBUTE_NAMES, sanitizeCostAttributes, } from './cost-attribution.js';
23
+ export type { OpenwopCostAttributeName } from './cost-attribution.js';
24
+ export { DEFAULT_WEBHOOK_FRESHNESS_WINDOW_SECONDS, verifyWebhookSignature, signWebhookDelivery, } from './webhook-helpers.js';
25
+ export type { VerifyWebhookSignatureOptions, VerifyWebhookOutcome, } from './webhook-helpers.js';
26
+ export { RegistryClient } from './registry-helpers.js';
27
+ export type { RegistryClientOptions, RegistryDiscovery, RegistryIndex, RegistryIndexEntry, RegistryPackMetadata, RegistryVersionManifest, } from './registry-helpers.js';
28
+ export type { AIEnvelope, AIEnvelopeErrorPayload, ClarificationRequestPayload, EnvelopeContract, EnvelopeContractRefusal, EnvelopeContractsCapability, EnvelopeMeta, EnvelopeOutcome, EnvelopeStrictness, PartialInfo, SchemaRequestPayload, SchemaResponsePayload, ValidationDetail, } from './types.js';
29
+ export { buildReasoningDirective } from './envelope-directive.js';
30
+ export type { ReasoningDirectiveStrength } from './envelope-directive.js';
31
+ export { parseRefusal } from './parse-refusal.js';
32
+ export type { RefusalProvider, RefusalSignal } from './parse-refusal.js';
20
33
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,eAAe,EACf,0BAA0B,EAC1B,kBAAkB,EAClB,+BAA+B,EAC/B,uBAAuB,EACvB,wBAAwB,EACxB,eAAe,EACf,WAAW,EACX,WAAW,EACX,SAAS,EACT,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAKzE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,QAAQ,GACT,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,+BAA+B,EAC/B,uBAAuB,EACvB,wBAAwB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,WAAW,EACX,SAAS,EACT,UAAU,EACV,aAAa,EACb,oBAAoB,EACpB,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EAEnB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,SAAS,EACT,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAIzE,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,uBAAuB,EACvB,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,QAAQ,GACT,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAMtE,OAAO,EACL,wCAAwC,EACxC,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,6BAA6B,EAC7B,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAK9B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAI/B,YAAY,EACV,UAAU,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,gBAAgB,EAChB,uBAAuB,EACvB,2BAA2B,EAC3B,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAQpB,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAW1E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -12,8 +12,42 @@
12
12
  export { OpenwopClient } from './client.js';
13
13
  export { WopError } from './types.js';
14
14
  export { streamEvents } from './sse.js';
15
+ // RFC 0002 + RFC 0024 typed event helpers — type guards over `RunEventDoc`
16
+ // plus a high-level streaming-reasoning subscription helper.
17
+ export { isAgentReasoned, isAgentReasoningDelta, isAgentToolCalled, isAgentToolReturned, isAgentHandoff, isAgentDecided, subscribeToAgentReasoning, } from './event-helpers.js';
15
18
  // Run-status + run-error helpers (added in 1). Forward-compat predicates
16
19
  // over the wire-format unions declared above; full design + rationale in
17
20
  // `run-helpers.ts`.
18
21
  export { ACTIVE_RUN_STATUSES, TERMINAL_RUN_STATUSES, isTerminalRunStatus, HTTP_ERROR_CODES, isHttpErrorCode, RUN_ERROR_CODES, isRunErrorCode, } from './run-helpers.js';
22
+ // Cost-attribution allowlist + sanitizer helpers
23
+ // (`spec/v1/observability.md §"Cost attribution attributes"`). Single
24
+ // source of truth for the canonical attribute names so independent
25
+ // hosts share one list instead of each re-deriving it.
26
+ export { OPENWOP_COST_ATTRIBUTE_NAMES, sanitizeCostAttributes, } from './cost-attribution.js';
27
+ // Webhook-delivery verification helpers (SDK-3 close-out 2026-05-15).
28
+ // HMAC-SHA256 + timestamp freshness window verification per
29
+ // spec/v1/webhooks.md §"Signature recipe". Receivers MUST verify both
30
+ // the HMAC AND the timestamp to defeat replay attacks.
31
+ export { DEFAULT_WEBHOOK_FRESHNESS_WINDOW_SECONDS, verifyWebhookSignature, signWebhookDelivery, } from './webhook-helpers.js';
32
+ // Public-registry read helpers (SDK-5 close-out 2026-05-15). Read-only
33
+ // typed client for the public node-pack registry at packs.openwop.dev
34
+ // per spec/v1/registry-operations.md.
35
+ export { RegistryClient } from './registry-helpers.js';
36
+ // RFC 0030 §A `reasoning` field prompt-directive helper. Hosts that
37
+ // advertise `capabilities.envelopes.reasoning.supported: true` use this
38
+ // to synthesize the system-prompt fragment that instructs the model to
39
+ // populate the OPTIONAL `reasoning` field. See `envelope-directive.ts`
40
+ // for the operational note on `"mandatory"` strength (provider-side
41
+ // refusal risk per the 2026-05-21 RFC 0030 amendment).
42
+ export { buildReasoningDirective } from './envelope-directive.js';
43
+ // RFC 0032 §B.3 + RFC 0033 §D refusal detection helper. Normalizes
44
+ // per-provider safety-stop signals (Anthropic `stop_reason: "refusal"`,
45
+ // OpenAI `message.refusal` + `finish_reason: "content_filter"`, Gemini
46
+ // `finishReason: "SAFETY"` + `promptFeedback.blockReason: "SAFETY"`)
47
+ // to a canonical `{ refusalText, safetyCategory?, provider }` shape.
48
+ // Hosts route the non-null return through `envelope.refusal` emission
49
+ // + the `envelope_refusal` terminal error code (RFC 0033 §F).
50
+ // **SECURITY:** Pass `refusalText` through the BYOK redaction harness
51
+ // BEFORE persistence — this helper does NOT redact.
52
+ export { parseRefusal } from './parse-refusal.js';
19
53
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA2BtC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxC,yEAAyE;AACzE,yEAAyE;AACzE,oBAAoB;AACpB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAoDtC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxC,2EAA2E;AAC3E,6DAA6D;AAC7D,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAM5B,yEAAyE;AACzE,yEAAyE;AACzE,oBAAoB;AACpB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,kBAAkB,CAAC;AAS1B,iDAAiD;AACjD,sEAAsE;AACtE,mEAAmE;AACnE,uDAAuD;AACvD,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAG/B,sEAAsE;AACtE,4DAA4D;AAC5D,sEAAsE;AACtE,uDAAuD;AACvD,OAAO,EACL,wCAAwC,EACxC,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAM9B,uEAAuE;AACvE,sEAAsE;AACtE,sCAAsC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AA4BvD,oEAAoE;AACpE,wEAAwE;AACxE,uEAAuE;AACvE,uEAAuE;AACvE,oEAAoE;AACpE,uDAAuD;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,mEAAmE;AACnE,wEAAwE;AACxE,uEAAuE;AACvE,qEAAqE;AACrE,qEAAqE;AACrE,sEAAsE;AACtE,8DAA8D;AAC9D,sEAAsE;AACtE,oDAAoD;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * parseRefusal — normalize per-provider LLM safety-stop signals to the
3
+ * canonical RFC 0032 §B.3 refusal shape.
4
+ *
5
+ * The three Tier-1 vendors (Anthropic / OpenAI / Google Gemini) surface
6
+ * refusals through different fields:
7
+ *
8
+ * - **Anthropic Messages API**: `stop_reason: "refusal"` (their 2025
9
+ * release) OR `stop_reason: "end_turn"` accompanied by safety-stop
10
+ * markers in the content. Refusal text MAY be inline in the
11
+ * `content[]` array's text blocks.
12
+ * - **OpenAI Chat Completions**: `choices[0].finish_reason:
13
+ * "content_filter"` OR `choices[0].message.refusal: <string>` (the
14
+ * refusal-text field added in their structured-output release).
15
+ * - **Google Gemini**: `candidates[0].finishReason: "SAFETY"` OR
16
+ * `promptFeedback.blockReason: "SAFETY"` (input-side block).
17
+ *
18
+ * Without normalization, every host re-implements this detection. This
19
+ * helper consolidates the per-vendor shape-detection and returns a
20
+ * canonical `RefusalSignal | null` — null when the response is a
21
+ * normal completion (no refusal detected).
22
+ *
23
+ * Per RFC 0032 §B.3 + RFC 0033 §D, hosts MUST NOT retry on refusal
24
+ * (circumvention concern). Callers route the non-null return through
25
+ * `envelope.refusal` emission + the `envelope_refusal` terminal error
26
+ * code (per RFC 0033 §F).
27
+ *
28
+ * Per SECURITY/invariants.yaml §envelope-refusal-no-prompt-leak,
29
+ * `refusalText` MUST be passed through the host's BYOK redaction
30
+ * harness BEFORE persistence — this helper does NOT redact; the
31
+ * caller is responsible for SR-1 carry-forward.
32
+ *
33
+ * @see RFCS/0032-envelope-reliability-events.md §B.3
34
+ * @see RFCS/0033-envelope-completion-contract.md §D + §F
35
+ * @see SECURITY/invariants.yaml §envelope-refusal-no-prompt-leak
36
+ */
37
+ /**
38
+ * Provider identifier for the matched shape. `"unknown"` is reserved
39
+ * for refusals matched on heuristic signals without a definitive
40
+ * vendor-shape match.
41
+ */
42
+ export type RefusalProvider = 'anthropic' | 'openai' | 'google' | 'unknown';
43
+ /**
44
+ * Canonical refusal signal. All three Tier-1 vendor shapes normalize
45
+ * to this form. `null` from `parseRefusal()` means "no refusal detected"
46
+ * (a normal completion); a non-null `RefusalSignal` means the caller
47
+ * SHOULD route through the RFC 0032 §B.3 refusal-emission path.
48
+ */
49
+ export interface RefusalSignal {
50
+ /**
51
+ * The provider's refusal text, when surfaced. MAY be `null` even for
52
+ * detected refusals — Anthropic + Gemini frequently refuse without
53
+ * inline text (the safety filter is opaque to the model). OpenAI's
54
+ * `message.refusal` field is the most consistent source of human-
55
+ * readable refusal text.
56
+ *
57
+ * **SECURITY:** When non-null, `refusalText` MAY contain prompt
58
+ * content that triggered the safety filter. Callers MUST pass it
59
+ * through their BYOK + prompt-content redaction harness BEFORE
60
+ * persistence (per `envelope-refusal-no-prompt-leak` SECURITY
61
+ * invariant).
62
+ */
63
+ refusalText: string | null;
64
+ /**
65
+ * Provider-specific safety-category identifier, when surfaced.
66
+ * Examples: Gemini's safety-rating categories
67
+ * (`HARM_CATEGORY_HARASSMENT`, etc.), OpenAI's content-filter category,
68
+ * Anthropic's policy-violation tag. Hosts MAY echo this on the
69
+ * `envelope.refusal.safetyCategory` event field for downstream
70
+ * observability.
71
+ */
72
+ safetyCategory?: string;
73
+ /**
74
+ * Which provider's shape was matched. Useful for debugging
75
+ * cross-host integration + for hosts that want to route refusals
76
+ * differently per vendor (e.g., different operator notifications).
77
+ */
78
+ provider: RefusalProvider;
79
+ }
80
+ /**
81
+ * Parse a provider response into a canonical refusal signal.
82
+ *
83
+ * Returns `null` when no refusal is detected (normal completion).
84
+ * Returns a `RefusalSignal` when the response matches one of the
85
+ * three Tier-1 vendors' safety-stop shapes.
86
+ *
87
+ * Detection order: OpenAI → Anthropic → Gemini. Each detector inspects
88
+ * a distinctive top-level field, so cross-vendor false-positives are
89
+ * unlikely. A response that doesn't match any vendor shape returns
90
+ * `null` (hosts that route through novel providers add their own
91
+ * detector + fall back to this for the three known ones).
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * import { parseRefusal } from '@openwop/openwop';
96
+ *
97
+ * const response = await callOpenAI({...});
98
+ * const refusal = parseRefusal(response);
99
+ * if (refusal) {
100
+ * // Route through envelope.refusal emission + envelope_refusal error code.
101
+ * // REMEMBER to redact refusalText through the BYOK harness before
102
+ * // persistence (SECURITY invariant envelope-refusal-no-prompt-leak).
103
+ * await emitEnvelopeRefusal({
104
+ * refusalText: redactBYOK(refusal.refusalText),
105
+ * safetyCategory: refusal.safetyCategory,
106
+ * provider: refusal.provider,
107
+ * });
108
+ * throw new EnvelopeRefusalError(...);
109
+ * }
110
+ * // ...normal-completion handling...
111
+ * ```
112
+ */
113
+ export declare function parseRefusal(providerResponse: unknown): RefusalSignal | null;
114
+ //# sourceMappingURL=parse-refusal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-refusal.d.ts","sourceRoot":"","sources":["../src/parse-refusal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;;;;OAYG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3B;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,QAAQ,EAAE,eAAe,CAAC;CAC3B;AA6LD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,YAAY,CAAC,gBAAgB,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAM5E"}