@databricks/appkit-ui 0.32.0 → 0.34.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 (87) hide show
  1. package/CLAUDE.md +53 -1
  2. package/NOTICE.md +1 -0
  3. package/dist/js/index.js +1 -0
  4. package/dist/react/hooks/index.d.ts +1 -0
  5. package/dist/react/hooks/index.js +1 -0
  6. package/dist/react/hooks/types.d.ts +11 -5
  7. package/dist/react/hooks/types.d.ts.map +1 -1
  8. package/dist/react/hooks/use-agent-chat.d.ts +128 -0
  9. package/dist/react/hooks/use-agent-chat.d.ts.map +1 -0
  10. package/dist/react/hooks/use-agent-chat.js +133 -0
  11. package/dist/react/hooks/use-agent-chat.js.map +1 -0
  12. package/dist/react/hooks/use-analytics-query.d.ts +4 -4
  13. package/dist/react/hooks/use-analytics-query.d.ts.map +1 -1
  14. package/dist/react/hooks/use-analytics-query.js +4 -4
  15. package/dist/react/hooks/use-analytics-query.js.map +1 -1
  16. package/dist/react/hooks/use-chart-data.js +9 -9
  17. package/dist/react/hooks/use-chart-data.js.map +1 -1
  18. package/dist/react/index.d.ts +2 -1
  19. package/dist/react/index.js +2 -1
  20. package/dist/shared/src/index.js +2 -0
  21. package/dist/shared/src/sql/helpers.d.ts +113 -8
  22. package/dist/shared/src/sql/helpers.d.ts.map +1 -1
  23. package/dist/shared/src/sql/helpers.js +116 -7
  24. package/dist/shared/src/sql/helpers.js.map +1 -1
  25. package/dist/shared/src/sql/index.js +1 -0
  26. package/dist/shared/src/sql/types.d.ts +16 -2
  27. package/dist/shared/src/sql/types.d.ts.map +1 -1
  28. package/docs/api/appkit/Class.AppKitMcpClient.md +157 -0
  29. package/docs/api/appkit/Class.DatabricksAdapter.md +151 -0
  30. package/docs/api/appkit/Function.agentIdFromMarkdownPath.md +18 -0
  31. package/docs/api/appkit/Function.createAgent.md +33 -0
  32. package/docs/api/appkit/Function.defineTool.md +26 -0
  33. package/docs/api/appkit/Function.executeFromRegistry.md +25 -0
  34. package/docs/api/appkit/Function.functionToolToDefinition.md +16 -0
  35. package/docs/api/appkit/Function.isFunctionTool.md +16 -0
  36. package/docs/api/appkit/Function.isHostedTool.md +16 -0
  37. package/docs/api/appkit/Function.isToolkitEntry.md +18 -0
  38. package/docs/api/appkit/Function.loadAgentFromFile.md +21 -0
  39. package/docs/api/appkit/Function.loadAgentsFromDir.md +26 -0
  40. package/docs/api/appkit/Function.mcpServer.md +28 -0
  41. package/docs/api/appkit/Function.parseTextToolCalls.md +26 -0
  42. package/docs/api/appkit/Function.resolveHostedTools.md +16 -0
  43. package/docs/api/appkit/Function.runAgent.md +26 -0
  44. package/docs/api/appkit/Function.tool.md +28 -0
  45. package/docs/api/appkit/Function.toolsFromRegistry.md +20 -0
  46. package/docs/api/appkit/Interface.AgentAdapter.md +21 -0
  47. package/docs/api/appkit/Interface.AgentDefinition.md +112 -0
  48. package/docs/api/appkit/Interface.AgentInput.md +37 -0
  49. package/docs/api/appkit/Interface.AgentRunContext.md +32 -0
  50. package/docs/api/appkit/Interface.AgentToolDefinition.md +37 -0
  51. package/docs/api/appkit/Interface.AgentsPluginConfig.md +241 -0
  52. package/docs/api/appkit/Interface.AutoInheritToolsConfig.md +27 -0
  53. package/docs/api/appkit/Interface.BasePluginConfig.md +1 -0
  54. package/docs/api/appkit/Interface.FunctionTool.md +80 -0
  55. package/docs/api/appkit/Interface.McpConnectAllResult.md +38 -0
  56. package/docs/api/appkit/Interface.Message.md +55 -0
  57. package/docs/api/appkit/Interface.PluginToolkitProvider.md +22 -0
  58. package/docs/api/appkit/Interface.PromptContext.md +30 -0
  59. package/docs/api/appkit/Interface.RegisteredAgent.md +75 -0
  60. package/docs/api/appkit/Interface.RunAgentInput.md +34 -0
  61. package/docs/api/appkit/Interface.RunAgentResult.md +23 -0
  62. package/docs/api/appkit/Interface.Thread.md +46 -0
  63. package/docs/api/appkit/Interface.ThreadStore.md +103 -0
  64. package/docs/api/appkit/Interface.ToolAnnotations.md +56 -0
  65. package/docs/api/appkit/Interface.ToolConfig.md +72 -0
  66. package/docs/api/appkit/Interface.ToolEntry.md +73 -0
  67. package/docs/api/appkit/Interface.ToolProvider.md +38 -0
  68. package/docs/api/appkit/Interface.ToolkitEntry.md +59 -0
  69. package/docs/api/appkit/Interface.ToolkitOptions.md +45 -0
  70. package/docs/api/appkit/TypeAlias.AgentEvent.md +299 -0
  71. package/docs/api/appkit/TypeAlias.AgentTool.md +11 -0
  72. package/docs/api/appkit/TypeAlias.AgentTools.md +8 -0
  73. package/docs/api/appkit/TypeAlias.AgentToolsFn.md +20 -0
  74. package/docs/api/appkit/TypeAlias.BaseSystemPromptOption.md +9 -0
  75. package/docs/api/appkit/TypeAlias.HostedTool.md +10 -0
  76. package/docs/api/appkit/TypeAlias.Plugins.md +26 -0
  77. package/docs/api/appkit/TypeAlias.ResolvedToolEntry.md +29 -0
  78. package/docs/api/appkit/TypeAlias.ToolRegistry.md +6 -0
  79. package/docs/api/appkit/Variable.agents.md +19 -0
  80. package/docs/api/appkit/Variable.sql.md +203 -21
  81. package/docs/api/appkit.md +113 -62
  82. package/docs/app-management.md +2 -2
  83. package/docs/plugins/agents.md +441 -0
  84. package/docs/plugins/analytics.md +7 -2
  85. package/llms.txt +53 -1
  86. package/package.json +1 -1
  87. package/sbom.cdx.json +1 -1
package/CLAUDE.md CHANGED
@@ -43,6 +43,7 @@ npx @databricks/appkit docs <query>
43
43
 
44
44
  ## Plugins
45
45
 
46
+ - [Agents](./docs/plugins/agents.md): This plugin is currently beta. APIs may change between minor releases. Import from @databricks/appkit/beta. See Plugin Stability Tiers.
46
47
  - [Analytics plugin](./docs/plugins/analytics.md): Enables SQL query execution against Databricks SQL Warehouses.
47
48
  - [Caching](./docs/plugins/caching.md): AppKit provides both global and plugin-level caching capabilities.
48
49
  - [Creating custom plugins](./docs/plugins/custom-plugins.md): If you need custom API routes or background logic, implement an AppKit plugin. The fastest way is to use the CLI:
@@ -59,11 +60,13 @@ npx @databricks/appkit docs <query>
59
60
 
60
61
  ## appkit API reference [collapsed]
61
62
 
62
- - [@databricks/appkit](./docs/api/appkit.md): Core library for building Databricks applications with type-safe SQL queries,
63
+ - [@databricks/appkit](./docs/api/appkit.md): Documentation merge entry for Typedoc combines the stable @databricks/appkit
63
64
  - [Abstract Class: AppKitError](./docs/api/appkit/Class.AppKitError.md): Base error class for all AppKit errors.
65
+ - [Class: AppKitMcpClient](./docs/api/appkit/Class.AppKitMcpClient.md): Lightweight MCP client for Databricks-hosted MCP servers.
64
66
  - [Class: AuthenticationError](./docs/api/appkit/Class.AuthenticationError.md): Error thrown when authentication fails.
65
67
  - [Class: ConfigurationError](./docs/api/appkit/Class.ConfigurationError.md): Error thrown when configuration is missing or invalid.
66
68
  - [Class: ConnectionError](./docs/api/appkit/Class.ConnectionError.md): Error thrown when a connection or network operation fails.
69
+ - [Class: DatabricksAdapter](./docs/api/appkit/Class.DatabricksAdapter.md): Adapter that talks directly to Databricks Model Serving /invocations endpoint.
67
70
  - [Class: ExecutionError](./docs/api/appkit/Class.ExecutionError.md): Error thrown when an operation execution fails.
68
71
  - [Class: InitializationError](./docs/api/appkit/Class.InitializationError.md): Error thrown when a service or component is not properly initialized.
69
72
  - [Abstract Class: Plugin<TConfig>](./docs/api/appkit/Class.Plugin.md): Base abstract class for creating AppKit plugins.
@@ -74,12 +77,17 @@ npx @databricks/appkit docs <query>
74
77
  - [Class: ValidationError](./docs/api/appkit/Class.ValidationError.md): Error thrown when input validation fails.
75
78
  - [Enumeration: RequestedClaimsPermissionSet](./docs/api/appkit/Enumeration.RequestedClaimsPermissionSet.md): Permission set for Unity Catalog table access
76
79
  - [Enumeration: ResourceType](./docs/api/appkit/Enumeration.ResourceType.md): Resource types from schema $defs.resourceType.enum
