@athenaintel/react 0.10.41 → 0.11.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/README.md CHANGED
@@ -103,9 +103,83 @@ What the statewire transport wires up in `AthenaChat`:
103
103
  statewire session to the selected thread. `ThreadList` and
104
104
  `useAthenaThreadManager` work in both modes.
105
105
 
106
- Remaining limitations: the `agent` prop is legacy-only (the statewire host
107
- always runs the Athena deep agent), and `model` defaults to the deep-agent
108
- default model in this mode.
106
+ Remaining limitations: on statewire the `agent` prop only accepts
107
+ `collab_agent:<asset_id>` refs (see below) any other value is ignored,
108
+ because the statewire host always runs the Athena deep agent. `model`
109
+ defaults to the deep-agent default model unless a collab agent supplies one.
110
+
111
+ ## Collab Agents
112
+
113
+ A **collab agent** is an agent configuration authored in Athena (prompt,
114
+ model, toolkits, behavior) and addressed as `collab_agent:<asset_id>`. Point
115
+ the provider at one and the chat runs **as** that agent:
116
+
117
+ ```tsx
118
+ <AthenaProvider
119
+ transport="statewire"
120
+ agent="collab_agent:asset_432af46f-293d-480b-a518-30b1f42a9ef7"
121
+ channel="askbob_web"
122
+ >
123
+ <AthenaChat />
124
+ </AthenaProvider>
125
+ ```
126
+
127
+ Copy the snippet with real ids from the agent's **Channels** tab in Athena.
128
+
129
+ `channel` is optional. It selects a **channel override layer** — a built-in
130
+ channel (`email`, `sms`, …) or a custom channel defined on that agent — so one
131
+ agent can present different prompts, models, and tools per surface. Omit it to
132
+ run the agent's base configuration.
133
+
134
+ ### Requirements
135
+
136
+ - **`transport="statewire"`.** The legacy transport ignores `collab_agent:` refs.
137
+ - **Publish the agent.** Resolution reads the *published* snapshot, not the
138
+ live draft, so a channel that exists only in the editor is rejected with
139
+ `collab_agent_channel_unknown`.
140
+ - **The acting user needs VIEW access** to the agent asset (admins bypass).
141
+ Running as an agent exposes its prompt and tool policy, so the same gate that
142
+ governs opening the asset governs running it.
143
+
144
+ ### Do not also pass `model`, `systemPrompt`, or `tools`
145
+
146
+ Request keys override the agent definition, so **anything you pass here
147
+ silently replaces what the agent's author configured** — the run succeeds and
148
+ returns a plausible answer using your config instead of theirs.
149
+
150
+ ```tsx
151
+ // ❌ the agent's prompt, model, and tools are all discarded
152
+ <AthenaProvider
153
+ transport="statewire"
154
+ agent="collab_agent:asset_1234"
155
+ model="claude-opus-4-6"
156
+ systemPrompt="You are a helpful assistant."
157
+ tools={['web_search_browse_toolkit']}
158
+ />
159
+
160
+ // ✅ the agent's own configuration wins
161
+ <AthenaProvider transport="statewire" agent="collab_agent:asset_1234" />
162
+ ```
163
+
164
+ Pass them only when you deliberately want to override the agent — an explicit
165
+ `model` is honoured as a caller override, with the agent's model as the default
166
+ beneath it.
167
+
168
+ ### Failure codes
169
+
170
+ A refused selection surfaces as an `AthenaSdkError` with
171
+ `code: 'collab_agent_rejected'`; the specific reason below rides in the error's
172
+ `detail`. Subscribe with `onError` (see
173
+ [Debugging](#debugging-and-diagnostics)) rather than guessing from messages:
174
+
175
+ | Code | Meaning |
176
+ |---|---|
177
+ | `collab_agent_not_found` | No such asset, or it is not a `collab_agent` |
178
+ | `collab_agent_forbidden` | The acting user lacks VIEW on the asset |
179
+ | `collab_agent_channel_unknown` | No such channel (the message lists the known ones) |
180
+ | `collab_agent_channel_disabled` | The channel exists but is toggled off |
181
+ | `collab_agent_channel_kind_mismatch` | A voice channel was selected for a text run |
182
+ | `collab_agent_runs_disabled` | The deployment has the SDK seam turned off |
109
183
 
110
184
  ## Authentication
111
185
 
@@ -323,6 +397,52 @@ function WorkflowButton() {
323
397
  }
324
398
  ```
325
399
 
400
+ ## Debugging and Diagnostics
401
+
402
+ The SDK emits structured diagnostic events — auth handshake, thread list,
403
+ statewire attach, sends, errors — with timings. Nothing is logged by default;
404
+ turn it on with the `debug` prop:
405
+
406
+ ```tsx
407
+ <AthenaProvider
408
+ debug // or 'debug' | 'info' | { console: 'debug', posthog: true }
409
+ onDiagnostic={(event) => console.log(event.name, event.durationMs)}
410
+ onError={(error) => reportToSentry(error)}
411
+ >
412
+ ```
413
+
414
+ `onError` receives an `AthenaSdkError` with a stable `code`, a human `hint`,
415
+ and the originating `status`/`detail` — match on `code`, never on message text:
416
+
417
+ ```tsx
418
+ import { ATHENA_SDK_ERROR_CODES } from '@athenaintel/react';
419
+
420
+ onError={(error) => {
421
+ if (error.code === ATHENA_SDK_ERROR_CODES.collab_agent_rejected) {
422
+ // the backend's granular reason rides in `detail`, e.g.
423
+ // 'collab_agent_channel_unknown' — whose message lists the known channels
424
+ console.warn(error.detail, error.hint);
425
+ }
426
+ }}
427
+ ```
428
+
429
+ Each provider registers its own consumer, so multiple mounted providers all
430
+ receive every event; a callback that throws is caught and cannot break the chat.
431
+
432
+ Without a rebuild, from the browser console:
433
+
434
+ ```js
435
+ localStorage.setItem('athena:debug', 'debug'); // console echo on, survives reload
436
+ __ATHENA_SDK__.diagnostics.snapshot(); // recent events + timings
437
+ __ATHENA_SDK__.diagnostics.export(); // JSON, for attaching to a bug report
438
+ ```
439
+
440
+ Credentials are redacted everywhere (buffer, console, PostHog): any key
441
+ matching `token`, `secret`, `api[-_]?key`, `authorization`, `cookie`, or
442
+ `password` is stripped before an event is recorded. Spans also emit
443
+ `performance.mark`/`measure` entries prefixed `athena-sdk:`, so they show up on
444
+ the browser Performance timeline.
445
+
326
446
  ## License
327
447
 
328
448
  Proprietary. For licensed enterprise customers only.
@@ -1,10 +1,16 @@
1
1
  import type { Toolkit } from '@assistant-ui/react';
2
- /** A frontend tool the statewire client-tool bridge can execute locally. */
2
+ import { toJSONSchema } from 'assistant-stream';
3
+ type StatewireToolParameters = ReturnType<typeof toJSONSchema>;
4
+ interface StatewireClientToolWireEntry extends Record<string, unknown> {
5
+ name: string;
6
+ description: string;
7
+ parameters: StatewireToolParameters;
8
+ requires_write: false;
9
+ }
3
10
  export interface StatewireClientTool {
4
11
  name: string;
5
12
  description?: string;
6
- /** JSON schema for the tool arguments. */
7
- parameters: Record<string, unknown>;
13
+ parameters: StatewireToolParameters;
8
14
  handler: (args: Record<string, unknown>, context: {
9
15
  toolCallId: string;
10
16
  }) => Promise<unknown>;
@@ -15,10 +21,11 @@ export interface StatewireClientTool {
15
21
  * The deep-agent runtime drops frontend (`ui_*`) tool definitions, so tools
16
22
  * with a local `execute` must instead be declared through
17
23
  * `runConfig.custom.client_tools` and answered by the statewire client-tool
18
- * bridge. Schema conversion reuses `toToolsJSONSchema` — the same conversion
19
- * the legacy assistant-transport applies — and a tool whose schema cannot be
20
- * converted is skipped with a warning rather than failing the whole set.
24
+ * bridge. Schema conversion reuses `toJSONSchema` — the same conversion
25
+ * primitive as the legacy assistant-transport — and a tool whose schema cannot
26
+ * be converted is skipped with a warning rather than failing the whole set.
21
27
  */
22
28
  export declare function collectStatewireClientTools(toolkit: Toolkit): StatewireClientTool[];
23
29
  /** Wire entries for `runConfig.custom.client_tools`. */
24
- export declare function statewireClientToolWireEntries(tools: readonly StatewireClientTool[]): Record<string, unknown>[];
30
+ export declare function statewireClientToolWireEntries(tools: readonly StatewireClientTool[]): StatewireClientToolWireEntry[];
31
+ export {};
@@ -0,0 +1,138 @@
1
+ /**
2
+ * SDK diagnostics: one structured event bus for everything the SDK does that
3
+ * can be slow or can fail — auth acquisition, thread-list fetches, statewire
4
+ * attach/snapshot/connection changes, sends, first tokens, and every error.
5
+ *
6
+ * Design:
7
+ * - Module-level singleton (one SDK, one bus), configured by `AthenaProvider`
8
+ * from its `debug` / `onDiagnostic` / `onError` props. Nothing here runs
9
+ * until something calls `emit`, and with defaults it costs a ring-buffer push.
10
+ * - Every event carries `t` (ms since page navigation start via
11
+ * `performance.now()`) so the buffer reads as a waterfall. Timed spans use
12
+ * `performance.mark` / `performance.measure` too, so the browser Performance
13
+ * panel shows them as user timings without any extra tooling.
14
+ * - Console output is opt-in (`debug` prop or `localStorage['athena:debug']`),
15
+ * prefixed `[AthenaSDK]`, and level-gated. Errors always reach `onError`.
16
+ * - PostHog forwarding (opt-in) sends the timing events as
17
+ * `athena_sdk_<name>` with `duration_ms`, through the SDK's capture gate.
18
+ *
19
+ * Consumers read the buffer with `useAthenaDiagnostics()` or, in the browser
20
+ * console, `window.__ATHENA_SDK__.diagnostics.snapshot()`.
21
+ */
22
+ import { AthenaSdkError } from './errors';
23
+ export declare const ATHENA_DIAGNOSTIC_LEVELS: readonly ['debug', 'info', 'warn', 'error'];
24
+ export type AthenaDiagnosticLevel = (typeof ATHENA_DIAGNOSTIC_LEVELS)[number];
25
+ /**
26
+ * Event names. Dotted `area.event` so a consumer can filter by prefix. Names
27
+ * are part of the public contract: renaming one is a breaking change for
28
+ * dashboards, so add new ones instead of repurposing.
29
+ */
30
+ export type AthenaDiagnosticEventName = 'sdk.init' | 'sdk.transport' | 'sdk.config.warning' | 'auth.bridge.start' | 'auth.bridge.ready' | 'auth.bridge.timeout' | 'auth.bridge.absent' | 'auth.bridge.error' | 'auth.token.acquired' | 'auth.token.refreshed' | 'auth.token.refresh_failed' | 'auth.token.stale' | 'auth.sso.userinfo.start' | 'auth.sso.userinfo.done' | 'auth.sso.userinfo.error' | 'threads.list.start' | 'threads.list.done' | 'threads.list.error' | 'threads.history.start' | 'threads.history.done' | 'threads.history.error' | 'statewire.attach.start' | 'statewire.snapshot' | 'statewire.connection' | 'statewire.parked' | 'statewire.error' | 'statewire.read_only' | 'run.send' | 'run.first_token' | 'run.done' | 'run.error' | 'stream.connected' | 'stream.error' | 'sdk.error';
31
+ export interface AthenaDiagnosticEvent {
32
+ /** Monotonic sequence number within this page. */
33
+ seq: number;
34
+ /** `performance.now()` at emit time (ms since navigation start). */
35
+ t: number;
36
+ /** Wall-clock ISO timestamp, for exports. */
37
+ at: string;
38
+ name: AthenaDiagnosticEventName;
39
+ level: AthenaDiagnosticLevel;
40
+ /** Milliseconds for a completed span (set on `*.done` / `*.ready` / `run.first_token`). */
41
+ durationMs?: number;
42
+ /** Structured context. Never contains credentials — see `redact`. */
43
+ data?: Record<string, unknown>;
44
+ /** Present on error-level events. */
45
+ error?: AthenaSdkError;
46
+ }
47
+ export interface AthenaDiagnosticsConfig {
48
+ /** Minimum level echoed to the console. `false` disables console output. */
49
+ console: AthenaDiagnosticLevel | false;
50
+ /** Forward timing + error events to PostHog as `athena_sdk_*` events. */
51
+ posthog: boolean;
52
+ /** Receives every event (already redacted). */
53
+ onEvent?: (event: AthenaDiagnosticEvent) => void;
54
+ /** Receives every error-level event's `AthenaSdkError`. */
55
+ onError?: (error: AthenaSdkError, event: AthenaDiagnosticEvent) => void;
56
+ /** Ring-buffer capacity. */
57
+ bufferSize: number;
58
+ }
59
+ export interface AthenaDiagnosticsSnapshot {
60
+ sdkVersion: string;
61
+ config: Omit<AthenaDiagnosticsConfig, 'onEvent' | 'onError'>;
62
+ events: AthenaDiagnosticEvent[];
63
+ /** Latest value per gauge (connection status, transport, auth mode, …). */
64
+ gauges: Record<string, unknown>;
65
+ /** Named span durations, most recent per name (`auth.bridge`, `threads.list`, …). */
66
+ timings: Record<string, number>;
67
+ }
68
+ type Listener = (event: AthenaDiagnosticEvent) => void;
69
+ declare class AthenaDiagnosticsBus {
70
+ private config;
71
+ private events;
72
+ private listeners;
73
+ private snapshotListeners;
74
+ private seq;
75
+ private gauges;
76
+ private timings;
77
+ private spans;
78
+ private cachedSnapshot;
79
+ configure(partial: Partial<AthenaDiagnosticsConfig>): void;
80
+ getConfig(): AthenaDiagnosticsConfig;
81
+ /** Effective console level: the configured one, or `localStorage['athena:debug']`. */
82
+ private consoleLevel;
83
+ emit(name: AthenaDiagnosticEventName, options?: {
84
+ level?: AthenaDiagnosticLevel;
85
+ data?: Record<string, unknown>;
86
+ durationMs?: number;
87
+ error?: unknown;
88
+ }): AthenaDiagnosticEvent;
89
+ /** Record an error-level event. Returns the normalized `AthenaSdkError`. */
90
+ error(name: AthenaDiagnosticEventName, error: unknown, fallback: {
91
+ code: AthenaSdkError['code'];
92
+ message: string;
93
+ hint?: string;
94
+ }, data?: Record<string, unknown>): AthenaSdkError;
95
+ /**
96
+ * Start a named span. Returns a function that ends it and emits `doneName`
97
+ * with `durationMs` (or `errorName` when passed an error). Also records a
98
+ * `performance.measure` so the span shows in the browser's Performance panel.
99
+ */
100
+ span(spanName: string, startName: AthenaDiagnosticEventName, data?: Record<string, unknown>): (doneName: AthenaDiagnosticEventName, doneOptions?: {
101
+ data?: Record<string, unknown>;
102
+ error?: unknown;
103
+ }) => number;
104
+ /** Set a gauge (latest-value metric): connection status, transport, auth mode. */
105
+ gauge(name: string, value: unknown): void;
106
+ subscribe(listener: Listener): () => void;
107
+ /**
108
+ * Additive consumer registration: every registered consumer receives every
109
+ * event (and every error), unlike the single `onEvent`/`onError` config
110
+ * slots which are last-writer-wins. `AthenaProvider` registers through this
111
+ * so two mounted providers never clobber each other's callbacks.
112
+ */
113
+ addConsumer(consumer: {
114
+ onEvent?: (event: AthenaDiagnosticEvent) => void;
115
+ onError?: (error: AthenaSdkError, event: AthenaDiagnosticEvent) => void;
116
+ }): () => void;
117
+ /** Subscribe to snapshot changes (for `useSyncExternalStore`). */
118
+ subscribeSnapshot(listener: () => void): () => void;
119
+ snapshot(): AthenaDiagnosticsSnapshot;
120
+ /** Copy-paste-able JSON of the current snapshot (errors serialized). */
121
+ export(): string;
122
+ clear(): void;
123
+ private invalidate;
124
+ private mark;
125
+ private measure;
126
+ private echoToConsole;
127
+ private forwardToPostHog;
128
+ }
129
+ export declare const athenaDiagnostics: AthenaDiagnosticsBus;
130
+ /**
131
+ * Expose the bus on `window.__ATHENA_SDK__` so an integrator can inspect the
132
+ * waterfall from the console (`__ATHENA_SDK__.diagnostics.snapshot()`) or turn
133
+ * console echo on without a rebuild (`localStorage.setItem('athena:debug','debug')`).
134
+ */
135
+ export declare function installAthenaDiagnosticsGlobal(): void;
136
+ /** Normalize the provider's `debug` prop into a diagnostics config patch. */
137
+ export declare function debugPropToDiagnosticsConfig(debug: boolean | AthenaDiagnosticLevel | Partial<AthenaDiagnosticsConfig> | undefined): Partial<AthenaDiagnosticsConfig>;
138
+ export {};
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Typed SDK errors. Every failure the SDK surfaces to a consumer carries a
3
+ * stable `code` (matchable in code), a human `message`, and a `hint` — the
4
+ * one-line "what to do about it" that turns a silent failure into a debuggable
5
+ * one. Backend rejections keep their structured `{ status, detail }` payload.
6
+ */
7
+ export declare const ATHENA_SDK_ERROR_CODES: {
8
+ /** No token and no API key resolved before a request had to be sent. */
9
+ readonly auth_missing: 'auth_missing';
10
+ /** The server refused the credential (401 / statewire `unauthorized` fin). */
11
+ readonly auth_rejected: 'auth_rejected';
12
+ /** `/_athena/auth` or the iframe bridge did not answer within the timeout. */
13
+ readonly auth_bridge_timeout: 'auth_bridge_timeout';
14
+ /** `/_athena/auth` answered with a non-404 error. */
15
+ readonly auth_bridge_failed: 'auth_bridge_failed';
16
+ /** The statewire sync URL could not be derived from the configuration. */
17
+ readonly config_invalid: 'config_invalid';
18
+ /** A prop was set that the active transport ignores (e.g. `agent` on statewire). */
19
+ readonly config_ignored: 'config_ignored';
20
+ /** The thread list request failed. */
21
+ readonly threads_list_failed: 'threads_list_failed';
22
+ /** Loading a thread's history failed. */
23
+ readonly thread_load_failed: 'thread_load_failed';
24
+ /** The statewire host rejected a command (structured `{status, detail}`). */
25
+ readonly statewire_rejected: 'statewire_rejected';
26
+ /** The statewire connection is gone and will not reconnect on its own. */
27
+ readonly statewire_gone: 'statewire_gone';
28
+ /** The legacy `/api/chat` stream failed. */
29
+ readonly stream_failed: 'stream_failed';
30
+ /** The selected collab agent / channel was refused by the backend. */
31
+ readonly collab_agent_rejected: 'collab_agent_rejected';
32
+ /** Anything else. */
33
+ readonly unknown: 'unknown';
34
+ };
35
+ export type AthenaSdkErrorCode = (typeof ATHENA_SDK_ERROR_CODES)[keyof typeof ATHENA_SDK_ERROR_CODES];
36
+ export interface AthenaSdkErrorOptions {
37
+ code: AthenaSdkErrorCode;
38
+ message: string;
39
+ /** One line telling the integrator what to check or change. */
40
+ hint?: string;
41
+ /** HTTP-ish status when the failure came from a server. */
42
+ status?: number;
43
+ /** Structured server detail (e.g. `{ code, message }` from agora). */
44
+ detail?: unknown;
45
+ cause?: unknown;
46
+ /** Extra structured context safe to log (never credentials). */
47
+ context?: Record<string, unknown>;
48
+ }
49
+ export declare class AthenaSdkError extends Error {
50
+ readonly code: AthenaSdkErrorCode;
51
+ readonly hint: string | undefined;
52
+ readonly status: number | undefined;
53
+ readonly detail: unknown;
54
+ readonly context: Record<string, unknown>;
55
+ constructor(options: AthenaSdkErrorOptions);
56
+ /** A log/console-friendly one-liner: `[code] message — hint`. */
57
+ describe(): string;
58
+ toJSON(): Record<string, unknown>;
59
+ }
60
+ export declare function isAthenaSdkError(value: unknown): value is AthenaSdkError;
61
+ /** Wrap any thrown value as an `AthenaSdkError` (idempotent for SDK errors). */
62
+ export declare function toAthenaSdkError(value: unknown, fallback: Omit<AthenaSdkErrorOptions, 'cause'>): AthenaSdkError;
63
+ /** Structured `{ code, message }` detail from an agora rejection, if present. */
64
+ export declare function readRejectionDetail(detail: unknown): {
65
+ code: string | null;
66
+ message: string | null;
67
+ };
@@ -0,0 +1,13 @@
1
+ import { type AthenaDiagnosticsSnapshot } from './athena-diagnostics';
2
+ /**
3
+ * Read the SDK diagnostics buffer reactively — events, gauges (connection
4
+ * status, transport, auth mode), and the latest span timings (time to auth,
5
+ * time to thread list, time to statewire snapshot, time to first token).
6
+ *
7
+ * Renders on every emitted event; mount it in a debug panel, not in the chat
8
+ * transcript.
9
+ */
10
+ export declare function useAthenaDiagnostics(): AthenaDiagnosticsSnapshot & {
11
+ clear: () => void;
12
+ export: () => string;
13
+ };