booths 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,93 +1,72 @@
1
1
  # Booths
2
2
 
3
- Booths is a modular and extensible framework for building and managing conversational AI agents. It provides a structured way to define the capabilities, context, and tools for different AI-powered conversational flows.
3
+ [![Check Build on Pull Request](https://github.com/phoneburner/booths/actions/workflows/build-check.yml/badge.svg)](https://github.com/phoneburner/booths/actions/workflows/build-check.yml)
4
+ [![Format Check](https://github.com/phoneburner/booths/actions/workflows/format-check.yml/badge.svg)](https://github.com/phoneburner/booths/actions/workflows/format-check.yml)
5
+ [![Publish to NPM on Release](https://github.com/phoneburner/booths/actions/workflows/release.yml/badge.svg)](https://github.com/phoneburner/booths/actions/workflows/release.yml)
6
+ [![Test Suite](https://github.com/phoneburner/booths/actions/workflows/test.yml/badge.svg)](https://github.com/phoneburner/booths/actions/workflows/test.yml)
4
7
 
5
- The system is designed around a central `CoreBooth` class that orchestrates interactions between users and a Large Language Model (LLM), leveraging a system of registries and plugins to manage the conversational state and capabilities.
8
+ *A modular, extensible framework for building and managing conversational AI agents in TypeScript.*
6
9
 
7
- ## Architecture Overview
10
+ > Booths provides a structured way to define agent capabilities, context, and tools, orchestrated by a `CoreBooth` that manages the interaction loop with your LLM and a rich plugin lifecycle.
8
11
 
9
- The Booths framework is built around a few key concepts that work together to create a powerful and flexible conversational AI system.
12
+ ---
10
13
 
11
- ```mermaid
12
- graph TD
13
- subgraph Application Layer
14
- A[Your Application]
15
- end
14
+ ## Table of Contents
16
15
 
17
- subgraph Booth Service Layer
18
- C(CoreBooth)
19
- end
16
+ * [Installation](#installation)
17
+ * [Quick Start Guide](#quick-start-guide)
18
+ * [Architecture Overview](#architecture-overview)
19
+ * [API Reference](#api-reference)
20
20
 
21
- subgraph Core Components
22
- D[Interaction Processor]
23
- E[LLM Adapter]
24
- F[Registries]
25
- G[Plugins]
26
- end
21
+ * [createCoreBooth](#createcorebooth)
27
22
 
28
- A -- "initializes and calls" --> C
29
- C -- "delegates to" --> D
30
- D -- "uses" --> E
31
- D -- "uses" --> F
32
- D -- "executes" --> G
33
- E -- "communicates with" --> H((LLM))
34
- F -- "manages" --> I{Booths}
35
- F -- "manages" --> J{Tools}
36
- F -- "manages" --> K{Plugins}
37
- G -- "hook into" --> D
23
+ * [Options](#options)
24
+ * [Examples](#examples)
25
+ * [InteractionProcessor](#interactionprocessor)
38
26
 
39
- style C fill:#f9f,stroke:#333,stroke-width:2px
40
- ```
27
+ * [Flow](#flow)
28
+ * [Important Constraints](#important-constraints)
29
+ * [Registries](#registries)
41
30
 
42
- 1. **Application Layer**: Your application integrates the Booths framework to handle conversational AI interactions.
43
- 2. **`CoreBooth`**: The framework foundation that provides global functionality, instructions, and infrastructure that applies to all booths. It manages the overall system configuration and coordinates the interaction flow.
44
- 3. **`InteractionProcessor`**: The engine that drives the conversation. It takes user input, runs it through the plugin lifecycle, sends it to the LLM (via the adapter), and processes the response.
45
- 4. **`LLMAdapter`**: A component that handles communication with the specific LLM provider (e.g., OpenAI). It translates requests and responses between the Booths system and the LLM's API.
46
- 5. **Registries**: These are responsible for managing the different components of the system:
47
- * `BoothRegistry`: Manages `BoothConfig` objects that define the behavior of different AI agents.
48
- * `ToolRegistry`: Manages the tools (functions) that booths can use.
49
- * `BoothPluginRegistry`: Manages the plugins that hook into the conversational lifecycle.
50
- 6. **Plugins**: These are modules that add functionality to the system by hooking into the `InteractionProcessor`'s lifecycle (e.g., managing conversation history, providing context to the LLM, executing tools).
31
+ * [BoothRegistry](#boothregistry)
32
+ * [ToolRegistry](#toolregistry)
33
+ * [BoothPluginRegistry](#boothpluginregistry)
34
+ * [Plugins](#plugins)
51
35
 
52
- ## Getting Started
36
+ * [Lifecycle Hooks](#lifecycle-hooks)
37
+ * [Best Practices](#best-practices)
38
+ * [Advanced Usage](#advanced-usage)
53
39
 
54
- The Booths framework is designed as a TypeScript library for building conversational AI systems. This repository contains the core framework implementation.
40
+ * [Customizing the end-of-turn marker](#customizing-the-end-of-turn-marker)
41
+ * [Per-tool interception & error recovery](#per-tool-interception--error-recovery)
42
+ * [Types](#types)
43
+ * [Session & State Management](#session--state-management)
44
+ * [Error Handling](#error-handling)
45
+ * [License](#license)
55
46
 
56
- ### Installation
47
+ ---
48
+
49
+ ## Installation
57
50
 
58
51
  ```bash
59
52
  npm install booths
60
53
  ```
61
54
 
62
- ### Prerequisites
63
-
64
- - Node.js and npm installed
65
- - An LLM provider API key (e.g., OpenAI)
55
+ **Prerequisites**
66
56
 
67
- ### Development
68
-
69
- To build the library:
70
-
71
- ```bash
72
- npm run build
73
- ```
57
+ * Node.js and npm
58
+ * An API key for your LLM provider (e.g., OpenAI)
74
59
 
75
- To check types:
76
-
77
- ```bash
78
- npm run typecheck
79
- ```
60
+ ---
80
61
 
81
62
  ## Quick Start Guide
82
63
 
83
- Here is a lightweight example of how to set up and use the Core Booth system manually.
84
-
85
- ### 1. Define a Booth
64
+ A minimal setup that defines a booth, a tool, an adapter, and starts a conversation.
86
65
 
87
- First, define a booth configuration. This object specifies the booth's identity, role, and the tools it can use.
66
+ ### 1) Define a Booth
88
67
 
89
- ```typescript
90
- // in my-booths.ts
68
+ ```ts
69
+ // my-booths.ts
91
70
  import type { BoothConfig } from 'booths';
92
71
 
93
72
  export const pirateBooth: BoothConfig = {
@@ -99,12 +78,10 @@ export const pirateBooth: BoothConfig = {
99
78
  };
100
79
  ```
101
80
 
102
- ### 2. Define a Tool
81
+ ### 2) Define a Tool
103
82
 
104
- Next, create a tool that the booth can use. A tool is a function that the LLM can decide to call.
105
-
106
- ```typescript
107
- // in my-tools.ts
83
+ ```ts
84
+ // my-tools.ts
108
85
  import type { ToolModule } from 'booths';
109
86
 
110
87
  export const tellPirateJokeTool: ToolModule = {
@@ -113,122 +90,259 @@ export const tellPirateJokeTool: ToolModule = {
113
90
  description: 'Tells a classic pirate joke.',
114
91
  parameters: { type: 'object', properties: {} },
115
92
  execute: async () => {
116
- return { joke: "Why are pirates called pirates? Because they arrrr!" };
93
+ return { joke: 'Why are pirates called pirates? Because they arrrr!' };
117
94
  },
118
95
  };
119
96
  ```
120
97
 
121
- ### 3. Implement the LLM Adapter
122
-
123
- The `CoreBooth` requires an `LLMAdapter` to communicate with your chosen language model. Here is a minimal example for OpenAI.
98
+ ### 3) Implement a simple LLM Adapter
124
99
 
125
- ```typescript
126
- // in OpenAIAdapter.ts
127
- import type { LLMAdapter, ResponseCreateParamsNonStreaming, Response } from 'booths';
100
+ ```ts
101
+ // OpenAIAdapter.ts
102
+ import type {
103
+ LLMAdapter,
104
+ ResponseCreateParamsNonStreaming,
105
+ Response,
106
+ ResponseInput,
107
+ } from 'booths';
128
108
  import OpenAI from 'openai';
129
109
 
130
110
  export class OpenAIAdapter implements LLMAdapter<Response> {
131
111
  private openai: OpenAI;
132
-
133
112
  constructor(apiKey: string) {
134
113
  this.openai = new OpenAI({ apiKey });
135
114
  }
136
-
137
115
  async invoke(params: ResponseCreateParamsNonStreaming): Promise<Response> {
138
116
  return this.openai.responses.create({ ...params, model: 'gpt-4o' });
139
117
  }
140
-
141
118
  async interpret(response: Response): Promise<Response> {
142
119
  return response;
143
120
  }
144
121
  }
145
122
  ```
146
123
 
147
- ### 4. Initialize the CoreBooth
124
+ > **Note**: All necessary types including `ResponseInput` and `ResponseInputItem` can be imported directly from `booths`, eliminating the need to import from the OpenAI library.
148
125
 
149
- Finally, use the `createCoreBooth` factory to instantiate the system.
126
+ ### 4) Initialize and talk to the booth
150
127
 
151
- ```typescript
152
- // in main.ts
128
+ ```ts
129
+ // main.ts
153
130
  import { createCoreBooth } from 'booths';
154
131
  import { pirateBooth } from './my-booths';
155
132
  import { tellPirateJokeTool } from './my-tools';
156
- import { OpenAIAdapter } from './openAIAdapter';
157
-
158
- // 1. Create the LLM adapter
159
- const llmAdapter = new OpenAIAdapter('your-openai-api-key');
133
+ import { OpenAIAdapter } from './OpenAIAdapter';
160
134
 
161
- // 2. Create the CoreBooth instance
162
- const coreBooth = createCoreBooth(llmAdapter, pirateBooth);
135
+ const llmAdapter = new OpenAIAdapter(process.env.OPENAI_API_KEY!);
136
+ const coreBooth = createCoreBooth(llmAdapter, pirateBooth /*, { endInteractionLoopMarker: '__custom_marker__' }*/);
163
137
 
164
- // Optional: Customize the end interaction loop marker
165
- // const coreBooth = createCoreBooth(llmAdapter, pirateBooth, {
166
- // endInteractionLoopMarker: '__custom_marker__'
167
- // });
168
-
169
- // 3. Register the tool (this step will be improved in future versions)
138
+ // Register tools (available to the current booth)
170
139
  coreBooth.toolRegistry.registerTools([tellPirateJokeTool]);
171
140
 
172
- // 4. Send a message and get a response
173
- async function haveConversation() {
174
- const userInput = 'Tell me a pirate joke.';
175
- const response = await coreBooth.callProcessor.send(userInput);
176
-
141
+ // Send a message and read the result
142
+ async function run() {
143
+ const response = await coreBooth.callProcessor.send('Tell me a pirate joke.');
177
144
  console.log(response.output_text);
178
- // Expected output: "Why are pirates called pirates? Because they arrrr!"
179
145
  }
146
+ run();
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Architecture Overview
180
152
 
181
- haveConversation();
153
+ ```mermaid
154
+ graph TD
155
+ subgraph Application Layer
156
+ A[Your Application]
157
+ end
158
+
159
+ subgraph Booth Service Layer
160
+ C(CoreBooth)
161
+ end
162
+
163
+ subgraph Core Components
164
+ D[Interaction Processor]
165
+ E[LLM Adapter]
166
+ F[Registries]
167
+ G[Plugins]
168
+ end
169
+
170
+ A -- "initializes and calls" --> C
171
+ C -- "delegates to" --> D
172
+ D -- "uses" --> E
173
+ D -- "uses" --> F
174
+ D -- "executes" --> G
175
+ E -- "communicates with" --> H((LLM))
176
+ F -- "manages" --> I{Booths}
177
+ F -- "manages" --> J{Tools}
178
+ F -- "manages" --> K{Plugins}
179
+ G -- "hook into" --> D
180
+ ```
181
+
182
+ ---
183
+
184
+ ## API Reference
185
+
186
+ ### createCoreBooth
187
+
188
+ Factory that wires an `LLMAdapter`, a `BoothConfig`, and internal registries/plugins into a working `CoreBooth` instance.
189
+
190
+ ```ts
191
+ function createCoreBooth(
192
+ adapter: LLMAdapter<any>,
193
+ booth: BoothConfig,
194
+ options?: { endInteractionLoopMarker?: string }
195
+ ): CoreBooth
196
+ ```
197
+
198
+ #### Options
199
+
200
+ | Name | Type | Default | Description |
201
+ | -------------------------- | -------- | -----------------------------: | ----------------------------------------------------- |
202
+ | `endInteractionLoopMarker` | `string` | `"__awaiting_user_response__"` | Marker used by plugins to determine when a turn ends. |
203
+
204
+ #### Examples
205
+
206
+ **Basic**
207
+
208
+ ```ts
209
+ const coreBooth = createCoreBooth(adapter, pirateBooth);
210
+ ```
211
+
212
+ **Custom end-of-turn marker**
213
+
214
+ ```ts
215
+ const coreBooth = createCoreBooth(adapter, pirateBooth, {
216
+ endInteractionLoopMarker: '__custom_marker__',
217
+ });
218
+ ```
219
+
220
+ ---
221
+
222
+ ### InteractionProcessor
223
+
224
+ Engine that manages the interaction loop with the LLM and the plugin lifecycle.
225
+
226
+ #### Flow
227
+
228
+ 1. Take user input
229
+ 2. Run `onBefore...` plugin hooks
230
+ 3. Send payload via `LLMAdapter`
231
+ 4. Receive response
232
+ 5. Run `onResponseReceived` hooks (e.g., execute tools)
233
+ 6. Repeat until `shouldEndInteractionLoop` returns `true`
234
+ 7. Run `onAfter...` hooks for cleanup
235
+
236
+ #### Important Constraints
237
+
238
+ * **Ordering matters**: tool execution and hook order follow the loop above.
239
+ * **End-of-turn detection** relies on the configured `endInteractionLoopMarker`.
240
+
241
+ ---
242
+
243
+ ### Registries
244
+
245
+ Booths uses three registries to manage the system’s moving parts.
246
+
247
+ #### BoothRegistry
248
+
249
+ * Manages `BoothConfig` objects (each is a specialized agent: role, description, tools, examples).
250
+ * Tracks the current context booth.
251
+
252
+ #### ToolRegistry
253
+
254
+ * Stores tools available to the LLM (as `ToolModule`).
255
+ * Provides registration helpers like `registerTools([...])`.
256
+
257
+ #### BoothPluginRegistry
258
+
259
+ * Manages plugins that hook into the interaction lifecycle.
260
+ * Enables modular capabilities like conversation history, context provisioning, tool provisioning/execution, and turn-finalization.
261
+
262
+ ---
263
+
264
+ ### Plugins
265
+
266
+ Plugins implement `BoothPlugin` and can influence request/response flow and tool execution.
267
+
268
+ #### Lifecycle Hooks
269
+
270
+ * `onBeforeInteractionLoopStart`
271
+ * `onBeforeMessageSend`
272
+ * `onResponseReceived`
273
+ * `onBeforeToolCall`
274
+ * `onAfterToolCall`
275
+ * `onToolCallError`
276
+ * `shouldEndInteractionLoop`
277
+ * `onAfterInteractionLoopEnd`
278
+
279
+ **Built-in plugin capabilities (typical set)**
280
+
281
+ * `ConversationHistoryPlugin` – maintains conversation history
282
+ * `ContextProviderPlugin` – supplies booth context to the LLM
283
+ * `ToolProviderPlugin` – exposes available tools
284
+ * `ToolExecutorPlugin` – executes tool calls with per-call hooks
285
+ * `FinishTurnPlugin` – decides when a turn is complete
286
+
287
+ ---
288
+
289
+ ## Best Practices
290
+
291
+ * Keep tools **pure** and **idempotent** when possible; return structured results.
292
+ * Favor **small, composable plugins**; use `onBeforeToolCall`/`onAfterToolCall`/`onToolCallError` to isolate auth, caching, and audit.
293
+ * Clearly separate **global tools** vs **booth-specific tools** and document access rules.
294
+ * Centralize **end-of-turn logic** to avoid inconsistent session behavior.
295
+
296
+ ---
297
+
298
+ ## Advanced Usage
299
+
300
+ ### Customizing the end-of-turn marker
301
+
302
+ ```ts
303
+ createCoreBooth(adapter, booth, { endInteractionLoopMarker: '__custom__' });
182
304
  ```
183
- ## How It Works
184
305
 
185
- The Core Booth system is comprised of several key components that work together to process user input and generate contextual responses.
306
+ Use this to coordinate UI or multi-agent systems that rely on explicit “awaiting user signals.
307
+
308
+ ### Per-tool interception & error recovery
309
+
310
+ ```ts
311
+ class AuditPlugin implements BoothPlugin {
312
+ async onBeforeToolCall(ctx) { /* validate or redact */ }
313
+ async onAfterToolCall(ctx) { /* persist results */ }
314
+ async onToolCallError(ctx, err) { /* fallback or retry */ }
315
+ }
316
+ ```
186
317
 
187
- ### 1. Registries
318
+ These hooks enable authentication, caching, and graceful degradation at the **individual tool** level.
188
319
 
189
- - **`BoothRegistry`**: Manages the collection of `BoothConfig` objects. Each booth represents a specialized agent with a specific role, description, and set of tools. It also keeps track of the "current context booth" to ensure the conversation stays on topic.
190
- - **`ToolRegistry`**: Manages the tools that can be made available to the LLM. Tools are functions that the AI can decide to call to perform actions or retrieve information.
191
- - **`BoothPluginRegistry`**: Manages plugins that hook into the interaction lifecycle. This allows for modular and reusable functionality to be added to the system.
320
+ ---
192
321
 
193
- ### 2. Plugins
322
+ ## Types
194
323
 
195
- Plugins are classes that implement the `BoothPlugin` interface. They can execute logic at different stages of the conversation:
324
+ Core concepts you’ll interact with most:
196
325
 
197
- - `onBeforeInteractionLoopStart`: Before the main loop begins.
198
- - `onBeforeMessageSend`: Before a message is sent to the LLM.
199
- - `onResponseReceived`: After a response is received from the LLM.
200
- - `onBeforeToolCall`: Before each individual tool call is executed _(allows modification of tool parameters, validation, and logging)_.
201
- - `onAfterToolCall`: After each individual tool call is successfully executed _(allows result processing, caching, and transformation)_.
202
- - `onToolCallError`: When a tool call encounters an error _(allows custom error handling and recovery)_.
203
- - `shouldEndInteractionLoop`: To determine if the conversation turn is over.
204
- - `onAfterInteractionLoopEnd`: After the main loop has finished.
326
+ * `BoothConfig`: identity, role, description, `tools: string[]`, sample `examples`
327
+ * `ToolModule`: `{ type: 'function' | ..., name, description, parameters, execute }`
328
+ * `LLMAdapter<TResponse>`: `{ invoke(params): Promise<TResponse>; interpret(response): Promise<TResponse> }`
205
329
 
206
- The system includes several core plugins by default:
330
+ ---
207
331
 
208
- - `ConversationHistoryPlugin`: Maintains the history of the conversation.
209
- - `ContextProviderPlugin`: Provides the LLM with the context of the current booth.
210
- - `ToolProviderPlugin`: Provides the LLM with the available tools for the current booth.
211
- - `ToolExecutorPlugin`: Executes tool calls requested by the LLM with granular hook support for individual tool call interception.
212
- - `FinishTurnPlugin`: Determines when the LLM's turn is finished and it's waiting for user input. The marker used to detect conversation end can be customized via the `endInteractionLoopMarker` option (defaults to `__awaiting_user_response__`).
332
+ ## Session & State Management
213
333
 
214
- #### Enhanced Tool Call Management
334
+ * Conversation continuity (history, context booth) is typically maintained by plugins.
335
+ * Tool calls may emit intermediate results/messages; design UI to handle “in-progress” states.
215
336
 
216
- The plugin system now provides granular control over individual tool executions through three new hooks:
337
+ ---
217
338
 
218
- - **`onBeforeToolCall`**: Intercept and modify tool calls before execution (parameter validation, authorization, logging)
219
- - **`onAfterToolCall`**: Process and transform tool results after successful execution (caching, metadata addition, data transformation)
220
- - **`onToolCallError`**: Handle tool execution errors with custom recovery logic (fallback responses, error logging, graceful degradation)
339
+ ## Error Handling
221
340
 
222
- This enables sophisticated tool management patterns like authentication, caching, audit logging, and error recovery at the individual tool level.
341
+ * Surface adapter/transport errors from `invoke`.
342
+ * Prefer plugin-level `onToolCallError` for tool failures; use retries or fallback responses.
223
343
 
224
- ### 3. Interaction Processor
344
+ ---
225
345
 
226
- The `InteractionProcessor` is the engine of the system. It manages the interaction loop with the LLM:
346
+ ## License
227
347
 
228
- 1. It takes user input.
229
- 2. Runs the `onBefore...` plugin hooks.
230
- 3. Sends the payload to the LLM.
231
- 4. Receives the response.
232
- 5. Runs the `onResponseReceived` plugin hooks to process the response (e.g., execute tools).
233
- 6. Repeats this loop until a plugin's `shouldEndInteractionLoop` returns `true`.
234
- 7. Runs the `onAfter...` plugin hooks for cleanup.
348
+ Distributed under the project’s LICENSE file.
package/dist/index.cjs ADDED
@@ -0,0 +1,60 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class f{plugins=[];registerPlugin(t){if(this.plugins.some(o=>o.id===t.id))throw new Error(`Plugin with ID ${t.id} is already registered.`);this.plugins.push(t)}getPlugins(){return this.plugins}registerPlugins(t){for(const o of t)this.registerPlugin(o)}getPluginById(t){return this.plugins.find(o=>o.id===t)}getPluginsByIds(t){const o=[];for(const e of t){const r=this.getPluginById(e);if(!r)throw new Error(`Plugin with ID ${e} is not registered.`);o.push(r)}return o}unregisterPlugin(t){const o=this.plugins.findIndex(e=>e.id===t);if(o===-1)throw new Error(`Plugin with ID ${t} does not exist.`);this.plugins.splice(o,1)}async runBeforeInteractionLoopStart(t,o,e){let r=o;for(const s of this.plugins)s.onBeforeInteractionLoopStart&&(r=await s.onBeforeInteractionLoopStart(t,r,e));return r}async runBeforeMessageSend(t,o){let e=o;for(const r of this.plugins)r.onBeforeMessageSend&&(e=await r.onBeforeMessageSend(t,e));return e}async runResponseReceived(t,o,e){let r=o;for(const s of this.plugins)s.onResponseReceived&&(r=await s.onResponseReceived(t,r,e));return r}async runShouldEndInteractionLoop(t,o,e){for(const r of this.plugins)if(await r.shouldEndInteractionLoop(t,o,e))return console.log(`BoothPluginRegistry: Plugin ${r.id} indicated loop should end.`),!0;return!1}async runAfterInteractionLoopEnd(t,o){let e=o;for(const r of this.plugins)r.onAfterInteractionLoopEnd&&(e=await r.onAfterInteractionLoopEnd(t,o));return e}async runBeforeToolCall(t,o,e){let r=o;for(const s of this.plugins)s.onBeforeToolCall&&(r=await s.onBeforeToolCall(t,r,e));return r}async runAfterToolCall(t,o,e,r){let s=e;for(const i of this.plugins)i.onAfterToolCall&&(s=await i.onAfterToolCall(t,o,s,r));return s}async runToolCallError(t,o,e,r){let s=`Error: ${e.message}`;for(const i of this.plugins)i.onToolCallError&&(s=await i.onToolCallError(t,o,e,r));return s}}const u={id:"orchestrator",role:`
2
+ This booth serves as the orchestration layer that analyzes user intent and routes
3
+ conversations to the most appropriate specialized booth configuration.
4
+ `,description:`
5
+ You are the orchestration layer responsible for determining which booth configuration
6
+ should be active based on user needs. Focus exclusively on routing - do not answer
7
+ questions or provide information directly to users.
8
+
9
+ [ROUTING STRATEGY]
10
+ - Analyze user request and route to the most appropriate specialized booth immediately
11
+ - This booth is only active for initial routing or when explicitly routed back to
12
+ - Once routed, the target booth handles the conversation until completion or re-routing
13
+
14
+ [ROUTING TARGETS]
15
+ - Ambiguous requests → Ask for clarification, then route appropriately
16
+
17
+ [CORE PRINCIPLES]
18
+ - Maintain illusion of single, continuous assistant
19
+ - Never reference booths, tools, or system mechanics to users
20
+ - Silent routing is preferred when intent is clear
21
+ - Only speak to users when clarification is absolutely necessary
22
+
23
+ [ROUTING BEHAVIOR]
24
+ - Clear intent: Route silently using route_to_booth() - do NOT respond to user
25
+ - Ambiguous intent: Ask user for clarification, then route once clarified
26
+ - Never respond to user AND route - it's either respond OR route, not both
27
+
28
+ [BEHAVIOR EXAMPLES]
29
+ - User: "How do I test my number?" → route_to_booth({ targetBooth: 'page-router-booth' })
30
+ - User: "I need help" → "What specifically would you like help with?" → then route based on response
31
+ `};class _{constructor(t,o){this.baseBooth=t,this.registerBooth(t),this.currentContextId=o||t.id}booths={};currentContextId;hasOrchestrator=!1;onMultiBoothModeEnabled;onMultiBoothModeDisabled;getCurrentContextId(){return this.currentContextId}setCurrentContextId(t){if(!this.booths[t])throw new Error(`Booth with ID ${t} is not registered.`);this.currentContextId=t}get currentContextBoothConfig(){const t=this.getBoothById(this.currentContextId);if(!t)throw new Error(`Booth with ID ${this.currentContextId} is not registered.`);return t}registerBooth(t){if(this.booths[t.id])return;this.booths[t.id]=t,Object.keys(this.booths).filter(e=>e!==u.id).length>1&&!this.hasOrchestrator&&this.enableMultiBoothMode()}get baseBoothConfig(){return this.baseBooth}get orchestratorBoothConfig(){const t=this.getBoothById("orchestrator");if(!t)throw new Error("Base booth does not have an orchestrator configuration.");return t}registerBooths(t){for(const o of t)this.registerBooth(o)}getBoothById(t){return this.booths[t]}getBoothsByIds(t){const o=[];for(const e of t){const r=this.getBoothById(e);if(!r)throw new Error(`Booth with ID ${e} is not registered.`);o.push(r)}return o}getAllBooths(){return this.booths}getSelectableBooths(){const t={};for(const[o,e]of Object.entries(this.booths))o!==this.baseBooth.id&&o!==u.id&&(t[o]=e);return t}toArray(){return Object.values(this.booths)}enableMultiBoothMode(){this.hasOrchestrator||(this.booths[u.id]=u,this.hasOrchestrator=!0,this.currentContextId=u.id,this.onMultiBoothModeEnabled?.())}disableMultiBoothMode(){this.hasOrchestrator&&(delete this.booths[u.id],this.hasOrchestrator=!1,this.currentContextId=this.baseBooth.id,this.onMultiBoothModeDisabled?.())}setMultiBoothModeCallbacks(t,o){this.onMultiBoothModeEnabled=t,this.onMultiBoothModeDisabled=o}get isMultiBoothMode(){return Object.keys(this.booths).filter(o=>o!==u.id).length>1}unregisterBooth(t){if(!this.booths[t])throw new Error(`Booth with ID ${t} does not exist.`);if(t===u.id)throw new Error("Cannot unregister orchestrator booth directly. It will be automatically managed based on booth count.");delete this.booths[t],Object.keys(this.booths).filter(e=>e!==u.id).length<=1&&this.hasOrchestrator&&this.disableMultiBoothMode()}}class v{constructor(t,o,e,r){this.boothRegistry=t,this.boothPlugins=o,this.toolRegistry=e,this.llmAdapter=r}loopLimit=10;createErrorResponse(t,o){const e=t instanceof Error?t.message:"Unknown error occurred while calling LLM",r=t instanceof Error&&"code"in t&&typeof t.code=="string"?t.code:"server_error",s={code:"server_error",message:e};if(r&&(s.code=r),o.model===void 0)throw new Error("Model must be specified in response parameters for error handling.");return{id:`error_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,created_at:Math.floor(Date.now()/1e3),output_text:"An error occurred while communicating with the language model.",error:s,incomplete_details:null,instructions:null,metadata:null,model:o.model,object:"response",output:[{id:`msg_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,content:[{type:"output_text",text:`Error: ${e}. Please try again or contact support if the issue persists.`,annotations:[]}],role:"assistant",status:"completed",type:"message"}],parallel_tool_calls:o.parallel_tool_calls||!1,temperature:o.temperature||null,tool_choice:o.tool_choice||"auto",tools:o.tools||[],top_p:o.top_p||null,status:"failed"}}async callLLM(t,o){try{const e=await this.llmAdapter.invoke(t,o);return await this.llmAdapter.interpret(e)}catch(e){return console.log("Error calling LLM:",e),this.createErrorResponse(e,t)}}async runInteractionLoop(t){let o=0,e=t,r,s=!0;for(;s&&o<this.loopLimit;){o++;const i={toolRegistry:this.toolRegistry,boothRegistry:this.boothRegistry,pluginRegistry:this.boothPlugins,llmAdapter:this.llmAdapter};e=await this.boothPlugins.runBeforeMessageSend(i,e),r=await this.callLLM(e,i),e=await this.boothPlugins.runResponseReceived(i,e,r),await this.boothPlugins.runShouldEndInteractionLoop(i,e,r)&&(console.log("Ending interaction loop because 'shouldEndInteractionLoop' returned true."),s=!1)}if(!r)throw new Error("No response received from LLM");return r}async send(t){let o;typeof t=="string"?o=[{role:"user",content:t}]:o=t;let e={input:o,tools:[]};const r=this.boothPlugins;e=await this.boothPlugins.runBeforeInteractionLoopStart({toolRegistry:this.toolRegistry,boothRegistry:this.boothRegistry,pluginRegistry:r,llmAdapter:this.llmAdapter},e,t);let s=await this.runInteractionLoop(e);return s=await this.boothPlugins.runAfterInteractionLoopEnd({toolRegistry:this.toolRegistry,boothRegistry:this.boothRegistry,pluginRegistry:r,llmAdapter:this.llmAdapter},s),s}}const L={id:"summarizer",role:'You are a highly skilled summarization AI. Your task is to read a conversation history and provide a concise, neutral, and objective summary. The summary should capture the key points, decisions made, and any unresolved questions. It must be written from a third-person perspective and should be clear enough for another AI assistant to understand the full context and continue the conversation seamlessly without needing the original transcript. Do not add any conversational fluff or introductory phrases like "Here is the summary:".',description:"A specialized booth for summarizing conversation histories."},w="route_to_booth";function y(n){const t=n.getSelectableBooths(),o=Object.values(t).map(r=>`- ${r.id}: ${r.role}
32
+ Examples:
33
+ ${(r.examples||[]).map(s=>` - "${s}"`).join(`
34
+ `)}`).join(`
35
+ `),e=Object.keys(t);return{type:"function",name:w,description:`
36
+ Routes the conversation to a specialized booth based on the user's needs. Each booth has a
37
+ specific role and set of capabilities.
38
+ `,parameters:{type:"object",properties:{targetBooth:{type:"string",description:`
39
+ The ID of the booth to route the conversation to. Must be one of the following available
40
+ booths:
41
+ ${o}
42
+ `,enum:e}},required:["targetBooth"],additionalProperties:!1},strict:!0,execute:async function({targetBooth:r}){try{return n.setCurrentContextId(r),{content:`Routed to booth ${r}`}}catch(s){return console.log("[routeToBoothTool] Error routing to booth:",s),{content:`Error: Unable to route to booth ${r}.`}}}}}class E{sessionHistory=[];plugin_id="conversation-history";plugin_name="Conversation History Plugin";plugin_description="A plugin to manage conversation history in booths.";responseContainsBoothChange(t){return t.output?t.output.some(o=>o.type==="function_call"?o.name===w:o.type==="message"&&"tool_calls"in o&&Array.isArray(o.tool_calls)?o.tool_calls.some(e=>e.type==="function"&&e.function.name===w):!1):!1}constructor(t=[]){this.sessionHistory=t}get history(){return this.sessionHistory}get id(){return this.plugin_id}get name(){return this.plugin_name}get description(){return this.plugin_description}async onBeforeInteractionLoopStart(t,o){const{input:e}=o,r=typeof e=="string"?[{role:"user",content:e}]:e||[];return this.sessionHistory.push(...r),{...o,input:this.sessionHistory}}async onResponseReceived(t,o,e){let s=[...o.input,...e?.output??[]];if(this.responseContainsBoothChange(e)){const l=`Please compact the following LLM conversation history. Take key points and make it useful for
43
+ an LLM that may need to look back for references. Conclude the summary with any next steps or
44
+ actions needed to complete the conversation.
45
+
46
+ The conversation History:
47
+ ${JSON.stringify(this.sessionHistory)}`.trim(),c=(await P(t.llmAdapter,L).callProcessor.send(l)).output,g=s.filter(a=>"role"in a&&a.role==="user").pop();console.log("LastUserMessage: ",g),console.log("Summary: ",c),console.log("Summary: ",c.join(""));let b="";const p=c.find(a=>a.type==="message");console.log("summaryMessages",p),p&&"type"in p&&p.type=="message"&&p.content.forEach(a=>{a.type==="output_text"&&(b+=a.text)}),console.log("summaryText",b);const R={role:"developer",content:`A conversation summary up to this point: ${b}`},T=s.filter(a=>!("role"in a&&a.role==="user"||"type"in a&&a.type==="message"));this.sessionHistory=g?[...T,R,g]:[...T,R],s=this.sessionHistory}else this.sessionHistory=s;return{...o,input:s}}async shouldEndInteractionLoop(){return!1}}class I{plugin_id="context-provider";plugin_name="Context Provider Plugin";plugin_description="A plugin to provide context to booths.";get id(){return this.plugin_id}get name(){return this.plugin_name}get description(){return this.plugin_description}async onBeforeMessageSend(t,o){const e=t.boothRegistry;let s=e.baseBoothConfig.description;if(e.isMultiBoothMode){const i=e.orchestratorBoothConfig,l=e.currentContextBoothConfig;s+=`
48
+
49
+ ${i.description}`,l.id!==i.id&&(s+=`
50
+
51
+ ${l.description}`)}return{...o,instructions:s}}async shouldEndInteractionLoop(){return!1}}class B{tools;constructor(){this.tools=new Map}registerTools(t){t.forEach(o=>this.registerTool(o))}registerTool(t){if(this.tools.has(t.name)){console.warn(`Tool with ID '${t.name}' is already registered. Duplicate registration ignored.`);return}this.tools.set(t.name,t)}getTool(t){const o=this.tools.get(t);if(!o)throw new Error(`Tool with name ${t} is not registered.`);return o}getToolsByNames(t){const o=[];for(const e of t){const r=this.getTool(e);o.push(r)}return o}getGlobalTools(){return Array.from(this.tools.values()).filter(t=>t.global)}getServerTools(){return Array.from(this.tools.values()).filter(t=>t.execute!==void 0)}getLocalTools(){return Array.from(this.tools.values()).filter(t=>t.execute===void 0)}isLocalTool(t){const o=this.tools.get(t);return o?o.execute===void 0:!1}unregisterTool(t){if(!this.tools.has(t))throw new Error(`Tool with ID ${t} is not registered.`);this.tools.delete(t)}}function $(n){switch(n.type){case"function":return`function:${n.name}`;case"mcp":return`mcp:${n.server_label}`;case"file_search":return`file_search:${n.vector_store_ids.join(",")}`;case"web_search_preview":case"web_search_preview_2025_03_11":return`web_search:${n.type}`;case"computer_use_preview":return`computer:${n.environment}:${n.display_width}x${n.display_height}`;case"code_interpreter":return`code_interpreter:${typeof n.container=="string"?n.container:"auto"}`;case"image_generation":return`image_generation:${n.model||"gpt-image-1"}`;case"local_shell":return"local_shell";default:return`${n.type}:${JSON.stringify(n)}`}}function A(n){const t=new Set,o=[];for(const e of n){const r=$(e);t.has(r)||(t.add(r),o.push(e))}return o}class C{description="A plugin to aggregate and provide tools from base and context booths.";id="tool-provider";name="Tool Provider Plugin";async onBeforeMessageSend(t,o){const e=t.boothRegistry.baseBoothConfig,r=t.boothRegistry.currentContextBoothConfig,l=[...e.tools||[],...r?.tools||[]].filter((h,c,g)=>g.indexOf(h)===c).map(h=>t.toolRegistry.getTool(h));if(e.mcp&&l.push(...e.mcp),r?.mcp&&l.push(...r.mcp),t.boothRegistry.isMultiBoothMode){const h=y(t.boothRegistry);l.push(h)}l.push(...t.toolRegistry.getGlobalTools());const d=A(l);return{...o,tools:d}}async shouldEndInteractionLoop(){return!1}}class m{id="tool-executor";name="Tool Executor";description="Checks for tool calls in the response, executes them, and adds the results to the message history.";async executeToolCall(t,o,e){try{const r=await t.pluginRegistry.runBeforeToolCall(t,o,e),s=t.toolRegistry.getTool(r.name);if(!s)return{type:"function_call_output",call_id:r.call_id,output:`Error: Tool '${r.name}' not found.`};if(!s.execute)return{type:"function_call_output",call_id:r.call_id,output:`Error: Tool '${r.name}' does not have an 'execute' method.`};const i=await s.execute(JSON.parse(r.arguments)),l=await t.pluginRegistry.runAfterToolCall(t,r,i,e);return l||console.log(`Booths.ToolExecutorPlugin: Tool execution returned an empty results for ${s.name}. This may result in 400 responses from the LLM.`),{type:"function_call_output",call_id:r.call_id,output:JSON.stringify(l)}}catch(r){console.log(`Error executing tool ${o.name}:`,r);const s=await t.pluginRegistry.runToolCallError(t,o,r,e);return{type:"function_call_output",call_id:o.call_id,output:typeof s=="string"?s:JSON.stringify(s)}}}static extractFunctionCalls(t){return t.filter(o=>o.type==="function_call")}async onResponseReceived(t,o,e){const r=e?.output??[],s=m.extractFunctionCalls(r);if(!s.length)return o;const i=[];for(let l=0;l<s.length;l++){const d=s[l];if(t.toolRegistry.isLocalTool(d.name))continue;const h={responseParams:o,response:e,toolCallIndex:l,totalToolCalls:s.length},c=await this.executeToolCall(t,d,h);i.push(c)}return{...o,input:[...o.input,...i]}}async shouldEndInteractionLoop(){return!1}}class M{constructor(t="__awaiting_user_response__"){this.marker=t}description="A plugin to ensure the interaction loop can be finished.";id="finish-turn-plugin";name="Finish Turn Plugin";async onBeforeMessageSend(t,o){let e=o.instructions||"";return e=`
52
+
53
+
54
+
55
+ [MUST]
56
+ - Always add the marker "${this.marker}" at the end of your output when you have finished your part
57
+ - This marker indicates that the interaction loop should end.
58
+
59
+ ${e}
60
+ `,{...o,instructions:e}}async shouldEndInteractionLoop(t,o,e){const s=JSON.stringify(e.output).includes(this.marker),i=e.status==="failed"||e.error!==null;return s||i}async onAfterInteractionLoopEnd(t,o){const e=JSON.stringify(o);if(e.includes(this.marker)){const r=e.replace(new RegExp(this.marker,"g"),"");return JSON.parse(r)}return o}}function P(n,t){const o=new _(t),e=new B,r=new f;return new x({llmAdapter:n,booths:o,tools:e,boothPlugins:r})}class x{boothPluginRegistry;boothRegistry;callProcessor;systemPluginsRegistry;toolRegistry;constructor(t){if(this.boothPluginRegistry=t?.boothPlugins??new f,this.boothRegistry=t.booths,this.toolRegistry=t?.tools??new B,this.boothRegistry.setMultiBoothModeCallbacks(()=>{const o=y(this.boothRegistry);this.toolRegistry.registerTools([o])},()=>{}),this.boothRegistry.isMultiBoothMode){const o=y(this.boothRegistry);this.toolRegistry.registerTools([o])}this.systemPluginsRegistry=new f,this.systemPluginsRegistry.registerPlugins([new E(t.sessionHistory),new I,new C,new m,new M(t.endInteractionLoopMarker)]),this.systemPluginsRegistry.registerPlugins(this.boothPluginRegistry.getPlugins()),this.callProcessor=new v(this.boothRegistry,this.systemPluginsRegistry,this.toolRegistry,t.llmAdapter)}}exports.BoothPluginRegistry=f;exports.BoothRegistry=_;exports.ContextProviderPlugin=I;exports.ConversationHistoryPlugin=E;exports.CoreBooth=x;exports.FinishTurnPlugin=M;exports.InteractionProcessor=v;exports.ToolExecutorPlugin=m;exports.ToolProviderPlugin=C;exports.ToolRegistry=B;exports.createCoreBooth=P;exports.createRouteToBoothTool=y;
package/dist/index.d.ts CHANGED
@@ -408,6 +408,13 @@ export declare class BoothRegistry {
408
408
  * @returns Record of all booth configurations indexed by their IDs
409
409
  */
410
410
  getAllBooths(): Record<string, BoothConfig>;
411
+ /**
412
+ * Returns only booths that should be available for routing (excludes core and orchestrator booths).
413
+ * This prevents double context issues by ensuring system booths are not selectable.
414
+ *
415
+ * @returns Record of selectable booth configurations indexed by their IDs
416
+ */
417
+ getSelectableBooths(): Record<string, BoothConfig>;
411
418
  toArray(): BoothConfig[];
412
419
  /**
413
420
  * Enables multi-booth mode by registering the orchestrator and setting it as current context.
@@ -848,6 +855,10 @@ export declare type RepositoryUtilities = {
848
855
 
849
856
  export { ResponseCreateParamsNonStreaming }
850
857
 
858
+ export { ResponseInput }
859
+
860
+ export { ResponseInputItem }
861
+
851
862
  /**
852
863
  * Represents the result of processing a single tool call.
853
864
  */
@@ -899,6 +910,12 @@ export declare class ToolExecutorPlugin implements BoothPlugin {
899
910
  * @private
900
911
  */
901
912
  private executeToolCall;
913
+ /**
914
+ * Extracts function call objects from an array of response output items.
915
+ *
916
+ * @param {ResponseOutputItem[]} output - The array of response output items to filter.
917
+ * @return {ResponseFunctionToolCall[]} An array containing only the function call objects from the input.
918
+ */
902
919
  static extractFunctionCalls(output: ResponseOutputItem[]): ResponseFunctionToolCall[];
903
920
  /**
904
921
  * After a response is received from the LLM, this hook checks for tool calls. If any are found,
package/dist/index.js CHANGED
@@ -202,7 +202,7 @@ class y {
202
202
  return s;
203
203
  }
204
204
  }
205
- const h = {
205
+ const u = {
206
206
  id: "orchestrator",
207
207
  role: `
208
208
  This booth serves as the orchestration layer that analyzes user intent and routes
@@ -315,7 +315,7 @@ class T {
315
315
  if (this.booths[t.id])
316
316
  return;
317
317
  this.booths[t.id] = t, Object.keys(this.booths).filter(
318
- (e) => e !== h.id
318
+ (e) => e !== u.id
319
319
  ).length > 1 && !this.hasOrchestrator && this.enableMultiBoothMode();
320
320
  }
321
321
  /**
@@ -384,6 +384,18 @@ class T {
384
384
  getAllBooths() {
385
385
  return this.booths;
386
386
  }
387
+ /**
388
+ * Returns only booths that should be available for routing (excludes core and orchestrator booths).
389
+ * This prevents double context issues by ensuring system booths are not selectable.
390
+ *
391
+ * @returns Record of selectable booth configurations indexed by their IDs
392
+ */
393
+ getSelectableBooths() {
394
+ const t = {};
395
+ for (const [o, e] of Object.entries(this.booths))
396
+ o !== this.baseBooth.id && o !== u.id && (t[o] = e);
397
+ return t;
398
+ }
387
399
  toArray() {
388
400
  return Object.values(this.booths);
389
401
  }
@@ -392,14 +404,14 @@ class T {
392
404
  * @private
393
405
  */
394
406
  enableMultiBoothMode() {
395
- this.hasOrchestrator || (this.booths[h.id] = h, this.hasOrchestrator = !0, this.currentContextId = h.id, this.onMultiBoothModeEnabled?.());
407
+ this.hasOrchestrator || (this.booths[u.id] = u, this.hasOrchestrator = !0, this.currentContextId = u.id, this.onMultiBoothModeEnabled?.());
396
408
  }
397
409
  /**
398
410
  * Disables multi-booth mode by unregistering the orchestrator and resetting context to base booth.
399
411
  * @private
400
412
  */
401
413
  disableMultiBoothMode() {
402
- this.hasOrchestrator && (delete this.booths[h.id], this.hasOrchestrator = !1, this.currentContextId = this.baseBooth.id, this.onMultiBoothModeDisabled?.());
414
+ this.hasOrchestrator && (delete this.booths[u.id], this.hasOrchestrator = !1, this.currentContextId = this.baseBooth.id, this.onMultiBoothModeDisabled?.());
403
415
  }
404
416
  /**
405
417
  * Sets callback functions for when multi-booth mode is enabled/disabled.
@@ -413,7 +425,7 @@ class T {
413
425
  }
414
426
  get isMultiBoothMode() {
415
427
  return Object.keys(this.booths).filter(
416
- (o) => o !== h.id
428
+ (o) => o !== u.id
417
429
  ).length > 1;
418
430
  }
419
431
  /**
@@ -425,12 +437,12 @@ class T {
425
437
  unregisterBooth(t) {
426
438
  if (!this.booths[t])
427
439
  throw new Error(`Booth with ID ${t} does not exist.`);
428
- if (t === h.id)
440
+ if (t === u.id)
429
441
  throw new Error(
430
442
  "Cannot unregister orchestrator booth directly. It will be automatically managed based on booth count."
431
443
  );
432
444
  delete this.booths[t], Object.keys(this.booths).filter(
433
- (e) => e !== h.id
445
+ (e) => e !== u.id
434
446
  ).length <= 1 && this.hasOrchestrator && this.disableMultiBoothMode();
435
447
  }
436
448
  }
@@ -522,36 +534,18 @@ class E {
522
534
  let o = 0, e = t, r, s = !0;
523
535
  for (; s && o < this.loopLimit; ) {
524
536
  o++;
525
- const i = this.boothPlugins;
526
- e = await this.boothPlugins.runBeforeMessageSend(
527
- {
528
- toolRegistry: this.toolRegistry,
529
- boothRegistry: this.boothRegistry,
530
- pluginRegistry: i,
531
- llmAdapter: this.llmAdapter
532
- },
533
- e
534
- ), r = await this.callLLM(e, {
537
+ const i = {
535
538
  toolRegistry: this.toolRegistry,
536
539
  boothRegistry: this.boothRegistry,
537
- pluginRegistry: i,
540
+ pluginRegistry: this.boothPlugins,
538
541
  llmAdapter: this.llmAdapter
539
- }), e = await this.boothPlugins.runResponseReceived(
540
- {
541
- toolRegistry: this.toolRegistry,
542
- boothRegistry: this.boothRegistry,
543
- pluginRegistry: i,
544
- llmAdapter: this.llmAdapter
545
- },
542
+ };
543
+ e = await this.boothPlugins.runBeforeMessageSend(i, e), r = await this.callLLM(e, i), e = await this.boothPlugins.runResponseReceived(
544
+ i,
546
545
  e,
547
546
  r
548
547
  ), await this.boothPlugins.runShouldEndInteractionLoop(
549
- {
550
- toolRegistry: this.toolRegistry,
551
- boothRegistry: this.boothRegistry,
552
- pluginRegistry: i,
553
- llmAdapter: this.llmAdapter
554
- },
548
+ i,
555
549
  e,
556
550
  r
557
551
  ) && (console.log("Ending interaction loop because 'shouldEndInteractionLoop' returned true."), s = !1);
@@ -607,7 +601,7 @@ const v = {
607
601
  description: "A specialized booth for summarizing conversation histories."
608
602
  }, m = "route_to_booth";
609
603
  function b(n) {
610
- const t = n.getAllBooths(), o = Object.values(t).map(
604
+ const t = n.getSelectableBooths(), o = Object.values(t).map(
611
605
  (r) => `- ${r.id}: ${r.role}
612
606
  Examples:
613
607
  ${(r.examples || []).map((s) => ` - "${s}"`).join(`
@@ -753,20 +747,23 @@ class I {
753
747
  async onResponseReceived(t, o, e) {
754
748
  let s = [...o.input, ...e?.output ?? []];
755
749
  if (this.responseContainsBoothChange(e)) {
756
- const l = `Please summarize the following conversation history:
750
+ const l = `Please compact the following LLM conversation history. Take key points and make it useful for
751
+ an LLM that may need to look back for references. Conclude the summary with any next steps or
752
+ actions needed to complete the conversation.
757
753
 
758
- ${JSON.stringify(this.sessionHistory)}`, c = (await $(t.llmAdapter, v).callProcessor.send(l)).output, g = s.filter((a) => "role" in a && a.role === "user").pop();
754
+ The conversation History:
755
+ ${JSON.stringify(this.sessionHistory)}`.trim(), c = (await $(t.llmAdapter, v).callProcessor.send(l)).output, g = s.filter((a) => "role" in a && a.role === "user").pop();
759
756
  console.log("LastUserMessage: ", g), console.log("Summary: ", c), console.log("Summary: ", c.join(""));
760
757
  let f = "";
761
758
  const p = c.find((a) => a.type === "message");
762
759
  console.log("summaryMessages", p), p && "type" in p && p.type == "message" && p.content.forEach((a) => {
763
760
  a.type === "output_text" && (f += a.text);
764
761
  }), console.log("summaryText", f);
765
- const R = {
762
+ const B = {
766
763
  role: "developer",
767
764
  content: `A conversation summary up to this point: ${f}`
768
- }, B = s.filter((a) => !("role" in a && a.role === "user" || "type" in a && a.type === "message"));
769
- this.sessionHistory = g ? [...B, R, g] : [...B, R], s = this.sessionHistory;
765
+ }, R = s.filter((a) => !("role" in a && a.role === "user" || "type" in a && a.type === "message"));
766
+ this.sessionHistory = g ? [...R, B, g] : [...R, B], s = this.sessionHistory;
770
767
  } else
771
768
  this.sessionHistory = s;
772
769
  return {
@@ -945,7 +942,7 @@ class _ {
945
942
  this.tools.delete(t);
946
943
  }
947
944
  }
948
- function x(n) {
945
+ function M(n) {
949
946
  switch (n.type) {
950
947
  case "function":
951
948
  return `function:${n.name}`;
@@ -968,15 +965,15 @@ function x(n) {
968
965
  return `${n.type}:${JSON.stringify(n)}`;
969
966
  }
970
967
  }
971
- function M(n) {
968
+ function x(n) {
972
969
  const t = /* @__PURE__ */ new Set(), o = [];
973
970
  for (const e of n) {
974
- const r = x(e);
971
+ const r = M(e);
975
972
  t.has(r) || (t.add(r), o.push(e));
976
973
  }
977
974
  return o;
978
975
  }
979
- class A {
976
+ class P {
980
977
  description = "A plugin to aggregate and provide tools from base and context booths.";
981
978
  id = "tool-provider";
982
979
  name = "Tool Provider Plugin";
@@ -989,15 +986,15 @@ class A {
989
986
  * @returns The updated response parameters with the aggregated list of tools.
990
987
  */
991
988
  async onBeforeMessageSend(t, o) {
992
- const e = t.boothRegistry.baseBoothConfig, r = t.boothRegistry.currentContextBoothConfig, l = [...e.tools || [], ...r?.tools || []].filter((u, c, g) => g.indexOf(u) === c).map(
993
- (u) => t.toolRegistry.getTool(u)
989
+ const e = t.boothRegistry.baseBoothConfig, r = t.boothRegistry.currentContextBoothConfig, l = [...e.tools || [], ...r?.tools || []].filter((h, c, g) => g.indexOf(h) === c).map(
990
+ (h) => t.toolRegistry.getTool(h)
994
991
  );
995
992
  if (e.mcp && l.push(...e.mcp), r?.mcp && l.push(...r.mcp), t.boothRegistry.isMultiBoothMode) {
996
- const u = b(t.boothRegistry);
997
- l.push(u);
993
+ const h = b(t.boothRegistry);
994
+ l.push(h);
998
995
  }
999
996
  l.push(...t.toolRegistry.getGlobalTools());
1000
- const d = M(l);
997
+ const d = x(l);
1001
998
  return {
1002
999
  ...o,
1003
1000
  tools: d
@@ -1071,6 +1068,12 @@ class w {
1071
1068
  };
1072
1069
  }
1073
1070
  }
1071
+ /**
1072
+ * Extracts function call objects from an array of response output items.
1073
+ *
1074
+ * @param {ResponseOutputItem[]} output - The array of response output items to filter.
1075
+ * @return {ResponseFunctionToolCall[]} An array containing only the function call objects from the input.
1076
+ */
1074
1077
  static extractFunctionCalls(t) {
1075
1078
  return t.filter(
1076
1079
  (o) => o.type === "function_call"
@@ -1094,12 +1097,12 @@ class w {
1094
1097
  const d = s[l];
1095
1098
  if (t.toolRegistry.isLocalTool(d.name))
1096
1099
  continue;
1097
- const u = {
1100
+ const h = {
1098
1101
  responseParams: o,
1099
1102
  response: e,
1100
1103
  toolCallIndex: l,
1101
1104
  totalToolCalls: s.length
1102
- }, c = await this.executeToolCall(t, d, u);
1105
+ }, c = await this.executeToolCall(t, d, h);
1103
1106
  i.push(c);
1104
1107
  }
1105
1108
  return {
@@ -1116,7 +1119,7 @@ class w {
1116
1119
  return !1;
1117
1120
  }
1118
1121
  }
1119
- class P {
1122
+ class L {
1120
1123
  constructor(t = "__awaiting_user_response__") {
1121
1124
  this.marker = t;
1122
1125
  }
@@ -1176,14 +1179,14 @@ class P {
1176
1179
  }
1177
1180
  function $(n, t) {
1178
1181
  const o = new T(t), e = new _(), r = new y();
1179
- return new L({
1182
+ return new A({
1180
1183
  llmAdapter: n,
1181
1184
  booths: o,
1182
1185
  tools: e,
1183
1186
  boothPlugins: r
1184
1187
  });
1185
1188
  }
1186
- class L {
1189
+ class A {
1187
1190
  /**
1188
1191
  * Represents a registry that maintains a collection of plugins for a booth system.
1189
1192
  * The boothPluginRegistry is used to manage and access plugins that enhance
@@ -1252,9 +1255,9 @@ class L {
1252
1255
  this.systemPluginsRegistry = new y(), this.systemPluginsRegistry.registerPlugins([
1253
1256
  new I(t.sessionHistory),
1254
1257
  new C(),
1255
- new A(),
1258
+ new P(),
1256
1259
  new w(),
1257
- new P(t.endInteractionLoopMarker)
1260
+ new L(t.endInteractionLoopMarker)
1258
1261
  ]), this.systemPluginsRegistry.registerPlugins(this.boothPluginRegistry.getPlugins()), this.callProcessor = new E(
1259
1262
  this.boothRegistry,
1260
1263
  this.systemPluginsRegistry,
@@ -1268,11 +1271,11 @@ export {
1268
1271
  T as BoothRegistry,
1269
1272
  C as ContextProviderPlugin,
1270
1273
  I as ConversationHistoryPlugin,
1271
- L as CoreBooth,
1272
- P as FinishTurnPlugin,
1274
+ A as CoreBooth,
1275
+ L as FinishTurnPlugin,
1273
1276
  E as InteractionProcessor,
1274
1277
  w as ToolExecutorPlugin,
1275
- A as ToolProviderPlugin,
1278
+ P as ToolProviderPlugin,
1276
1279
  _ as ToolRegistry,
1277
1280
  $ as createCoreBooth,
1278
1281
  b as createRouteToBoothTool
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "booths",
3
3
  "private": false,
4
- "version": "1.4.0",
4
+ "version": "1.5.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -12,7 +12,8 @@
12
12
  "exports": {
13
13
  ".": {
14
14
  "types": "./dist/index.d.ts",
15
- "import": "./dist/index.js"
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.cjs"
16
17
  }
17
18
  },
18
19
  "scripts": {