80
+ - [Function: agentIdFromMarkdownPath()](./docs/api/appkit/Function.agentIdFromMarkdownPath.md): Derives the logical agent id from a markdown path. When the file is named
77
81
  - [Function: appKitServingTypesPlugin()](./docs/api/appkit/Function.appKitServingTypesPlugin.md): Vite plugin to generate TypeScript types for AppKit serving endpoints.
78
82
  - [Function: appKitTypesPlugin()](./docs/api/appkit/Function.appKitTypesPlugin.md): Vite plugin to generate types for AppKit queries.
83
+ - [Function: createAgent()](./docs/api/appkit/Function.createAgent.md): Pure factory for agent definitions. Returns the passed-in definition after
79
84
  - [Function: createApp()](./docs/api/appkit/Function.createApp.md): Bootstraps AppKit with the provided configuration.
80
85
  - [Function: createLakebasePool()](./docs/api/appkit/Function.createLakebasePool.md): Create a Lakebase pool with appkit's logger integration.
86
+ - [Function: defineTool()](./docs/api/appkit/Function.defineTool.md): Defines a single tool entry for a plugin's internal registry.
87
+ - [Function: executeFromRegistry()](./docs/api/appkit/Function.executeFromRegistry.md): Validates tool-call arguments against the entry's schema and invokes its
81
88
  - [Function: extractServingEndpoints()](./docs/api/appkit/Function.extractServingEndpoints.md): Extract serving endpoint config from a server file by AST-parsing it.
82
89
  - [Function: findServerFile()](./docs/api/appkit/Function.findServerFile.md): Find the server entry file by checking candidate paths in order.
90
+ - [Function: functionToolToDefinition()](./docs/api/appkit/Function.functionToolToDefinition.md): Parameters
83
91
  - [Function: generateDatabaseCredential()](./docs/api/appkit/Function.generateDatabaseCredential.md): Generate OAuth credentials for Postgres database connection using the proper Postgres API.
84
92
  - [Function: getExecutionContext()](./docs/api/appkit/Function.getExecutionContext.md): Get the current execution context.
85
93
  - [Function: getLakebaseOrmConfig()](./docs/api/appkit/Function.getLakebaseOrmConfig.md): Get Lakebase connection configuration for ORMs that don't accept pg.Pool directly.
@@ -88,13 +96,32 @@ npx @databricks/appkit docs <query>
88
96
  - [Function: getResourceRequirements()](./docs/api/appkit/Function.getResourceRequirements.md): Gets the resource requirements from a plugin's manifest.
89
97
  - [Function: getUsernameWithApiLookup()](./docs/api/appkit/Function.getUsernameWithApiLookup.md): Resolves the PostgreSQL username for a Lakebase connection.
90
98
  - [Function: getWorkspaceClient()](./docs/api/appkit/Function.getWorkspaceClient.md): Get workspace client from config or SDK default auth chain
99
+ - [Function: isFunctionTool()](./docs/api/appkit/Function.isFunctionTool.md): Parameters
100
+ - [Function: isHostedTool()](./docs/api/appkit/Function.isHostedTool.md): Parameters
91
101
  - [Function: isSQLTypeMarker()](./docs/api/appkit/Function.isSQLTypeMarker.md): Type guard to check if a value is a SQL type marker
102
+ - [Function: isToolkitEntry()](./docs/api/appkit/Function.isToolkitEntry.md): Type guard for ToolkitEntry — used by the agents plugin to differentiate
103
+ - [Function: loadAgentFromFile()](./docs/api/appkit/Function.loadAgentFromFile.md): Loads a single markdown agent file and resolves its frontmatter against
104
+ - [Function: loadAgentsFromDir()](./docs/api/appkit/Function.loadAgentsFromDir.md): Scans a directory for one subdirectory per agent, each containing
105
+ - [Function: mcpServer()](./docs/api/appkit/Function.mcpServer.md): Factory for declaring a custom MCP server tool.
106
+ - [Function: parseTextToolCalls()](./docs/api/appkit/Function.parseTextToolCalls.md): Parses text-based tool calls from model output.
107
+ - [Function: resolveHostedTools()](./docs/api/appkit/Function.resolveHostedTools.md): Parameters
108
+ - [Function: runAgent()](./docs/api/appkit/Function.runAgent.md): Standalone agent execution without createApp. Resolves the adapter, binds
109
+ - [Function: tool()](./docs/api/appkit/Function.tool.md): Factory for defining function tools with Zod schemas.
110
+ - [Function: toolsFromRegistry()](./docs/api/appkit/Function.toolsFromRegistry.md): Produces the AgentToolDefinition[] a ToolProvider exposes to the LLM,
111
+ - [Interface: AgentAdapter](./docs/api/appkit/Interface.AgentAdapter.md): Methods
112
+ - [Interface: AgentDefinition](./docs/api/appkit/Interface.AgentDefinition.md): Properties
113
+ - [Interface: AgentInput](./docs/api/appkit/Interface.AgentInput.md): Properties
114
+ - [Interface: AgentRunContext](./docs/api/appkit/Interface.AgentRunContext.md): Properties
115
+ - [Interface: AgentsPluginConfig](./docs/api/appkit/Interface.AgentsPluginConfig.md): Base configuration interface for AppKit plugins
116
+ - [Interface: AgentToolDefinition](./docs/api/appkit/Interface.AgentToolDefinition.md): Properties
117
+ - [Interface: AutoInheritToolsConfig](./docs/api/appkit/Interface.AutoInheritToolsConfig.md): Auto-inherit configuration. When enabled for a given agent origin, agents
92
118
  - [Interface: BasePluginConfig](./docs/api/appkit/Interface.BasePluginConfig.md): Base configuration interface for AppKit plugins
93
119
  - [Interface: CacheConfig](./docs/api/appkit/Interface.CacheConfig.md): Configuration for the CacheInterceptor. Controls TTL, size limits, storage backend, and probabilistic cleanup.
94
120
  - [Interface: DatabaseCredential](./docs/api/appkit/Interface.DatabaseCredential.md): Database credentials with OAuth token for Postgres connection
95
121
  - [Interface: EndpointConfig](./docs/api/appkit/Interface.EndpointConfig.md): Properties
96
122
  - [Interface: FilePolicyUser](./docs/api/appkit/Interface.FilePolicyUser.md): Minimal user identity passed to the policy function.
97
123
  - [Interface: FileResource](./docs/api/appkit/Interface.FileResource.md): Describes the file or directory being acted upon.
124
+ - [Interface: FunctionTool](./docs/api/appkit/Interface.FunctionTool.md): Properties
98
125
  - [Interface: GenerateDatabaseCredentialRequest](./docs/api/appkit/Interface.GenerateDatabaseCredentialRequest.md): Request parameters for generating database OAuth credentials
99
126
  - [Interface: IJobsConfig](./docs/api/appkit/Interface.IJobsConfig.md): Configuration for the Jobs plugin.
100
127
  - [Interface: ITelemetry](./docs/api/appkit/Interface.ITelemetry.md): Plugin-facing interface for OpenTelemetry instrumentation.
@@ -102,28 +129,53 @@ npx @databricks/appkit docs <query>
102
129
  - [Interface: JobConfig](./docs/api/appkit/Interface.JobConfig.md): Per-job configuration options.
103
130
  - [Interface: JobsConnectorConfig](./docs/api/appkit/Interface.JobsConnectorConfig.md): Properties
104
131
  - [Interface: LakebasePoolConfig](./docs/api/appkit/Interface.LakebasePoolConfig.md): Configuration for creating a Lakebase connection pool
132
+ - [Interface: McpConnectAllResult](./docs/api/appkit/Interface.McpConnectAllResult.md): Per-endpoint outcome of AppKitMcpClient.connectAll. Callers (the
133
+ - [Interface: Message](./docs/api/appkit/Interface.Message.md): Properties
105
134
  - [Interface: PluginManifest<TName>](./docs/api/appkit/Interface.PluginManifest.md): Plugin manifest that declares metadata and resource requirements.
135
+ - [Interface: PluginToolkitProvider](./docs/api/appkit/Interface.PluginToolkitProvider.md): Minimum shape every entry in the Plugins map must expose. Core
136
+ - [Interface: PromptContext](./docs/api/appkit/Interface.PromptContext.md): Context passed to baseSystemPrompt callbacks.
137
+ - [Interface: RegisteredAgent](./docs/api/appkit/Interface.RegisteredAgent.md): Properties
106
138
  - [Interface: RequestedClaims](./docs/api/appkit/Interface.RequestedClaims.md): Optional claims for fine-grained Unity Catalog table permissions
107
139
  - [Interface: RequestedResource](./docs/api/appkit/Interface.RequestedResource.md): Resource to request permissions for in Unity Catalog
108
140
  - [Interface: ResourceEntry](./docs/api/appkit/Interface.ResourceEntry.md): Internal representation of a resource in the registry.
109
141
  - [Interface: ResourceFieldEntry](./docs/api/appkit/Interface.ResourceFieldEntry.md): Defines a single field for a resource. Each field has its own environment variable and optional description. Single-value types use one key (e.g. id); multi-value types (database, secret) use multiple (e.g. instancename, databasename or scope, key).
110
142
  - [Interface: ResourceRequirement](./docs/api/appkit/Interface.ResourceRequirement.md): Declares a resource requirement for a plugin.
143
+ - [Interface: RunAgentInput](./docs/api/appkit/Interface.RunAgentInput.md): Properties
144
+ - [Interface: RunAgentResult](./docs/api/appkit/Interface.RunAgentResult.md): Properties
111
145
  - [Interface: ServingEndpointEntry](./docs/api/appkit/Interface.ServingEndpointEntry.md): Shape of a single registry entry.
112
146
  - [Interface: ServingEndpointRegistry](./docs/api/appkit/Interface.ServingEndpointRegistry.md): Registry interface for serving endpoint type generation.
113
147
  - [Interface: StreamExecutionSettings](./docs/api/appkit/Interface.StreamExecutionSettings.md): Execution settings for streaming endpoints. Extends PluginExecutionSettings with SSE stream configuration.
114
148
  - [Interface: TelemetryConfig](./docs/api/appkit/Interface.TelemetryConfig.md): OpenTelemetry configuration for AppKit applications
149
+ - [Interface: Thread](./docs/api/appkit/Interface.Thread.md): Properties
150
+ - [Interface: ThreadStore](./docs/api/appkit/Interface.ThreadStore.md): Methods
151
+ - [Interface: ToolAnnotations](./docs/api/appkit/Interface.ToolAnnotations.md): Properties
152
+ - [Interface: ToolConfig<S>](./docs/api/appkit/Interface.ToolConfig.md): Type Parameters
153
+ - [Interface: ToolEntry<S>](./docs/api/appkit/Interface.ToolEntry.md): Single-tool entry for a plugin's internal tool registry.
154
+ - [Interface: ToolkitEntry](./docs/api/appkit/Interface.ToolkitEntry.md): A tool reference produced by a plugin's .toolkit() call. The agents plugin
155
+ - [Interface: ToolkitOptions](./docs/api/appkit/Interface.ToolkitOptions.md): Properties
156
+ - [Interface: ToolProvider](./docs/api/appkit/Interface.ToolProvider.md): Methods
115
157
  - [Interface: ValidationResult](./docs/api/appkit/Interface.ValidationResult.md): Result of validating all registered resources against the environment.
158
+ - [Type Alias: AgentEvent](./docs/api/appkit/TypeAlias.AgentEvent.md): Type Declaration
159
+ - [Type Alias: AgentTool](./docs/api/appkit/TypeAlias.AgentTool.md): Any tool an agent can invoke: inline function tools (tool()), hosted MCP
160
+ - [Type Alias: AgentTools](./docs/api/appkit/TypeAlias.AgentTools.md): Per-agent tool record. String keys map to inline tools, toolkit entries,
161
+ - [Type Alias: AgentToolsFn()](./docs/api/appkit/TypeAlias.AgentToolsFn.md): Function form of AgentDefinition.tools. Receives the typed
162
+ - [Type Alias: BaseSystemPromptOption](./docs/api/appkit/TypeAlias.BaseSystemPromptOption.md)
116
163
  - [Type Alias: ConfigSchema](./docs/api/appkit/TypeAlias.ConfigSchema.md): Configuration schema definition for plugin config.
117
164
  - [Type Alias: ExecutionResult<T>](./docs/api/appkit/TypeAlias.ExecutionResult.md): Discriminated union for plugin execution results.
118
165
  - [Type Alias: FileAction](./docs/api/appkit/TypeAlias.FileAction.md): Every action the files plugin can perform.
119
166
  - [Type Alias: FilePolicy()](./docs/api/appkit/TypeAlias.FilePolicy.md): A policy function that decides whether user may perform action on
167
+ - [Type Alias: HostedTool](./docs/api/appkit/TypeAlias.HostedTool.md)
120
168
  - [Type Alias: IAppRouter](./docs/api/appkit/TypeAlias.IAppRouter.md): Express router type for plugin route registration
121
169
  - [Type Alias: JobHandle](./docs/api/appkit/TypeAlias.JobHandle.md): Job handle returned by appkit.jobs("etl").
122
170
  - [Type Alias: JobsExport()](./docs/api/appkit/TypeAlias.JobsExport.md): Public API shape of the jobs plugin.
123
171
  - [Type Alias: PluginData<T, U, N>](./docs/api/appkit/TypeAlias.PluginData.md): Tuple of plugin class, config, and name. Created by toPlugin() and passed to createApp().
172
+ - [Type Alias: Plugins](./docs/api/appkit/TypeAlias.Plugins.md): Plugin map passed to the function form of AgentDefinition.tools.
173
+ - [Type Alias: ResolvedToolEntry](./docs/api/appkit/TypeAlias.ResolvedToolEntry.md): Internal tool-index entry after a tool record has been resolved to a dispatchable form.
124
174
  - [Type Alias: ResourcePermission](./docs/api/appkit/TypeAlias.ResourcePermission.md): Union of all possible permission levels across all resource types.
125
175
  - [Type Alias: ServingFactory](./docs/api/appkit/TypeAlias.ServingFactory.md): Factory function returned by AppKit.serving.
176
+ - [Type Alias: ToolRegistry](./docs/api/appkit/TypeAlias.ToolRegistry.md)
126
177
  - [Type Alias: ToPlugin()<T, U, N>](./docs/api/appkit/TypeAlias.ToPlugin.md): Factory function type returned by toPlugin(). Accepts optional config and returns a PluginData tuple.
178
+ - [Variable: agents](./docs/api/appkit/Variable.agents.md): Plugin factory for the agents plugin. Reads config/agents/*.md by default,
127
179
  - [Variable: READ_ACTIONS](./docs/api/appkit/Variable.READ_ACTIONS.md): Actions that only read data.
128
180
  - [Variable: sql](./docs/api/appkit/Variable.sql.md): SQL helper namespace
129
181
  - [Variable: WRITE_ACTIONS](./docs/api/appkit/Variable.WRITE_ACTIONS.md): Actions that mutate data.
package/NOTICE.md CHANGED
@@ -66,6 +66,7 @@ This Software contains code from the following open source projects:
66
66
  | [express](https://www.npmjs.com/package/express) | 4.22.0 | MIT | http://expressjs.com/ |
67
67
  | [get-port](https://www.npmjs.com/package/get-port) | 7.2.0 | MIT | https://github.com/sindresorhus/get-port#readme |
68
68
  | [input-otp](https://www.npmjs.com/package/input-otp) | 1.4.2 | MIT | https://input-otp.rodz.dev/ |
69
+ | [js-yaml](https://www.npmjs.com/package/js-yaml) | 3.14.2, 4.1.1 | MIT | https://github.com/nodeca/js-yaml#readme |
69
70
  | [lucide-react](https://www.npmjs.com/package/lucide-react) | 0.554.0 | ISC | https://lucide.dev |
70
71
  | [marked](https://www.npmjs.com/package/marked) | 16.4.2, 17.0.3 | MIT | https://marked.js.org |
71
72
  | [next-themes](https://www.npmjs.com/package/next-themes) | 0.4.6 | MIT | https://github.com/pacocoursey/next-themes#readme |
package/dist/js/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isSQLTypeMarker, sql } from "../shared/src/sql/helpers.js";
2
+ import "../shared/src/index.js";
2
3
  import { DATE_FIELD_PATTERNS, METADATA_DATE_PATTERNS, NAME_FIELD_PATTERNS } from "./constants.js";
3
4
  import { getArrowModule, initializeTypeIdSets } from "./arrow/lazy-arrow.js";
4
5
  import { ArrowClient } from "./arrow/arrow-client.js";
@@ -1,5 +1,6 @@
1
1
  import { UseChartDataOptions, UseChartDataResult, useChartData } from "./use-chart-data.js";
2
2
  import { AnalyticsFormat, InferResultByFormat, InferRowType, InferServingChunk, InferServingRequest, InferServingResponse, PluginRegistry, QueryRegistry, ServingAlias, ServingEndpointRegistry, TypedArrowTable, UseAnalyticsQueryOptions, UseAnalyticsQueryResult } from "./types.js";
3
+ import { AgentChatEvent, UseAgentChatOptions, UseAgentChatResult, useAgentChat } from "./use-agent-chat.js";
3
4
  import { useAnalyticsQuery } from "./use-analytics-query.js";
4
5
  import { usePluginClientConfig } from "./use-plugin-config.js";
5
6
  import { UseServingInvokeOptions, UseServingInvokeResult, useServingInvoke } from "./use-serving-invoke.js";
@@ -1,5 +1,6 @@
1
1
  import { useAnalyticsQuery } from "./use-analytics-query.js";
2
2
  import { useChartData } from "./use-chart-data.js";
3
+ import { useAgentChat } from "./use-agent-chat.js";
3
4
  import { usePluginClientConfig } from "./use-plugin-config.js";
4
5
  import { useServingInvoke } from "./use-serving-invoke.js";
5
6
  import { useServingStream } from "./use-serving-stream.js";
@@ -1,8 +1,14 @@
1
1
  import { Table } from "apache-arrow";
2
2
 
3
3
  //#region src/react/hooks/types.d.ts
4
- /** Supported response formats for analytics queries */
5
- type AnalyticsFormat = "JSON" | "ARROW";
4
+ /**
5
+ * Supported response formats for analytics queries.
6
+ *
7
+ * "JSON" and "ARROW" are legacy aliases kept for backwards compatibility
8
+ * with appkit/appkit-ui < 0.33.0 — safe to remove once no consumer is on
9
+ * a pre-0.33.0 version.
10
+ */
11
+ type AnalyticsFormat = "JSON_ARRAY" | "ARROW_STREAM" /** @deprecated Use "JSON_ARRAY". Safe to remove once no consumer is on appkit-ui < 0.33.0. */ | "JSON" /** @deprecated Use "ARROW_STREAM". Safe to remove once no consumer is on appkit-ui < 0.33.0. */ | "ARROW";
6
12
  /**
7
13
  * Typed Arrow Table - preserves row type information for type inference.
8
14
  * At runtime this is just a regular Arrow Table, but TypeScript knows the row schema.
@@ -21,8 +27,8 @@ interface TypedArrowTable<TRow extends Record<string, unknown> = Record<string,
21
27
  readonly __rowType?: TRow;
22
28
  }
23
29
  /** Options for configuring an analytics SSE query */
24
- interface UseAnalyticsQueryOptions<F extends AnalyticsFormat = "JSON"> {
25
- /** Response format - "JSON" returns typed arrays, "ARROW" returns TypedArrowTable */
30
+ interface UseAnalyticsQueryOptions<F extends AnalyticsFormat = "JSON_ARRAY"> {
31
+ /** Response format - "JSON_ARRAY" returns typed arrays, "ARROW_STREAM" returns TypedArrowTable */
26
32
  format?: F;
27
33
  /** Maximum size of serialized parameters in bytes */
28
34
  maxParametersSize?: number;
@@ -86,7 +92,7 @@ type InferRowType<K> = K extends AugmentedRegistry<QueryRegistry> ? QueryRegistr
86
92
  * - JSON format: Returns the typed array from QueryRegistry
87
93
  * - ARROW format: Returns TypedArrowTable with row type preserved
88
94
  */
89
- type InferResultByFormat<T, K, F extends AnalyticsFormat> = F extends "ARROW" ? TypedArrowTable<InferRowType<K>> : InferResult<T, K>;
95
+ type InferResultByFormat<T, K, F extends AnalyticsFormat> = F extends "ARROW_STREAM" | "ARROW" ? TypedArrowTable<InferRowType<K>> : InferResult<T, K>;
90
96
  /**
91
97
  * Infers parameters type from QueryRegistry[K]["parameters"]
92
98
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/react/hooks/types.ts"],"mappings":";;;;KAOY,eAAA;AAAZ;;;;;AAYA;;;;;AAZA,UAYiB,eAAA,cACF,MAAA,oBAA0B,MAAA,2BAC/B,KAAA;EAAA;;;;EAAA,SAKC,SAAA,GAAY,IAAA;AAAA;;UAQN,wBAAA,WAAmC,eAAA;EAR7B;EAUrB,MAAA,GAAS,CAAA;EAVgB;EAazB,iBAAA;EALuC;EAQvC,SAAA;AAAA;;UAIe,uBAAA;EAVf;EAYA,IAAA,EAAM,CAAA;EATN;EAWA,OAAA;EARS;EAUT,KAAA;AAAA;;;;;;;;;;;AAqBF;;;;;;;;UAAiB,aAAA;EAAA,CACd,GAAA;IACC,IAAA;IACA,UAAA,EAAY,MAAA;IACZ,MAAA;EAAA;AAAA;;KAKQ,iBAAA,0BACE,CAAA,mBAAoB,CAAA,WAAY,CAAA,GAAI,CAAA,CAAE,CAAA;;KAIxC,QAAA,GAAW,iBAAA,CAAkB,aAAA,2BAErC,iBAAA,CAAkB,aAAA;;;;;KAMV,WAAA,SAAoB,CAAA,SAAU,iBAAA,CAAkB,aAAA,IACxD,aAAA,CAAc,CAAA;EAAa,MAAA;AAAA,IACzB,CAAA,GACA,CAAA,GACF,CAAA;;;AAZJ;;KAkBY,YAAA,MAAkB,CAAA,SAAU,iBAAA,CAAkB,aAAA,IACtD,aAAA,CAAc,CAAA;EAAa,MAAA,EAAQ,KAAA;AAAA,IACjC,CAAA,SAAU,MAAA,oBACR,CAAA,GACA,MAAA,oBACF,MAAA,oBACF,MAAA;;;;;;KAOQ,mBAAA,iBAGA,eAAA,IACR,CAAA,mBAAoB,eAAA,CAAgB,YAAA,CAAa,CAAA,KAAM,WAAA,CAAY,CAAA,EAAG,CAAA;;;;KAK9D,WAAA,MAAiB,CAAA,SAAU,iBAAA,CAAkB,aAAA,IACrD,aAAA,CAAc,CAAA;EAAa,UAAA;AAAA,IACzB,CAAA,GACA,MAAA,oBACF,MAAA;AAAA,UAEa,cAAA;EAAA,CACd,GAAA,WAAc,MAAA;AAAA;;;;;;;;;;;;;;;UA2BA,uBAAA;;KAGL,YAAA,GACV,iBAAA,CAAkB,uBAAA,2BAEd,iBAAA,CAAkB,uBAAA;;KAGZ,iBAAA,MACV,CAAA,SAAU,iBAAA,CAAkB,uBAAA,IACxB,uBAAA,CAAwB,CAAA;EAAa,KAAA;AAAA,IACnC,CAAA;;KAKI,oBAAA,MACV,CAAA,SAAU,iBAAA,CAAkB,uBAAA,IACxB,uBAAA,CAAwB,CAAA;EAAa,QAAA;AAAA,IACnC,CAAA;;KAKI,mBAAA,MACV,CAAA,SAAU,iBAAA,CAAkB,uBAAA,IACxB,uBAAA,CAAwB,CAAA;EAAa,OAAA;AAAA,IACnC,GAAA,GACA,MAAA,oBACF,MAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/react/hooks/types.ts"],"mappings":";;;;;AAaA;;;;;KAAY,eAAA;;;;;;;;;;;UAkBK,eAAA,cACF,MAAA,oBAA0B,MAAA,2BAC/B,KAAA;EAKiB;AAQ3B;;;EAR2B,SAAhB,SAAA,GAAY,IAAA;AAAA;;UAQN,wBAAA,WACL,eAAA;EAGD;EAAT,MAAA,GAAS,CAAA;EAMT;EAHA,iBAAA;EAGS;EAAT,SAAA;AAAA;;UAIe,uBAAA;EAAwB;EAEvC,IAAA,EAAM,CAAA;EAAA;EAEN,OAAA;EAEA;EAAA,KAAA;AAAA;AAqBF;;;;;;;;;;;AASA;;;;;;;AATA,UAAiB,aAAA;EAAA,CACd,GAAA;IACC,IAAA;IACA,UAAA,EAAY,MAAA;IACZ,MAAA;EAAA;AAAA;;KAKQ,iBAAA,0BACE,CAAA,mBAAoB,CAAA,WAAY,CAAA,GAAI,CAAA,CAAE,CAAA;;KAIxC,QAAA,GAAW,iBAAA,CAAkB,aAAA,2BAErC,iBAAA,CAAkB,aAAA;;;;;KAMV,WAAA,SAAoB,CAAA,SAAU,iBAAA,CAAkB,aAAA,IACxD,aAAA,CAAc,CAAA;EAAa,MAAA;AAAA,IACzB,CAAA,GACA,CAAA,GACF,CAAA;;;;;KAMQ,YAAA,MAAkB,CAAA,SAAU,iBAAA,CAAkB,aAAA,IACtD,aAAA,CAAc,CAAA;EAAa,MAAA,EAAQ,KAAA;AAAA,IACjC,CAAA,SAAU,MAAA,oBACR,CAAA,GACA,MAAA,oBACF,MAAA,oBACF,MAAA;AAhBJ;;;;;AAAA,KAuBY,mBAAA,iBAAoC,eAAA,IAAmB,CAAA,oCAG/D,eAAA,CAAgB,YAAA,CAAa,CAAA,KAC7B,WAAA,CAAY,CAAA,EAAG,CAAA;;;;KAKP,WAAA,MAAiB,CAAA,SAAU,iBAAA,CAAkB,aAAA,IACrD,aAAA,CAAc,CAAA;EAAa,UAAA;AAAA,IACzB,CAAA,GACA,MAAA,oBACF,MAAA;AAAA,UAEa,cAAA;EAAA,CACd,GAAA,WAAc,MAAA;AAAA;;;;;;;;AA7BjB;;;;;;;UAwDiB,uBAAA;;KAGL,YAAA,GACV,iBAAA,CAAkB,uBAAA,2BAEd,iBAAA,CAAkB,uBAAA;;KAGZ,iBAAA,MACV,CAAA,SAAU,iBAAA,CAAkB,uBAAA,IACxB,uBAAA,CAAwB,CAAA;EAAa,KAAA;AAAA,IACnC,CAAA;;KAKI,oBAAA,MACV,CAAA,SAAU,iBAAA,CAAkB,uBAAA,IACxB,uBAAA,CAAwB,CAAA;EAAa,QAAA;AAAA,IACnC,CAAA;;KAKI,mBAAA,MACV,CAAA,SAAU,iBAAA,CAAkB,uBAAA,IACxB,uBAAA,CAAwB,CAAA;EAAa,OAAA;AAAA,IACnC,GAAA,GACA,MAAA,oBACF,MAAA"}
@@ -0,0 +1,128 @@
1
+ //#region src/react/hooks/use-agent-chat.d.ts
2
+ /**
3
+ * One Responses-API-shaped event yielded by the agents plugin SSE stream.
4
+ *
5
+ * The hook handles the two paths every chat UI needs — accumulating
6
+ * `content` from `response.output_text.delta` and capturing `threadId`
7
+ * from `appkit.metadata` — and surfaces everything else (tool calls,
8
+ * approval gates, status events, etc.) through {@link UseAgentChatOptions.onEvent}.
9
+ *
10
+ * Fields beyond `type` are intentionally loose because the agents plugin
11
+ * forwards adapter-specific shapes verbatim. Treat unknown fields as
12
+ * opaque pass-through.
13
+ */
14
+ interface AgentChatEvent {
15
+ type: string;
16
+ delta?: string;
17
+ item_id?: string;
18
+ item?: {
19
+ type?: string;
20
+ id?: string;
21
+ call_id?: string;
22
+ name?: string;
23
+ arguments?: string;
24
+ output?: string;
25
+ status?: string;
26
+ };
27
+ content?: string;
28
+ data?: Record<string, unknown>;
29
+ error?: string;
30
+ sequence_number?: number;
31
+ output_index?: number;
32
+ approval_id?: string;
33
+ stream_id?: string;
34
+ tool_name?: string;
35
+ args?: unknown;
36
+ annotations?: Record<string, unknown>;
37
+ }
38
+ interface UseAgentChatOptions {
39
+ /**
40
+ * Agent name registered with the `agents()` plugin (e.g. `"assistant"`,
41
+ * `"helper"`). Send-time payload includes this so the plugin routes the
42
+ * turn to the right `AgentDefinition`.
43
+ */
44
+ agent: string;
45
+ /**
46
+ * Override the chat endpoint. Default `"/api/agents/chat"` matches the
47
+ * route the agents plugin mounts under its prefix. Useful when the
48
+ * server mounts under a non-default base path or when proxying.
49
+ */
50
+ endpoint?: string;
51
+ /**
52
+ * Called for every parsed SSE event before any state update. Use this
53
+ * to drive tool-call rows, approval cards, inspectors, or anything
54
+ * beyond the streaming text content. Errors thrown here are swallowed
55
+ * so a buggy handler can't kill the stream.
56
+ */
57
+ onEvent?: (event: AgentChatEvent) => void;
58
+ }
59
+ interface UseAgentChatResult {
60
+ /** Accumulated assistant text from `response.output_text.delta` events. */
61
+ content: string;
62
+ /**
63
+ * Every parsed event, in order. Provided for components that need to
64
+ * render historical tool calls or replay state after a remount —
65
+ * lighter than re-deriving from message history. For one-off side
66
+ * effects prefer {@link UseAgentChatOptions.onEvent}.
67
+ */
68
+ events: AgentChatEvent[];
69
+ /**
70
+ * Thread id captured from the first `appkit.metadata` event of the
71
+ * stream. Subsequent `send()` calls automatically forward this so the
72
+ * server reuses the same thread.
73
+ */
74
+ threadId: string | null;
75
+ /** True while an SSE stream is open. */
76
+ isStreaming: boolean;
77
+ /** Last error message (cleared on next successful `send()`). */
78
+ error: string | null;
79
+ /**
80
+ * Send a user turn and stream the response. Aborts any in-flight
81
+ * stream. Resolves when the stream completes (success or error).
82
+ */
83
+ send: (message: string) => Promise<void>;
84
+ /**
85
+ * Discard accumulated content, events, and threadId. Aborts any
86
+ * in-flight stream. Use when switching agents or starting a fresh
87
+ * conversation.
88
+ */
89
+ reset: () => void;
90
+ }
91
+ /**
92
+ * React hook for chatting with an agent registered via the `agents()`
93
+ * plugin. Wraps {@link connectSSE} (which owns the buffer cap, abort
94
+ * composition, retry/backoff, and frame parsing) with the small amount
95
+ * of stateful glue every chat UI needs: accumulated assistant text,
96
+ * thread id, streaming flag, and an event callback.
97
+ *
98
+ * The hook is intentionally lower-level than a full chat component —
99
+ * it owns one stream at a time, not a multi-turn message history. The
100
+ * caller composes its own messages array (typically a `useState`) and
101
+ * appends to it via the `onEvent` callback for tool calls and via the
102
+ * `content` field for assistant text.
103
+ *
104
+ * @example
105
+ * ```tsx
106
+ * function Chat({ agent }: { agent: string }) {
107
+ * const [messages, setMessages] = useState<Message[]>([]);
108
+ * const { content, threadId, isStreaming, send, reset } = useAgentChat({
109
+ * agent,
110
+ * onEvent(ev) {
111
+ * if (ev.type === "response.output_item.added" && ev.item?.type === "function_call") {
112
+ * setMessages((m) => [...m, { role: "tool", name: ev.item?.name, args: ev.item?.arguments }]);
113
+ * }
114
+ * },
115
+ * });
116
+ * // `content` reflects the latest assistant turn; reset() between conversations.
117
+ * // ...
118
+ * }
119
+ * ```
120
+ */
121
+ declare function useAgentChat({
122
+ agent,
123
+ endpoint,
124
+ onEvent
125
+ }: UseAgentChatOptions): UseAgentChatResult;
126
+ //#endregion
127
+ export { AgentChatEvent, UseAgentChatOptions, UseAgentChatResult, useAgentChat };
128
+ //# sourceMappingURL=use-agent-chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent-chat.d.ts","names":[],"sources":["../../../src/react/hooks/use-agent-chat.ts"],"mappings":";;AAeA;;;;;;;;;;;UAAiB,cAAA;EACf,IAAA;EACA,KAAA;EACA,OAAA;EACA,IAAA;IACE,IAAA;IACA,EAAA;IACA,OAAA;IACA,IAAA;IACA,SAAA;IACA,MAAA;IACA,MAAA;EAAA;EAEF,OAAA;EACA,IAAA,GAAO,MAAA;EACP,KAAA;EACA,eAAA;EACA,YAAA;EAEA,WAAA;EACA,SAAA;EACA,SAAA;EACA,IAAA;EACA,WAAA,GAAc,MAAA;AAAA;AAAA,UAGC,mBAAA;EAmBG;;;;AAGpB;EAhBE,KAAA;;;;;;EAMA,QAAA;EA2BA;;;;;;EApBA,OAAA,IAAW,KAAA,EAAO,cAAA;AAAA;AAAA,UAGH,kBAAA;EA+DW;EA7D1B,OAAA;EA8DA;;;;;;EAvDA,MAAA,EAAQ,cAAA;EAuDR;;;;;EAjDA,QAAA;EAoDC;EAlDD,WAAA;EAkDyC;EAhDzC,KAAA;;;;;EAKA,IAAA,GAAO,OAAA,aAAoB,OAAA;;;;;;EAM3B,KAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCc,YAAA,CAAA;EACd,KAAA;EACA,QAAA;EACA;AAAA,GACC,mBAAA,GAAsB,kBAAA"}
@@ -0,0 +1,133 @@
1
+ import { connectSSE } from "../../js/sse/connect-sse.js";
2
+ import "../../js/index.js";
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
+
5
+ //#region src/react/hooks/use-agent-chat.ts
6
+ /**
7
+ * React hook for chatting with an agent registered via the `agents()`
8
+ * plugin. Wraps {@link connectSSE} (which owns the buffer cap, abort
9
+ * composition, retry/backoff, and frame parsing) with the small amount
10
+ * of stateful glue every chat UI needs: accumulated assistant text,
11
+ * thread id, streaming flag, and an event callback.
12
+ *
13
+ * The hook is intentionally lower-level than a full chat component —
14
+ * it owns one stream at a time, not a multi-turn message history. The
15
+ * caller composes its own messages array (typically a `useState`) and
16
+ * appends to it via the `onEvent` callback for tool calls and via the
17
+ * `content` field for assistant text.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * function Chat({ agent }: { agent: string }) {
22
+ * const [messages, setMessages] = useState<Message[]>([]);
23
+ * const { content, threadId, isStreaming, send, reset } = useAgentChat({
24
+ * agent,
25
+ * onEvent(ev) {
26
+ * if (ev.type === "response.output_item.added" && ev.item?.type === "function_call") {
27
+ * setMessages((m) => [...m, { role: "tool", name: ev.item?.name, args: ev.item?.arguments }]);
28
+ * }
29
+ * },
30
+ * });
31
+ * // `content` reflects the latest assistant turn; reset() between conversations.
32
+ * // ...
33
+ * }
34
+ * ```
35
+ */
36
+ function useAgentChat({ agent, endpoint = "/api/agents/chat", onEvent }) {
37
+ const [content, setContent] = useState("");
38
+ const [events, setEvents] = useState([]);
39
+ const [threadId, setThreadId] = useState(null);
40
+ const [isStreaming, setIsStreaming] = useState(false);
41
+ const [error, setError] = useState(null);
42
+ const threadIdRef = useRef(null);
43
+ const contentRef = useRef("");
44
+ const onEventRef = useRef(onEvent);
45
+ onEventRef.current = onEvent;
46
+ const abortControllerRef = useRef(null);
47
+ const reset = useCallback(() => {
48
+ abortControllerRef.current?.abort();
49
+ abortControllerRef.current = null;
50
+ threadIdRef.current = null;
51
+ contentRef.current = "";
52
+ setContent("");
53
+ setEvents([]);
54
+ setThreadId(null);
55
+ setIsStreaming(false);
56
+ setError(null);
57
+ }, []);
58
+ const send = useCallback(async (message) => {
59
+ abortControllerRef.current?.abort();
60
+ const controller = new AbortController();
61
+ abortControllerRef.current = controller;
62
+ contentRef.current = "";
63
+ setContent("");
64
+ setEvents([]);
65
+ setError(null);
66
+ setIsStreaming(true);
67
+ const payload = {
68
+ message,
69
+ agent,
70
+ ...threadIdRef.current ? { threadId: threadIdRef.current } : {}
71
+ };
72
+ try {
73
+ await connectSSE({
74
+ url: endpoint,
75
+ payload,
76
+ signal: controller.signal,
77
+ maxRetries: 0,
78
+ onMessage: async ({ data }) => {
79
+ if (controller.signal.aborted) return;
80
+ if (!data || data === "[DONE]") return;
81
+ let event;
82
+ try {
83
+ event = JSON.parse(data);
84
+ } catch {
85
+ return;
86
+ }
87
+ if (!event.type) return;
88
+ try {
89
+ onEventRef.current?.(event);
90
+ } catch {}
91
+ setEvents((prev) => [...prev, event]);
92
+ if (event.type === "appkit.metadata") {
93
+ const tid = event.data?.threadId;
94
+ if (typeof tid === "string") {
95
+ threadIdRef.current = tid;
96
+ setThreadId(tid);
97
+ }
98
+ } else if (event.type === "response.output_text.delta" && typeof event.delta === "string") {
99
+ contentRef.current += event.delta;
100
+ setContent(contentRef.current);
101
+ }
102
+ },
103
+ onError: (err) => {
104
+ if (controller.signal.aborted) return;
105
+ setError(err instanceof Error ? err.message : "Chat stream failed");
106
+ }
107
+ });
108
+ } catch (err) {
109
+ if (!controller.signal.aborted) setError(err instanceof Error ? err.message : "Chat stream failed");
110
+ } finally {
111
+ if (abortControllerRef.current === controller) abortControllerRef.current = null;
112
+ setIsStreaming(false);
113
+ }
114
+ }, [agent, endpoint]);
115
+ useEffect(() => {
116
+ return () => {
117
+ abortControllerRef.current?.abort();
118
+ };
119
+ }, []);
120
+ return {
121
+ content,
122
+ events,
123
+ threadId,
124
+ isStreaming,
125
+ error,
126
+ send,
127
+ reset
128
+ };
129
+ }
130
+
131
+ //#endregion
132
+ export { useAgentChat };
133
+ //# sourceMappingURL=use-agent-chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent-chat.js","names":[],"sources":["../../../src/react/hooks/use-agent-chat.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { connectSSE } from \"@/js\";\n\n/**\n * One Responses-API-shaped event yielded by the agents plugin SSE stream.\n *\n * The hook handles the two paths every chat UI needs — accumulating\n * `content` from `response.output_text.delta` and capturing `threadId`\n * from `appkit.metadata` — and surfaces everything else (tool calls,\n * approval gates, status events, etc.) through {@link UseAgentChatOptions.onEvent}.\n *\n * Fields beyond `type` are intentionally loose because the agents plugin\n * forwards adapter-specific shapes verbatim. Treat unknown fields as\n * opaque pass-through.\n */\nexport interface AgentChatEvent {\n type: string;\n delta?: string;\n item_id?: string;\n item?: {\n type?: string;\n id?: string;\n call_id?: string;\n name?: string;\n arguments?: string;\n output?: string;\n status?: string;\n };\n content?: string;\n data?: Record<string, unknown>;\n error?: string;\n sequence_number?: number;\n output_index?: number;\n // `appkit.approval_pending` payload\n approval_id?: string;\n stream_id?: string;\n tool_name?: string;\n args?: unknown;\n annotations?: Record<string, unknown>;\n}\n\nexport interface UseAgentChatOptions {\n /**\n * Agent name registered with the `agents()` plugin (e.g. `\"assistant\"`,\n * `\"helper\"`). Send-time payload includes this so the plugin routes the\n * turn to the right `AgentDefinition`.\n */\n agent: string;\n /**\n * Override the chat endpoint. Default `\"/api/agents/chat\"` matches the\n * route the agents plugin mounts under its prefix. Useful when the\n * server mounts under a non-default base path or when proxying.\n */\n endpoint?: string;\n /**\n * Called for every parsed SSE event before any state update. Use this\n * to drive tool-call rows, approval cards, inspectors, or anything\n * beyond the streaming text content. Errors thrown here are swallowed\n * so a buggy handler can't kill the stream.\n */\n onEvent?: (event: AgentChatEvent) => void;\n}\n\nexport interface UseAgentChatResult {\n /** Accumulated assistant text from `response.output_text.delta` events. */\n content: string;\n /**\n * Every parsed event, in order. Provided for components that need to\n * render historical tool calls or replay state after a remount —\n * lighter than re-deriving from message history. For one-off side\n * effects prefer {@link UseAgentChatOptions.onEvent}.\n */\n events: AgentChatEvent[];\n /**\n * Thread id captured from the first `appkit.metadata` event of the\n * stream. Subsequent `send()` calls automatically forward this so the\n * server reuses the same thread.\n */\n threadId: string | null;\n /** True while an SSE stream is open. */\n isStreaming: boolean;\n /** Last error message (cleared on next successful `send()`). */\n error: string | null;\n /**\n * Send a user turn and stream the response. Aborts any in-flight\n * stream. Resolves when the stream completes (success or error).\n */\n send: (message: string) => Promise<void>;\n /**\n * Discard accumulated content, events, and threadId. Aborts any\n * in-flight stream. Use when switching agents or starting a fresh\n * conversation.\n */\n reset: () => void;\n}\n\n/**\n * React hook for chatting with an agent registered via the `agents()`\n * plugin. Wraps {@link connectSSE} (which owns the buffer cap, abort\n * composition, retry/backoff, and frame parsing) with the small amount\n * of stateful glue every chat UI needs: accumulated assistant text,\n * thread id, streaming flag, and an event callback.\n *\n * The hook is intentionally lower-level than a full chat component —\n * it owns one stream at a time, not a multi-turn message history. The\n * caller composes its own messages array (typically a `useState`) and\n * appends to it via the `onEvent` callback for tool calls and via the\n * `content` field for assistant text.\n *\n * @example\n * ```tsx\n * function Chat({ agent }: { agent: string }) {\n * const [messages, setMessages] = useState<Message[]>([]);\n * const { content, threadId, isStreaming, send, reset } = useAgentChat({\n * agent,\n * onEvent(ev) {\n * if (ev.type === \"response.output_item.added\" && ev.item?.type === \"function_call\") {\n * setMessages((m) => [...m, { role: \"tool\", name: ev.item?.name, args: ev.item?.arguments }]);\n * }\n * },\n * });\n * // `content` reflects the latest assistant turn; reset() between conversations.\n * // ...\n * }\n * ```\n */\nexport function useAgentChat({\n agent,\n endpoint = \"/api/agents/chat\",\n onEvent,\n}: UseAgentChatOptions): UseAgentChatResult {\n const [content, setContent] = useState(\"\");\n const [events, setEvents] = useState<AgentChatEvent[]>([]);\n const [threadId, setThreadId] = useState<string | null>(null);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Refs avoid the standard \"stale closure\" problem with `send` and\n // `onEvent`: `send` is a stable callback that reads the latest\n // threadId/onEvent without re-mounting connectSSE on every render.\n const threadIdRef = useRef<string | null>(null);\n const contentRef = useRef(\"\");\n const onEventRef = useRef(onEvent);\n onEventRef.current = onEvent;\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const reset = useCallback(() => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n threadIdRef.current = null;\n contentRef.current = \"\";\n setContent(\"\");\n setEvents([]);\n setThreadId(null);\n setIsStreaming(false);\n setError(null);\n }, []);\n\n const send = useCallback(\n async (message: string) => {\n // Abort any previous stream — only one chat turn in flight per hook.\n abortControllerRef.current?.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n\n contentRef.current = \"\";\n setContent(\"\");\n setEvents([]);\n setError(null);\n setIsStreaming(true);\n\n const payload = {\n message,\n agent,\n ...(threadIdRef.current ? { threadId: threadIdRef.current } : {}),\n };\n\n try {\n await connectSSE({\n url: endpoint,\n payload,\n signal: controller.signal,\n // Chat turns aren't idempotent — re-sending the payload after a\n // transient failure would either duplicate the user message or\n // depend on server-side Last-Event-ID resumption (the agents\n // plugin's StreamManager supports it, but failure-mode auditing\n // is easier with retries off by default; callers can re-enable\n // via the underlying connectSSE once they understand the\n // resumption contract on their endpoint).\n maxRetries: 0,\n onMessage: async ({ data }) => {\n if (controller.signal.aborted) return;\n if (!data || data === \"[DONE]\") return;\n let event: AgentChatEvent;\n try {\n event = JSON.parse(data) as AgentChatEvent;\n } catch {\n // Skip malformed payloads — the rest of the stream is\n // still useful and the agents plugin recovers on the\n // next event boundary.\n return;\n }\n if (!event.type) return;\n\n // Best-effort: never let an onEvent throw break the stream.\n try {\n onEventRef.current?.(event);\n } catch {\n // swallow\n }\n\n setEvents((prev) => [...prev, event]);\n\n if (event.type === \"appkit.metadata\") {\n const tid = event.data?.threadId;\n if (typeof tid === \"string\") {\n threadIdRef.current = tid;\n setThreadId(tid);\n }\n } else if (\n event.type === \"response.output_text.delta\" &&\n typeof event.delta === \"string\"\n ) {\n contentRef.current += event.delta;\n setContent(contentRef.current);\n }\n },\n onError: (err) => {\n if (controller.signal.aborted) return;\n setError(err instanceof Error ? err.message : \"Chat stream failed\");\n },\n });\n } catch (err) {\n if (!controller.signal.aborted) {\n setError(err instanceof Error ? err.message : \"Chat stream failed\");\n }\n } finally {\n if (abortControllerRef.current === controller) {\n abortControllerRef.current = null;\n }\n setIsStreaming(false);\n }\n },\n [agent, endpoint],\n );\n\n // Abort any in-flight stream when the component unmounts.\n useEffect(() => {\n return () => {\n abortControllerRef.current?.abort();\n };\n }, []);\n\n return {\n content,\n events,\n threadId,\n isStreaming,\n error,\n send,\n reset,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8HA,SAAgB,aAAa,EAC3B,OACA,WAAW,oBACX,WAC0C;CAC1C,MAAM,CAAC,SAAS,cAAc,SAAS,GAAG;CAC1C,MAAM,CAAC,QAAQ,aAAa,SAA2B,EAAE,CAAC;CAC1D,MAAM,CAAC,UAAU,eAAe,SAAwB,KAAK;CAC7D,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CAKvD,MAAM,cAAc,OAAsB,KAAK;CAC/C,MAAM,aAAa,OAAO,GAAG;CAC7B,MAAM,aAAa,OAAO,QAAQ;AAClC,YAAW,UAAU;CACrB,MAAM,qBAAqB,OAA+B,KAAK;CAE/D,MAAM,QAAQ,kBAAkB;AAC9B,qBAAmB,SAAS,OAAO;AACnC,qBAAmB,UAAU;AAC7B,cAAY,UAAU;AACtB,aAAW,UAAU;AACrB,aAAW,GAAG;AACd,YAAU,EAAE,CAAC;AACb,cAAY,KAAK;AACjB,iBAAe,MAAM;AACrB,WAAS,KAAK;IACb,EAAE,CAAC;CAEN,MAAM,OAAO,YACX,OAAO,YAAoB;AAEzB,qBAAmB,SAAS,OAAO;EACnC,MAAM,aAAa,IAAI,iBAAiB;AACxC,qBAAmB,UAAU;AAE7B,aAAW,UAAU;AACrB,aAAW,GAAG;AACd,YAAU,EAAE,CAAC;AACb,WAAS,KAAK;AACd,iBAAe,KAAK;EAEpB,MAAM,UAAU;GACd;GACA;GACA,GAAI,YAAY,UAAU,EAAE,UAAU,YAAY,SAAS,GAAG,EAAE;GACjE;AAED,MAAI;AACF,SAAM,WAAW;IACf,KAAK;IACL;IACA,QAAQ,WAAW;IAQnB,YAAY;IACZ,WAAW,OAAO,EAAE,WAAW;AAC7B,SAAI,WAAW,OAAO,QAAS;AAC/B,SAAI,CAAC,QAAQ,SAAS,SAAU;KAChC,IAAI;AACJ,SAAI;AACF,cAAQ,KAAK,MAAM,KAAK;aAClB;AAIN;;AAEF,SAAI,CAAC,MAAM,KAAM;AAGjB,SAAI;AACF,iBAAW,UAAU,MAAM;aACrB;AAIR,gBAAW,SAAS,CAAC,GAAG,MAAM,MAAM,CAAC;AAErC,SAAI,MAAM,SAAS,mBAAmB;MACpC,MAAM,MAAM,MAAM,MAAM;AACxB,UAAI,OAAO,QAAQ,UAAU;AAC3B,mBAAY,UAAU;AACtB,mBAAY,IAAI;;gBAGlB,MAAM,SAAS,gCACf,OAAO,MAAM,UAAU,UACvB;AACA,iBAAW,WAAW,MAAM;AAC5B,iBAAW,WAAW,QAAQ;;;IAGlC,UAAU,QAAQ;AAChB,SAAI,WAAW,OAAO,QAAS;AAC/B,cAAS,eAAe,QAAQ,IAAI,UAAU,qBAAqB;;IAEtE,CAAC;WACK,KAAK;AACZ,OAAI,CAAC,WAAW,OAAO,QACrB,UAAS,eAAe,QAAQ,IAAI,UAAU,qBAAqB;YAE7D;AACR,OAAI,mBAAmB,YAAY,WACjC,oBAAmB,UAAU;AAE/B,kBAAe,MAAM;;IAGzB,CAAC,OAAO,SAAS,CAClB;AAGD,iBAAgB;AACd,eAAa;AACX,sBAAmB,SAAS,OAAO;;IAEpC,EAAE,CAAC;AAEN,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACD"}
@@ -6,8 +6,8 @@ import { AnalyticsFormat, InferParams, InferResultByFormat, QueryKey, UseAnalyti
6
6
  * Integration hook between client and analytics plugin.
7
7
  *
8
8
  * The return type is automatically inferred based on the format:
9
- * - `format: "JSON"` (default): Returns typed array from QueryRegistry
10
- * - `format: "ARROW"`: Returns TypedArrowTable with row type preserved
9
+ * - `format: "JSON_ARRAY"` (default): Returns typed array from QueryRegistry
10
+ * - `format: "ARROW_STREAM"`: Returns TypedArrowTable with row type preserved
11
11
  *
12
12
  * Note: User context execution is determined by query file naming:
13
13
  * - `queryKey.obo.sql`: Executes as user (OBO = on-behalf-of / user delegation)
@@ -26,11 +26,11 @@ import { AnalyticsFormat, InferParams, InferResultByFormat, QueryKey, UseAnalyti
26
26
  *
27
27
  * @example Arrow format
28
28
  * ```typescript
29
- * const { data } = useAnalyticsQuery("spend_data", params, { format: "ARROW" });
29
+ * const { data } = useAnalyticsQuery("spend_data", params, { format: "ARROW_STREAM" });
30
30
  * // data: TypedArrowTable<{ group_key: string; cost_usd: number; ... }> | null
31
31
  * ```
32
32
  */
33
- declare function useAnalyticsQuery<T = unknown, K extends QueryKey = QueryKey, F extends AnalyticsFormat = "JSON">(queryKey: K, parameters?: InferParams<K> | null, options?: UseAnalyticsQueryOptions<F>): UseAnalyticsQueryResult<InferResultByFormat<T, K, F>>;
33
+ declare function useAnalyticsQuery<T = unknown, K extends QueryKey = QueryKey, F extends AnalyticsFormat = "JSON_ARRAY">(queryKey: K, parameters?: InferParams<K> | null, options?: UseAnalyticsQueryOptions<F>): UseAnalyticsQueryResult<InferResultByFormat<T, K, F>>;
34
34
  //#endregion
35
35
  export { useAnalyticsQuery };
36
36
  //# sourceMappingURL=use-analytics-query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-analytics-query.d.ts","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"mappings":";;;;;AAqDA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,iBAAA,wBAEJ,QAAA,GAAW,QAAA,YACX,eAAA,UAAA,CAEV,QAAA,EAAU,CAAA,EACV,UAAA,GAAa,WAAA,CAAY,CAAA,UACzB,OAAA,GAAS,wBAAA,CAAyB,CAAA,IACjC,uBAAA,CAAwB,mBAAA,CAAoB,CAAA,EAAG,CAAA,EAAG,CAAA"}
1
+ {"version":3,"file":"use-analytics-query.d.ts","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"mappings":";;;;;AAqDA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,iBAAA,wBAEJ,QAAA,GAAW,QAAA,YACX,eAAA,gBAAA,CAEV,QAAA,EAAU,CAAA,EACV,UAAA,GAAa,WAAA,CAAY,CAAA,UACzB,OAAA,GAAS,wBAAA,CAAyB,CAAA,IACjC,uBAAA,CAAwB,mBAAA,CAAoB,CAAA,EAAG,CAAA,EAAG,CAAA"}
@@ -17,8 +17,8 @@ function getArrowStreamUrl(id) {
17
17
  * Integration hook between client and analytics plugin.
18
18
  *
19
19
  * The return type is automatically inferred based on the format:
20
- * - `format: "JSON"` (default): Returns typed array from QueryRegistry
21
- * - `format: "ARROW"`: Returns TypedArrowTable with row type preserved
20
+ * - `format: "JSON_ARRAY"` (default): Returns typed array from QueryRegistry
21
+ * - `format: "ARROW_STREAM"`: Returns TypedArrowTable with row type preserved
22
22
  *
23
23
  * Note: User context execution is determined by query file naming:
24
24
  * - `queryKey.obo.sql`: Executes as user (OBO = on-behalf-of / user delegation)
@@ -37,12 +37,12 @@ function getArrowStreamUrl(id) {
37
37
  *
38
38
  * @example Arrow format
39
39
  * ```typescript
40
- * const { data } = useAnalyticsQuery("spend_data", params, { format: "ARROW" });
40
+ * const { data } = useAnalyticsQuery("spend_data", params, { format: "ARROW_STREAM" });
41
41
  * // data: TypedArrowTable<{ group_key: string; cost_usd: number; ... }> | null
42
42
  * ```
43
43
  */
44
44
  function useAnalyticsQuery(queryKey, parameters, options = {}) {
45
- const format = options?.format ?? "JSON";
45
+ const format = options?.format ?? "JSON_ARRAY";
46
46
  const maxParametersSize = options?.maxParametersSize ?? 100 * 1024;
47
47
  const autoStart = options?.autoStart ?? true;
48
48
  const devMode = getDevMode();
@@ -1 +1 @@
1
- {"version":3,"file":"use-analytics-query.js","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { ArrowClient, connectSSE } from \"@/js\";\nimport type {\n AnalyticsFormat,\n InferParams,\n InferResultByFormat,\n QueryKey,\n UseAnalyticsQueryOptions,\n UseAnalyticsQueryResult,\n} from \"./types\";\nimport { useQueryHMR } from \"./use-query-hmr\";\n\nfunction getDevMode() {\n const url = new URL(window.location.href);\n const searchParams = url.searchParams;\n const dev = searchParams.get(\"dev\");\n\n return dev ? `?dev=${dev}` : \"\";\n}\n\nfunction getArrowStreamUrl(id: string) {\n return `/api/analytics/arrow-result/${id}`;\n}\n\n/**\n * Subscribe to an analytics query over SSE and returns its latest result.\n * Integration hook between client and analytics plugin.\n *\n * The return type is automatically inferred based on the format:\n * - `format: \"JSON\"` (default): Returns typed array from QueryRegistry\n * - `format: \"ARROW\"`: Returns TypedArrowTable with row type preserved\n *\n * Note: User context execution is determined by query file naming:\n * - `queryKey.obo.sql`: Executes as user (OBO = on-behalf-of / user delegation)\n * - `queryKey.sql`: Executes as service principal\n *\n * @param queryKey - Analytics query identifier\n * @param parameters - Query parameters (type-safe based on QueryRegistry)\n * @param options - Analytics query settings including format\n * @returns Query result state with format-appropriate data type\n *\n * @example JSON format (default)\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params);\n * // data: Array<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n *\n * @example Arrow format\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params, { format: \"ARROW\" });\n * // data: TypedArrowTable<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n */\nexport function useAnalyticsQuery<\n T = unknown,\n K extends QueryKey = QueryKey,\n F extends AnalyticsFormat = \"JSON\",\n>(\n queryKey: K,\n parameters?: InferParams<K> | null,\n options: UseAnalyticsQueryOptions<F> = {} as UseAnalyticsQueryOptions<F>,\n): UseAnalyticsQueryResult<InferResultByFormat<T, K, F>> {\n const format = options?.format ?? \"JSON\";\n const maxParametersSize = options?.maxParametersSize ?? 100 * 1024;\n const autoStart = options?.autoStart ?? true;\n\n const devMode = getDevMode();\n const urlSuffix = `/api/analytics/query/${encodeURIComponent(queryKey)}${devMode}`;\n\n type ResultType = InferResultByFormat<T, K, F>;\n const [data, setData] = useState<ResultType | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n if (!queryKey || queryKey.trim().length === 0) {\n throw new Error(\n \"useAnalyticsQuery: 'queryKey' must be a non-empty string.\",\n );\n }\n\n const payload = useMemo(() => {\n try {\n const serialized = JSON.stringify({ parameters, format });\n const sizeInBytes = new Blob([serialized]).size;\n if (sizeInBytes > maxParametersSize) {\n throw new Error(\n \"useAnalyticsQuery: Parameters size exceeds the maximum allowed size\",\n );\n }\n\n return serialized;\n } catch (error) {\n console.error(\"useAnalyticsQuery: Failed to serialize parameters\", error);\n return null;\n }\n }, [parameters, format, maxParametersSize]);\n\n const start = useCallback(() => {\n if (payload === null) {\n setError(\"Failed to serialize query parameters\");\n return;\n }\n\n // Abort previous request if exists\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n\n setLoading(true);\n setError(null);\n setData(null);\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n connectSSE({\n url: urlSuffix,\n payload: payload,\n signal: abortController.signal,\n onMessage: async (message) => {\n try {\n const parsed = JSON.parse(message.data);\n\n // success - JSON format\n if (parsed.type === \"result\") {\n setLoading(false);\n setData(parsed.data as ResultType);\n return;\n }\n\n // success - Arrow format\n if (parsed.type === \"arrow\") {\n try {\n const arrowData = await ArrowClient.fetchArrow(\n getArrowStreamUrl(parsed.statement_id),\n );\n const table = await ArrowClient.processArrowBuffer(arrowData);\n setLoading(false);\n // Table is cast to TypedArrowTable with row type from QueryRegistry\n setData(table as ResultType);\n return;\n } catch (error) {\n console.error(\n \"[useAnalyticsQuery] Failed to fetch Arrow data\",\n error,\n );\n setLoading(false);\n setError(\"Unable to load data, please try again\");\n return;\n }\n }\n\n // error\n if (parsed.type === \"error\" || parsed.error || parsed.code) {\n const errorMsg =\n parsed.error || parsed.message || \"Unable to execute query\";\n\n setLoading(false);\n setError(errorMsg);\n\n if (parsed.code) {\n console.error(\n `[useAnalyticsQuery] Code: ${parsed.code}, Message: ${errorMsg}`,\n );\n }\n return;\n }\n } catch (error) {\n console.warn(\"[useAnalyticsQuery] Malformed message received\", error);\n }\n },\n onError: (error) => {\n if (abortController.signal.aborted) return;\n setLoading(false);\n\n let userMessage = \"Unable to load data, please try again\";\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n userMessage = \"Request timed out, please try again\";\n } else if (error.message.includes(\"Failed to fetch\")) {\n userMessage = \"Network error. Please check your connection.\";\n }\n\n console.error(\"[useAnalyticsQuery] Error\", {\n queryKey,\n error: error.message,\n stack: error.stack,\n });\n }\n setError(userMessage);\n },\n });\n }, [queryKey, payload, urlSuffix]);\n\n useEffect(() => {\n if (autoStart) {\n start();\n }\n\n return () => {\n abortControllerRef.current?.abort();\n };\n }, [start, autoStart]);\n\n // Enable HMR for query updates in dev mode\n useQueryHMR(queryKey, start);\n\n return { data, loading, error };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-analytics-query.js","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { ArrowClient, connectSSE } from \"@/js\";\nimport type {\n AnalyticsFormat,\n InferParams,\n InferResultByFormat,\n QueryKey,\n UseAnalyticsQueryOptions,\n UseAnalyticsQueryResult,\n} from \"./types\";\nimport { useQueryHMR } from \"./use-query-hmr\";\n\nfunction getDevMode() {\n const url = new URL(window.location.href);\n const searchParams = url.searchParams;\n const dev = searchParams.get(\"dev\");\n\n return dev ? `?dev=${dev}` : \"\";\n}\n\nfunction getArrowStreamUrl(id: string) {\n return `/api/analytics/arrow-result/${id}`;\n}\n\n/**\n * Subscribe to an analytics query over SSE and returns its latest result.\n * Integration hook between client and analytics plugin.\n *\n * The return type is automatically inferred based on the format:\n * - `format: \"JSON_ARRAY\"` (default): Returns typed array from QueryRegistry\n * - `format: \"ARROW_STREAM\"`: Returns TypedArrowTable with row type preserved\n *\n * Note: User context execution is determined by query file naming:\n * - `queryKey.obo.sql`: Executes as user (OBO = on-behalf-of / user delegation)\n * - `queryKey.sql`: Executes as service principal\n *\n * @param queryKey - Analytics query identifier\n * @param parameters - Query parameters (type-safe based on QueryRegistry)\n * @param options - Analytics query settings including format\n * @returns Query result state with format-appropriate data type\n *\n * @example JSON format (default)\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params);\n * // data: Array<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n *\n * @example Arrow format\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params, { format: \"ARROW_STREAM\" });\n * // data: TypedArrowTable<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n */\nexport function useAnalyticsQuery<\n T = unknown,\n K extends QueryKey = QueryKey,\n F extends AnalyticsFormat = \"JSON_ARRAY\",\n>(\n queryKey: K,\n parameters?: InferParams<K> | null,\n options: UseAnalyticsQueryOptions<F> = {} as UseAnalyticsQueryOptions<F>,\n): UseAnalyticsQueryResult<InferResultByFormat<T, K, F>> {\n const format = options?.format ?? \"JSON_ARRAY\";\n const maxParametersSize = options?.maxParametersSize ?? 100 * 1024;\n const autoStart = options?.autoStart ?? true;\n\n const devMode = getDevMode();\n const urlSuffix = `/api/analytics/query/${encodeURIComponent(queryKey)}${devMode}`;\n\n type ResultType = InferResultByFormat<T, K, F>;\n const [data, setData] = useState<ResultType | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n if (!queryKey || queryKey.trim().length === 0) {\n throw new Error(\n \"useAnalyticsQuery: 'queryKey' must be a non-empty string.\",\n );\n }\n\n const payload = useMemo(() => {\n try {\n const serialized = JSON.stringify({ parameters, format });\n const sizeInBytes = new Blob([serialized]).size;\n if (sizeInBytes > maxParametersSize) {\n throw new Error(\n \"useAnalyticsQuery: Parameters size exceeds the maximum allowed size\",\n );\n }\n\n return serialized;\n } catch (error) {\n console.error(\"useAnalyticsQuery: Failed to serialize parameters\", error);\n return null;\n }\n }, [parameters, format, maxParametersSize]);\n\n const start = useCallback(() => {\n if (payload === null) {\n setError(\"Failed to serialize query parameters\");\n return;\n }\n\n // Abort previous request if exists\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n\n setLoading(true);\n setError(null);\n setData(null);\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n connectSSE({\n url: urlSuffix,\n payload: payload,\n signal: abortController.signal,\n onMessage: async (message) => {\n try {\n const parsed = JSON.parse(message.data);\n\n // success - JSON format\n if (parsed.type === \"result\") {\n setLoading(false);\n setData(parsed.data as ResultType);\n return;\n }\n\n // success - Arrow format\n if (parsed.type === \"arrow\") {\n try {\n const arrowData = await ArrowClient.fetchArrow(\n getArrowStreamUrl(parsed.statement_id),\n );\n const table = await ArrowClient.processArrowBuffer(arrowData);\n setLoading(false);\n // Table is cast to TypedArrowTable with row type from QueryRegistry\n setData(table as ResultType);\n return;\n } catch (error) {\n console.error(\n \"[useAnalyticsQuery] Failed to fetch Arrow data\",\n error,\n );\n setLoading(false);\n setError(\"Unable to load data, please try again\");\n return;\n }\n }\n\n // error\n if (parsed.type === \"error\" || parsed.error || parsed.code) {\n const errorMsg =\n parsed.error || parsed.message || \"Unable to execute query\";\n\n setLoading(false);\n setError(errorMsg);\n\n if (parsed.code) {\n console.error(\n `[useAnalyticsQuery] Code: ${parsed.code}, Message: ${errorMsg}`,\n );\n }\n return;\n }\n } catch (error) {\n console.warn(\"[useAnalyticsQuery] Malformed message received\", error);\n }\n },\n onError: (error) => {\n if (abortController.signal.aborted) return;\n setLoading(false);\n\n let userMessage = \"Unable to load data, please try again\";\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n userMessage = \"Request timed out, please try again\";\n } else if (error.message.includes(\"Failed to fetch\")) {\n userMessage = \"Network error. Please check your connection.\";\n }\n\n console.error(\"[useAnalyticsQuery] Error\", {\n queryKey,\n error: error.message,\n stack: error.stack,\n });\n }\n setError(userMessage);\n },\n });\n }, [queryKey, payload, urlSuffix]);\n\n useEffect(() => {\n if (autoStart) {\n start();\n }\n\n return () => {\n abortControllerRef.current?.abort();\n };\n }, [start, autoStart]);\n\n // Enable HMR for query updates in dev mode\n useQueryHMR(queryKey, start);\n\n return { data, loading, error };\n}\n"],"mappings":"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"}