@sschepis/oboto-agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -0
- package/dist/index.d.ts +188 -0
- package/dist/index.js +431 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# oboto-agent
|
|
2
|
+
|
|
3
|
+
Event-driven dual-LLM orchestration library for autonomous AI agents.
|
|
4
|
+
|
|
5
|
+
`oboto-agent` is a lightweight TypeScript library that acts as the central nervous system for AI agents. It binds three specialized primitives together through a typed event bus:
|
|
6
|
+
|
|
7
|
+
- **[lmscript](https://github.com/sschepis/lmscript)** — LLM I/O, structured output, provider abstraction
|
|
8
|
+
- **[swiss-army-tool](https://github.com/sschepis/swiss-army-tool)** — Hierarchical tool execution
|
|
9
|
+
- **[as-agent](https://github.com/sschepis/as-agent)** — Session state and conversation history
|
|
10
|
+
|
|
11
|
+
## Key Features
|
|
12
|
+
|
|
13
|
+
- **Dual-LLM architecture** — Fast local model (Ollama/LMStudio) for triage, powerful cloud model (Anthropic/OpenAI/Gemini) for complex tasks
|
|
14
|
+
- **Automatic triage** — Local model classifies each input and only escalates when needed
|
|
15
|
+
- **Event-driven** — All state transitions emit typed events for CLI, web, or daemon integration
|
|
16
|
+
- **Context management** — Automatic summarization when context window fills up
|
|
17
|
+
- **Interruption handling** — Users can redirect the agent mid-execution
|
|
18
|
+
- **Platform-agnostic** — No Node.js-specific APIs; works in browser, Deno, and Bun
|
|
19
|
+
- **Headless** — No UI framework dependency; bring your own interface
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @sschepis/oboto-agent
|
|
25
|
+
|
|
26
|
+
# Peer dependencies
|
|
27
|
+
npm install @sschepis/lmscript @sschepis/swiss-army-tool @sschepis/as-agent zod
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { ObotoAgent } from "@sschepis/oboto-agent";
|
|
34
|
+
import { OllamaProvider, AnthropicProvider } from "@sschepis/lmscript";
|
|
35
|
+
import { TreeBuilder, Router, SessionManager } from "@sschepis/swiss-army-tool";
|
|
36
|
+
|
|
37
|
+
// Build tools
|
|
38
|
+
const builder = new TreeBuilder();
|
|
39
|
+
builder.leaf("time", "Get current time", {}, () => new Date().toISOString());
|
|
40
|
+
builder.leaf("greet", "Say hello", { name: "string" }, (args) => `Hello, ${args.name}!`);
|
|
41
|
+
const { root } = builder.build();
|
|
42
|
+
const router = new Router(root, new SessionManager("s1"));
|
|
43
|
+
|
|
44
|
+
// Create agent
|
|
45
|
+
const agent = new ObotoAgent({
|
|
46
|
+
localModel: new OllamaProvider({ baseUrl: "http://localhost:11434" }),
|
|
47
|
+
remoteModel: new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY! }),
|
|
48
|
+
localModelName: "llama3:8b",
|
|
49
|
+
remoteModelName: "claude-sonnet-4-20250514",
|
|
50
|
+
router,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Listen to events
|
|
54
|
+
agent.on("agent_thought", (e) => console.log(e.payload.text));
|
|
55
|
+
agent.on("tool_execution_complete", (e) => console.log("Tool:", e.payload.result));
|
|
56
|
+
agent.on("error", (e) => console.error(e.payload.message));
|
|
57
|
+
|
|
58
|
+
// Run
|
|
59
|
+
await agent.submitInput("What time is it?");
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Architecture
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
User Input
|
|
66
|
+
│
|
|
67
|
+
▼
|
|
68
|
+
┌──────────────────────────┐
|
|
69
|
+
│ ObotoAgent │
|
|
70
|
+
│ │
|
|
71
|
+
│ ┌─────────┐ ┌────────┐ │
|
|
72
|
+
│ │ Event │ │Context │ │
|
|
73
|
+
│ │ Bus │ │Manager │ │
|
|
74
|
+
│ └─────────┘ └────────┘ │
|
|
75
|
+
│ │
|
|
76
|
+
│ Triage (local LLM) │
|
|
77
|
+
│ ├── Simple → respond │
|
|
78
|
+
│ └── Complex → escalate │
|
|
79
|
+
│ │
|
|
80
|
+
│ AgentLoop (remote LLM) │
|
|
81
|
+
│ └── Tool calls → Router│
|
|
82
|
+
└──────────────────────────┘
|
|
83
|
+
│ │ │
|
|
84
|
+
▼ ▼ ▼
|
|
85
|
+
as-agent lmscript swiss-army-tool
|
|
86
|
+
(state) (LLM I/O) (tools)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Execution Flow
|
|
90
|
+
|
|
91
|
+
1. **Input** — User submits text via `submitInput()`
|
|
92
|
+
2. **Record** — Message appended to session (as-agent) and context window (lmscript ContextStack)
|
|
93
|
+
3. **Triage** — Local LLM classifies: simple or complex?
|
|
94
|
+
4. **Direct response** — If simple, local model responds immediately
|
|
95
|
+
5. **Escalate** — If complex, remote model runs with tool access via AgentLoop
|
|
96
|
+
6. **Tool execution** — LLM calls tools through the swiss-army-tool Router
|
|
97
|
+
7. **Turn complete** — Response recorded, events emitted
|
|
98
|
+
|
|
99
|
+
### Events
|
|
100
|
+
|
|
101
|
+
| Event | Description |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `user_input` | User submitted text |
|
|
104
|
+
| `triage_result` | Local LLM classified the input |
|
|
105
|
+
| `agent_thought` | LLM produced text output |
|
|
106
|
+
| `tool_execution_start` | Tool call began |
|
|
107
|
+
| `tool_execution_complete` | Tool call finished |
|
|
108
|
+
| `state_updated` | Session or context changed |
|
|
109
|
+
| `interruption` | User interrupted mid-execution |
|
|
110
|
+
| `error` | Something failed |
|
|
111
|
+
| `turn_complete` | Full turn finished |
|
|
112
|
+
|
|
113
|
+
## API
|
|
114
|
+
|
|
115
|
+
### `ObotoAgent`
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const agent = new ObotoAgent(config: ObotoAgentConfig);
|
|
119
|
+
|
|
120
|
+
await agent.submitInput(text); // Submit user input
|
|
121
|
+
agent.interrupt(newDirectives?); // Halt and redirect
|
|
122
|
+
agent.on(event, handler); // Subscribe (returns unsub fn)
|
|
123
|
+
agent.once(event, handler); // One-time subscribe
|
|
124
|
+
agent.getSession(); // Get session state
|
|
125
|
+
agent.processing; // Is currently executing?
|
|
126
|
+
agent.removeAllListeners(); // Clear all subscriptions
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Configuration
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
interface ObotoAgentConfig {
|
|
133
|
+
localModel: LLMProvider; // Fast local model
|
|
134
|
+
remoteModel: LLMProvider; // Powerful cloud model
|
|
135
|
+
localModelName: string; // e.g. "llama3:8b"
|
|
136
|
+
remoteModelName: string; // e.g. "claude-sonnet-4-20250514"
|
|
137
|
+
router: Router; // swiss-army-tool Router
|
|
138
|
+
session?: Session; // Resume existing session
|
|
139
|
+
maxContextTokens?: number; // Default: 8192
|
|
140
|
+
maxIterations?: number; // Default: 10
|
|
141
|
+
systemPrompt?: string; // Custom system prompt
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Utility Exports
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Adapters
|
|
149
|
+
createRouterTool(router, root?) // Router → lmscript ToolDefinition
|
|
150
|
+
toChat(msg) // as-agent → lmscript message
|
|
151
|
+
fromChat(msg) // lmscript → as-agent message
|
|
152
|
+
sessionToHistory(session) // Session → ChatMessage[]
|
|
153
|
+
createEmptySession() // Fresh empty session
|
|
154
|
+
|
|
155
|
+
// Components
|
|
156
|
+
AgentEventBus // Standalone event emitter
|
|
157
|
+
ContextManager // Context window manager
|
|
158
|
+
createTriageFunction(modelName) // Triage LScriptFunction
|
|
159
|
+
TriageSchema // Zod schema for triage output
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Examples
|
|
163
|
+
|
|
164
|
+
See the [examples/](examples/) directory:
|
|
165
|
+
|
|
166
|
+
- **[basic-cli.ts](examples/basic-cli.ts)** — Interactive CLI agent with triage
|
|
167
|
+
- **[custom-tools.ts](examples/custom-tools.ts)** — Rich file system tool tree
|
|
168
|
+
- **[event-monitoring.ts](examples/event-monitoring.ts)** — Log all agent events with colors
|
|
169
|
+
- **[session-persistence.ts](examples/session-persistence.ts)** — Save/restore sessions across restarts
|
|
170
|
+
|
|
171
|
+
## Documentation
|
|
172
|
+
|
|
173
|
+
- **[Architecture](docs/architecture.md)** — System design, data flow, adapter layer
|
|
174
|
+
- **[API Reference](docs/api.md)** — Complete API documentation
|
|
175
|
+
- **[Guides](docs/guides.md)** — How-to guides for common tasks
|
|
176
|
+
|
|
177
|
+
## Development
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
npm install # Install dependencies
|
|
181
|
+
npm run build # Build with tsup
|
|
182
|
+
npm test # Run tests with vitest
|
|
183
|
+
npm run typecheck # Type-check without emitting
|
|
184
|
+
npm run dev # Watch mode build
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Supported Providers
|
|
188
|
+
|
|
189
|
+
Any lmscript provider works as either the local or remote model:
|
|
190
|
+
|
|
191
|
+
| Provider | Package | Typical Role |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| Ollama | `OllamaProvider` | Local |
|
|
194
|
+
| LM Studio | `LMStudioProvider` | Local |
|
|
195
|
+
| Anthropic | `AnthropicProvider` | Remote |
|
|
196
|
+
| OpenAI | `OpenAIProvider` | Remote |
|
|
197
|
+
| Google Gemini | `GeminiProvider` | Remote |
|
|
198
|
+
| OpenRouter | `OpenRouterProvider` | Remote |
|
|
199
|
+
| DeepSeek | `DeepSeekProvider` | Remote |
|
|
200
|
+
| AWS Bedrock | `VertexAnthropicProvider` | Remote |
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Session, ConversationMessage } from '@sschepis/as-agent';
|
|
2
|
+
import { LLMProvider, ToolDefinition, ChatMessage, LScriptRuntime, LScriptFunction } from '@sschepis/lmscript';
|
|
3
|
+
import { Router, BranchNode } from '@sschepis/swiss-army-tool';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
interface ObotoAgentConfig {
|
|
7
|
+
/** Small, fast model for triage and summarization (e.g. Ollama, LMStudio) */
|
|
8
|
+
localModel: LLMProvider;
|
|
9
|
+
/** Powerful model for complex reasoning (e.g. Anthropic, OpenAI, Gemini) */
|
|
10
|
+
remoteModel: LLMProvider;
|
|
11
|
+
/** Model identifier for the local provider (e.g. "llama3:8b") */
|
|
12
|
+
localModelName: string;
|
|
13
|
+
/** Model identifier for the remote provider (e.g. "claude-sonnet-4-20250514") */
|
|
14
|
+
remoteModelName: string;
|
|
15
|
+
/** Pre-built swiss-army-tool Router for tool execution */
|
|
16
|
+
router: Router;
|
|
17
|
+
/** Existing session to resume (creates empty session if omitted) */
|
|
18
|
+
session?: Session;
|
|
19
|
+
/** Maximum tokens for context window management. Default: 8192 */
|
|
20
|
+
maxContextTokens?: number;
|
|
21
|
+
/** Maximum LLM iterations per turn. Default: 10 */
|
|
22
|
+
maxIterations?: number;
|
|
23
|
+
/** System prompt prepended to all LLM calls */
|
|
24
|
+
systemPrompt?: string;
|
|
25
|
+
}
|
|
26
|
+
type AgentEventType = "user_input" | "agent_thought" | "triage_result" | "tool_execution_start" | "tool_execution_complete" | "state_updated" | "interruption" | "error" | "turn_complete";
|
|
27
|
+
interface AgentEvent<T = unknown> {
|
|
28
|
+
type: AgentEventType;
|
|
29
|
+
payload: T;
|
|
30
|
+
timestamp: number;
|
|
31
|
+
}
|
|
32
|
+
interface TriageResult {
|
|
33
|
+
/** Whether the input should be escalated to the remote model */
|
|
34
|
+
escalate: boolean;
|
|
35
|
+
/** Brief reasoning for the triage decision */
|
|
36
|
+
reasoning: string;
|
|
37
|
+
/** Direct response if the local model can answer immediately */
|
|
38
|
+
directResponse?: string;
|
|
39
|
+
}
|
|
40
|
+
interface ToolExecutionEvent {
|
|
41
|
+
command: string;
|
|
42
|
+
kwargs: Record<string, unknown>;
|
|
43
|
+
result?: string;
|
|
44
|
+
error?: string;
|
|
45
|
+
durationMs?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type EventHandler$1 = (event: AgentEvent) => void;
|
|
49
|
+
/**
|
|
50
|
+
* ObotoAgent is the central orchestrator for dual-LLM agent execution.
|
|
51
|
+
*
|
|
52
|
+
* It binds together:
|
|
53
|
+
* - lmscript (LLM I/O via local and remote providers)
|
|
54
|
+
* - swiss-army-tool (tool execution via Router)
|
|
55
|
+
* - as-agent (session state and conversation history)
|
|
56
|
+
*
|
|
57
|
+
* All interaction flows through an event-driven architecture.
|
|
58
|
+
*/
|
|
59
|
+
declare class ObotoAgent {
|
|
60
|
+
private bus;
|
|
61
|
+
private localRuntime;
|
|
62
|
+
private remoteRuntime;
|
|
63
|
+
private contextManager;
|
|
64
|
+
private routerTool;
|
|
65
|
+
private triageFn;
|
|
66
|
+
private session;
|
|
67
|
+
private isProcessing;
|
|
68
|
+
private interrupted;
|
|
69
|
+
private systemPrompt;
|
|
70
|
+
private maxIterations;
|
|
71
|
+
private config;
|
|
72
|
+
constructor(config: ObotoAgentConfig);
|
|
73
|
+
/** Subscribe to agent events. Returns an unsubscribe function. */
|
|
74
|
+
on(type: AgentEventType, handler: EventHandler$1): () => void;
|
|
75
|
+
/** Subscribe to an event for a single emission. */
|
|
76
|
+
once(type: AgentEventType, handler: EventHandler$1): () => void;
|
|
77
|
+
/** Submit user input to the agent. Triggers the execution loop. */
|
|
78
|
+
submitInput(text: string): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Interrupt the current execution loop.
|
|
81
|
+
* Optionally inject new directives into the context.
|
|
82
|
+
*/
|
|
83
|
+
interrupt(newDirectives?: string): void;
|
|
84
|
+
/** Get the current session state. */
|
|
85
|
+
getSession(): Session;
|
|
86
|
+
/** Whether the agent is currently processing input. */
|
|
87
|
+
get processing(): boolean;
|
|
88
|
+
/** Remove all event listeners. */
|
|
89
|
+
removeAllListeners(): void;
|
|
90
|
+
private executionLoop;
|
|
91
|
+
private triage;
|
|
92
|
+
private executeWithModel;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
type EventHandler = (event: AgentEvent) => void;
|
|
96
|
+
/**
|
|
97
|
+
* Platform-agnostic typed event bus.
|
|
98
|
+
* Uses a plain Map instead of Node.js EventEmitter for browser/Deno/Bun compatibility.
|
|
99
|
+
*/
|
|
100
|
+
declare class AgentEventBus {
|
|
101
|
+
private listeners;
|
|
102
|
+
/** Subscribe to an event type. Returns an unsubscribe function. */
|
|
103
|
+
on(type: AgentEventType, handler: EventHandler): () => void;
|
|
104
|
+
/** Unsubscribe a handler from an event type. */
|
|
105
|
+
off(type: AgentEventType, handler: EventHandler): void;
|
|
106
|
+
/** Subscribe to an event type for a single emission. */
|
|
107
|
+
once(type: AgentEventType, handler: EventHandler): () => void;
|
|
108
|
+
/** Emit an event to all subscribers. */
|
|
109
|
+
emit(type: AgentEventType, payload: unknown): void;
|
|
110
|
+
/** Remove all listeners for all event types. */
|
|
111
|
+
removeAllListeners(): void;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Parameter schema for the omni-tool bridge. */
|
|
115
|
+
declare const RouterToolParams: z.ZodObject<{
|
|
116
|
+
command: z.ZodString;
|
|
117
|
+
kwargs: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
118
|
+
}, "strip", z.ZodTypeAny, {
|
|
119
|
+
command: string;
|
|
120
|
+
kwargs: Record<string, unknown>;
|
|
121
|
+
}, {
|
|
122
|
+
command: string;
|
|
123
|
+
kwargs?: Record<string, unknown> | undefined;
|
|
124
|
+
}>;
|
|
125
|
+
/**
|
|
126
|
+
* Bridge a swiss-army-tool Router into an lmscript ToolDefinition.
|
|
127
|
+
*
|
|
128
|
+
* The LLM sees a single tool ("terminal_interface") with `command` and `kwargs`
|
|
129
|
+
* parameters. When called, it routes through the swiss-army-tool command tree.
|
|
130
|
+
*/
|
|
131
|
+
declare function createRouterTool(router: Router, root?: BranchNode): ToolDefinition<typeof RouterToolParams, string>;
|
|
132
|
+
|
|
133
|
+
/** Convert an as-agent ConversationMessage to an lmscript ChatMessage. */
|
|
134
|
+
declare function toChat(msg: ConversationMessage): ChatMessage;
|
|
135
|
+
/** Convert an lmscript ChatMessage to an as-agent ConversationMessage. */
|
|
136
|
+
declare function fromChat(msg: ChatMessage): ConversationMessage;
|
|
137
|
+
/** Convert an entire as-agent Session to an array of lmscript ChatMessages. */
|
|
138
|
+
declare function sessionToHistory(session: Session): ChatMessage[];
|
|
139
|
+
/** Create an empty as-agent Session. */
|
|
140
|
+
declare function createEmptySession(): Session;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Manages the sliding context window with automatic summarization.
|
|
144
|
+
* Wraps lmscript's ContextStack and uses the local LLM for compression.
|
|
145
|
+
*/
|
|
146
|
+
declare class ContextManager {
|
|
147
|
+
private localRuntime;
|
|
148
|
+
private stack;
|
|
149
|
+
private summarizeFn;
|
|
150
|
+
constructor(localRuntime: LScriptRuntime, localModelName: string, maxTokens: number);
|
|
151
|
+
/** Append a message to the context. Triggers pruning if over budget. */
|
|
152
|
+
push(message: ChatMessage): Promise<void>;
|
|
153
|
+
/** Append multiple messages. */
|
|
154
|
+
pushAll(messages: ChatMessage[]): Promise<void>;
|
|
155
|
+
/** Get all messages in the current context window. */
|
|
156
|
+
getMessages(): ChatMessage[];
|
|
157
|
+
/** Get estimated token count. */
|
|
158
|
+
getTokenCount(): number;
|
|
159
|
+
/** Clear all context. */
|
|
160
|
+
clear(): void;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Zod schema for structured triage output. */
|
|
164
|
+
declare const TriageSchema: z.ZodObject<{
|
|
165
|
+
escalate: z.ZodBoolean;
|
|
166
|
+
reasoning: z.ZodString;
|
|
167
|
+
directResponse: z.ZodOptional<z.ZodString>;
|
|
168
|
+
}, "strip", z.ZodTypeAny, {
|
|
169
|
+
escalate: boolean;
|
|
170
|
+
reasoning: string;
|
|
171
|
+
directResponse?: string | undefined;
|
|
172
|
+
}, {
|
|
173
|
+
escalate: boolean;
|
|
174
|
+
reasoning: string;
|
|
175
|
+
directResponse?: string | undefined;
|
|
176
|
+
}>;
|
|
177
|
+
type TriageInput = {
|
|
178
|
+
userInput: string;
|
|
179
|
+
recentContext: string;
|
|
180
|
+
availableTools: string;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Create an LScriptFunction for local-LLM triage classification.
|
|
184
|
+
* The local model evaluates whether input needs escalation to the remote model.
|
|
185
|
+
*/
|
|
186
|
+
declare function createTriageFunction(modelName: string): LScriptFunction<TriageInput, typeof TriageSchema>;
|
|
187
|
+
|
|
188
|
+
export { type AgentEvent, AgentEventBus, type AgentEventType, ContextManager, ObotoAgent, type ObotoAgentConfig, type ToolExecutionEvent, type TriageResult, TriageSchema, createEmptySession, createRouterTool, createTriageFunction, fromChat, sessionToHistory, toChat };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
// src/oboto-agent.ts
|
|
2
|
+
import { z as z4 } from "zod";
|
|
3
|
+
import {
|
|
4
|
+
LScriptRuntime,
|
|
5
|
+
AgentLoop
|
|
6
|
+
} from "@sschepis/lmscript";
|
|
7
|
+
import { MessageRole as MessageRole2 } from "@sschepis/as-agent";
|
|
8
|
+
|
|
9
|
+
// src/event-bus.ts
|
|
10
|
+
var AgentEventBus = class {
|
|
11
|
+
listeners = /* @__PURE__ */ new Map();
|
|
12
|
+
/** Subscribe to an event type. Returns an unsubscribe function. */
|
|
13
|
+
on(type, handler) {
|
|
14
|
+
if (!this.listeners.has(type)) {
|
|
15
|
+
this.listeners.set(type, /* @__PURE__ */ new Set());
|
|
16
|
+
}
|
|
17
|
+
this.listeners.get(type).add(handler);
|
|
18
|
+
return () => this.off(type, handler);
|
|
19
|
+
}
|
|
20
|
+
/** Unsubscribe a handler from an event type. */
|
|
21
|
+
off(type, handler) {
|
|
22
|
+
this.listeners.get(type)?.delete(handler);
|
|
23
|
+
}
|
|
24
|
+
/** Subscribe to an event type for a single emission. */
|
|
25
|
+
once(type, handler) {
|
|
26
|
+
const wrapper = (event) => {
|
|
27
|
+
this.off(type, wrapper);
|
|
28
|
+
handler(event);
|
|
29
|
+
};
|
|
30
|
+
return this.on(type, wrapper);
|
|
31
|
+
}
|
|
32
|
+
/** Emit an event to all subscribers. */
|
|
33
|
+
emit(type, payload) {
|
|
34
|
+
const event = {
|
|
35
|
+
type,
|
|
36
|
+
payload,
|
|
37
|
+
timestamp: Date.now()
|
|
38
|
+
};
|
|
39
|
+
const handlers = this.listeners.get(type);
|
|
40
|
+
if (handlers) {
|
|
41
|
+
for (const handler of handlers) {
|
|
42
|
+
handler(event);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Remove all listeners for all event types. */
|
|
47
|
+
removeAllListeners() {
|
|
48
|
+
this.listeners.clear();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/context-manager.ts
|
|
53
|
+
import { z } from "zod";
|
|
54
|
+
import { ContextStack } from "@sschepis/lmscript";
|
|
55
|
+
var SummarySchema = z.object({
|
|
56
|
+
summary: z.string().describe("A dense summary of the conversation so far")
|
|
57
|
+
});
|
|
58
|
+
var ContextManager = class {
|
|
59
|
+
constructor(localRuntime, localModelName, maxTokens) {
|
|
60
|
+
this.localRuntime = localRuntime;
|
|
61
|
+
this.stack = new ContextStack({
|
|
62
|
+
maxTokens,
|
|
63
|
+
pruneStrategy: "summarize"
|
|
64
|
+
});
|
|
65
|
+
this.summarizeFn = {
|
|
66
|
+
name: "summarize_context",
|
|
67
|
+
model: localModelName,
|
|
68
|
+
system: "You are a summarization engine. Compress the given conversation into a dense, factual summary that preserves all key information, decisions, and context needed for continued operation. Be concise but thorough.",
|
|
69
|
+
prompt: ({ conversation }) => conversation,
|
|
70
|
+
schema: SummarySchema,
|
|
71
|
+
temperature: 0.2,
|
|
72
|
+
maxRetries: 1
|
|
73
|
+
};
|
|
74
|
+
this.stack.setSummarizer(async (messages) => {
|
|
75
|
+
const conversation = messages.map((m) => {
|
|
76
|
+
const text = typeof m.content === "string" ? m.content : m.content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
77
|
+
return `${m.role}: ${text}`;
|
|
78
|
+
}).join("\n");
|
|
79
|
+
const result = await this.localRuntime.execute(this.summarizeFn, {
|
|
80
|
+
conversation
|
|
81
|
+
});
|
|
82
|
+
return result.data.summary;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
localRuntime;
|
|
86
|
+
stack;
|
|
87
|
+
summarizeFn;
|
|
88
|
+
/** Append a message to the context. Triggers pruning if over budget. */
|
|
89
|
+
async push(message) {
|
|
90
|
+
await this.stack.push(message);
|
|
91
|
+
}
|
|
92
|
+
/** Append multiple messages. */
|
|
93
|
+
async pushAll(messages) {
|
|
94
|
+
await this.stack.pushAll(messages);
|
|
95
|
+
}
|
|
96
|
+
/** Get all messages in the current context window. */
|
|
97
|
+
getMessages() {
|
|
98
|
+
return this.stack.getMessages();
|
|
99
|
+
}
|
|
100
|
+
/** Get estimated token count. */
|
|
101
|
+
getTokenCount() {
|
|
102
|
+
return this.stack.getTokenCount();
|
|
103
|
+
}
|
|
104
|
+
/** Clear all context. */
|
|
105
|
+
clear() {
|
|
106
|
+
this.stack.clear();
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// src/triage.ts
|
|
111
|
+
import { z as z2 } from "zod";
|
|
112
|
+
var TriageSchema = z2.object({
|
|
113
|
+
escalate: z2.boolean().describe("True if the request needs a powerful model, false if answerable directly"),
|
|
114
|
+
reasoning: z2.string().describe("Brief explanation of the triage decision"),
|
|
115
|
+
directResponse: z2.string().optional().describe("Direct answer if the request can be handled without escalation")
|
|
116
|
+
});
|
|
117
|
+
var TRIAGE_SYSTEM = `You are a fast triage classifier for an AI agent system.
|
|
118
|
+
Your job is to decide whether a user's request can be answered directly (simple queries,
|
|
119
|
+
casual chat, short lookups) or needs to be escalated to a more powerful model
|
|
120
|
+
(complex reasoning, multi-step tool usage, code generation, analysis).
|
|
121
|
+
|
|
122
|
+
Rules:
|
|
123
|
+
- If the request is a greeting, simple question, or casual conversation: respond directly.
|
|
124
|
+
- If the request needs tool calls, code analysis, or multi-step reasoning: escalate.
|
|
125
|
+
- If unsure, escalate. It's better to over-escalate than to give a poor direct answer.
|
|
126
|
+
- Keep directResponse under 200 words when answering directly.
|
|
127
|
+
|
|
128
|
+
Respond with JSON matching the schema.`;
|
|
129
|
+
function createTriageFunction(modelName) {
|
|
130
|
+
return {
|
|
131
|
+
name: "triage",
|
|
132
|
+
model: modelName,
|
|
133
|
+
system: TRIAGE_SYSTEM,
|
|
134
|
+
prompt: ({ userInput, recentContext, availableTools }) => `Recent context:
|
|
135
|
+
${recentContext}
|
|
136
|
+
|
|
137
|
+
Available tools: ${availableTools}
|
|
138
|
+
|
|
139
|
+
User: ${userInput}`,
|
|
140
|
+
schema: TriageSchema,
|
|
141
|
+
temperature: 0.1,
|
|
142
|
+
maxRetries: 1
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/adapters/tools.ts
|
|
147
|
+
import { z as z3 } from "zod";
|
|
148
|
+
import { generateToolSchema } from "@sschepis/swiss-army-tool";
|
|
149
|
+
var RouterToolParams = z3.object({
|
|
150
|
+
command: z3.string().describe(
|
|
151
|
+
"The command or menu path (e.g., 'help', 'filesystem read', 'db query')"
|
|
152
|
+
),
|
|
153
|
+
kwargs: z3.record(z3.unknown()).optional().default({}).describe("Key-value arguments for the command")
|
|
154
|
+
});
|
|
155
|
+
function createRouterTool(router, root) {
|
|
156
|
+
const schema = generateToolSchema({ root });
|
|
157
|
+
return {
|
|
158
|
+
name: schema.name,
|
|
159
|
+
description: schema.description,
|
|
160
|
+
parameters: RouterToolParams,
|
|
161
|
+
execute: async (params) => {
|
|
162
|
+
return router.execute(params.command, params.kwargs ?? {});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/adapters/memory.ts
|
|
168
|
+
import {
|
|
169
|
+
MessageRole
|
|
170
|
+
} from "@sschepis/as-agent";
|
|
171
|
+
var ROLE_TO_STRING = {
|
|
172
|
+
[MessageRole.System]: "system",
|
|
173
|
+
[MessageRole.User]: "user",
|
|
174
|
+
[MessageRole.Assistant]: "assistant",
|
|
175
|
+
[MessageRole.Tool]: "user"
|
|
176
|
+
};
|
|
177
|
+
var STRING_TO_ROLE = {
|
|
178
|
+
system: MessageRole.System,
|
|
179
|
+
user: MessageRole.User,
|
|
180
|
+
assistant: MessageRole.Assistant
|
|
181
|
+
};
|
|
182
|
+
function blocksToText(blocks) {
|
|
183
|
+
return blocks.map((b) => {
|
|
184
|
+
switch (b.kind) {
|
|
185
|
+
case "text":
|
|
186
|
+
return b.text;
|
|
187
|
+
case "tool_use":
|
|
188
|
+
return `[Tool call: ${b.name}(${b.input})]`;
|
|
189
|
+
case "tool_result":
|
|
190
|
+
return b.isError ? `[Tool error (${b.toolName}): ${b.output}]` : `[Tool result (${b.toolName}): ${b.output}]`;
|
|
191
|
+
}
|
|
192
|
+
}).join("\n");
|
|
193
|
+
}
|
|
194
|
+
function toChat(msg) {
|
|
195
|
+
return {
|
|
196
|
+
role: ROLE_TO_STRING[msg.role] ?? "user",
|
|
197
|
+
content: blocksToText(msg.blocks)
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function fromChat(msg) {
|
|
201
|
+
const text = typeof msg.content === "string" ? msg.content : msg.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
202
|
+
return {
|
|
203
|
+
role: STRING_TO_ROLE[msg.role] ?? MessageRole.User,
|
|
204
|
+
blocks: [{ kind: "text", text }]
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function sessionToHistory(session) {
|
|
208
|
+
return session.messages.map(toChat);
|
|
209
|
+
}
|
|
210
|
+
function createEmptySession() {
|
|
211
|
+
return { version: 1, messages: [] };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/oboto-agent.ts
|
|
215
|
+
var AgentResponseSchema = z4.object({
|
|
216
|
+
response: z4.string().describe("The agent's response to the user")
|
|
217
|
+
});
|
|
218
|
+
var ObotoAgent = class {
|
|
219
|
+
bus = new AgentEventBus();
|
|
220
|
+
localRuntime;
|
|
221
|
+
remoteRuntime;
|
|
222
|
+
contextManager;
|
|
223
|
+
routerTool;
|
|
224
|
+
triageFn;
|
|
225
|
+
session;
|
|
226
|
+
isProcessing = false;
|
|
227
|
+
interrupted = false;
|
|
228
|
+
systemPrompt;
|
|
229
|
+
maxIterations;
|
|
230
|
+
config;
|
|
231
|
+
constructor(config) {
|
|
232
|
+
this.config = config;
|
|
233
|
+
this.localRuntime = new LScriptRuntime({ provider: config.localModel });
|
|
234
|
+
this.remoteRuntime = new LScriptRuntime({ provider: config.remoteModel });
|
|
235
|
+
this.session = config.session ?? createEmptySession();
|
|
236
|
+
this.systemPrompt = config.systemPrompt ?? "You are a helpful AI assistant with access to tools.";
|
|
237
|
+
this.maxIterations = config.maxIterations ?? 10;
|
|
238
|
+
this.contextManager = new ContextManager(
|
|
239
|
+
this.localRuntime,
|
|
240
|
+
config.localModelName,
|
|
241
|
+
config.maxContextTokens ?? 8192
|
|
242
|
+
);
|
|
243
|
+
this.routerTool = createRouterTool(config.router);
|
|
244
|
+
this.triageFn = createTriageFunction(config.localModelName);
|
|
245
|
+
this.contextManager.push({
|
|
246
|
+
role: "system",
|
|
247
|
+
content: this.systemPrompt
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
// ── Public API ─────────────────────────────────────────────────────
|
|
251
|
+
/** Subscribe to agent events. Returns an unsubscribe function. */
|
|
252
|
+
on(type, handler) {
|
|
253
|
+
return this.bus.on(type, handler);
|
|
254
|
+
}
|
|
255
|
+
/** Subscribe to an event for a single emission. */
|
|
256
|
+
once(type, handler) {
|
|
257
|
+
return this.bus.once(type, handler);
|
|
258
|
+
}
|
|
259
|
+
/** Submit user input to the agent. Triggers the execution loop. */
|
|
260
|
+
async submitInput(text) {
|
|
261
|
+
if (this.isProcessing) {
|
|
262
|
+
this.interrupt(text);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
this.isProcessing = true;
|
|
266
|
+
this.interrupted = false;
|
|
267
|
+
try {
|
|
268
|
+
await this.executionLoop(text);
|
|
269
|
+
} catch (err) {
|
|
270
|
+
this.bus.emit("error", {
|
|
271
|
+
message: err instanceof Error ? err.message : String(err),
|
|
272
|
+
error: err
|
|
273
|
+
});
|
|
274
|
+
} finally {
|
|
275
|
+
this.isProcessing = false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Interrupt the current execution loop.
|
|
280
|
+
* Optionally inject new directives into the context.
|
|
281
|
+
*/
|
|
282
|
+
interrupt(newDirectives) {
|
|
283
|
+
this.interrupted = true;
|
|
284
|
+
this.bus.emit("interruption", { newDirectives });
|
|
285
|
+
if (newDirectives) {
|
|
286
|
+
const msg = {
|
|
287
|
+
role: MessageRole2.User,
|
|
288
|
+
blocks: [{ kind: "text", text: `[INTERRUPTION] ${newDirectives}` }]
|
|
289
|
+
};
|
|
290
|
+
this.session.messages.push(msg);
|
|
291
|
+
this.contextManager.push(toChat(msg));
|
|
292
|
+
this.bus.emit("state_updated", { reason: "interruption" });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/** Get the current session state. */
|
|
296
|
+
getSession() {
|
|
297
|
+
return this.session;
|
|
298
|
+
}
|
|
299
|
+
/** Whether the agent is currently processing input. */
|
|
300
|
+
get processing() {
|
|
301
|
+
return this.isProcessing;
|
|
302
|
+
}
|
|
303
|
+
/** Remove all event listeners. */
|
|
304
|
+
removeAllListeners() {
|
|
305
|
+
this.bus.removeAllListeners();
|
|
306
|
+
}
|
|
307
|
+
// ── Internal ───────────────────────────────────────────────────────
|
|
308
|
+
async executionLoop(userInput) {
|
|
309
|
+
this.bus.emit("user_input", { text: userInput });
|
|
310
|
+
const userMsg = {
|
|
311
|
+
role: MessageRole2.User,
|
|
312
|
+
blocks: [{ kind: "text", text: userInput }]
|
|
313
|
+
};
|
|
314
|
+
this.session.messages.push(userMsg);
|
|
315
|
+
await this.contextManager.push(toChat(userMsg));
|
|
316
|
+
this.bus.emit("state_updated", { reason: "user_input" });
|
|
317
|
+
const triageResult = await this.triage(userInput);
|
|
318
|
+
this.bus.emit("triage_result", triageResult);
|
|
319
|
+
if (this.interrupted) return;
|
|
320
|
+
if (!triageResult.escalate && triageResult.directResponse) {
|
|
321
|
+
const response = triageResult.directResponse;
|
|
322
|
+
this.bus.emit("agent_thought", { text: response, model: "local" });
|
|
323
|
+
const assistantMsg = {
|
|
324
|
+
role: MessageRole2.Assistant,
|
|
325
|
+
blocks: [{ kind: "text", text: response }]
|
|
326
|
+
};
|
|
327
|
+
this.session.messages.push(assistantMsg);
|
|
328
|
+
await this.contextManager.push(toChat(assistantMsg));
|
|
329
|
+
this.bus.emit("state_updated", { reason: "assistant_response" });
|
|
330
|
+
this.bus.emit("turn_complete", { model: "local", escalated: false });
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const runtime = triageResult.escalate ? this.remoteRuntime : this.localRuntime;
|
|
334
|
+
const modelName = triageResult.escalate ? this.config.remoteModelName : this.config.localModelName;
|
|
335
|
+
if (triageResult.escalate) {
|
|
336
|
+
this.bus.emit("agent_thought", {
|
|
337
|
+
text: triageResult.reasoning,
|
|
338
|
+
model: "local",
|
|
339
|
+
escalating: true
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
await this.executeWithModel(runtime, modelName, userInput);
|
|
343
|
+
}
|
|
344
|
+
async triage(userInput) {
|
|
345
|
+
const recentMessages = this.contextManager.getMessages().slice(-5);
|
|
346
|
+
const recentContext = recentMessages.map((m) => {
|
|
347
|
+
const text = typeof m.content === "string" ? m.content : "[complex content]";
|
|
348
|
+
return `${m.role}: ${text}`;
|
|
349
|
+
}).join("\n");
|
|
350
|
+
const result = await this.localRuntime.execute(this.triageFn, {
|
|
351
|
+
userInput,
|
|
352
|
+
recentContext,
|
|
353
|
+
availableTools: this.routerTool.description
|
|
354
|
+
});
|
|
355
|
+
return result.data;
|
|
356
|
+
}
|
|
357
|
+
async executeWithModel(runtime, modelName, userInput) {
|
|
358
|
+
const contextMessages = this.contextManager.getMessages();
|
|
359
|
+
const contextStr = contextMessages.filter((m) => m.role !== "system").map((m) => {
|
|
360
|
+
const text = typeof m.content === "string" ? m.content : "[complex content]";
|
|
361
|
+
return `${m.role}: ${text}`;
|
|
362
|
+
}).join("\n");
|
|
363
|
+
const agentFn = {
|
|
364
|
+
name: "agent_execute",
|
|
365
|
+
model: modelName,
|
|
366
|
+
system: this.systemPrompt,
|
|
367
|
+
prompt: ({ context }) => context ? `Conversation so far:
|
|
368
|
+
${context}
|
|
369
|
+
|
|
370
|
+
Respond to the user's latest message. Use tools when needed.` : `Respond to the user.`,
|
|
371
|
+
schema: AgentResponseSchema,
|
|
372
|
+
temperature: 0.7,
|
|
373
|
+
tools: [this.routerTool]
|
|
374
|
+
};
|
|
375
|
+
const agent = new AgentLoop(runtime, {
|
|
376
|
+
maxIterations: this.maxIterations,
|
|
377
|
+
onToolCall: (toolCall) => {
|
|
378
|
+
const args = toolCall.arguments;
|
|
379
|
+
this.bus.emit("tool_execution_start", {
|
|
380
|
+
command: args?.command ?? toolCall.name,
|
|
381
|
+
kwargs: args?.kwargs ?? {}
|
|
382
|
+
});
|
|
383
|
+
this.bus.emit("tool_execution_complete", {
|
|
384
|
+
command: args?.command ?? toolCall.name,
|
|
385
|
+
kwargs: args?.kwargs ?? {},
|
|
386
|
+
result: toolCall.result
|
|
387
|
+
});
|
|
388
|
+
if (this.interrupted) return false;
|
|
389
|
+
},
|
|
390
|
+
onIteration: (_iteration, response) => {
|
|
391
|
+
this.bus.emit("agent_thought", {
|
|
392
|
+
text: response,
|
|
393
|
+
model: modelName,
|
|
394
|
+
iteration: _iteration
|
|
395
|
+
});
|
|
396
|
+
if (this.interrupted) return false;
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
const result = await agent.run(agentFn, {
|
|
400
|
+
userInput,
|
|
401
|
+
context: contextStr
|
|
402
|
+
});
|
|
403
|
+
const responseText = result.data.response;
|
|
404
|
+
const assistantMsg = {
|
|
405
|
+
role: MessageRole2.Assistant,
|
|
406
|
+
blocks: [{ kind: "text", text: responseText }]
|
|
407
|
+
};
|
|
408
|
+
this.session.messages.push(assistantMsg);
|
|
409
|
+
await this.contextManager.push(toChat(assistantMsg));
|
|
410
|
+
this.bus.emit("state_updated", { reason: "assistant_response" });
|
|
411
|
+
this.bus.emit("turn_complete", {
|
|
412
|
+
model: modelName,
|
|
413
|
+
escalated: true,
|
|
414
|
+
iterations: result.iterations,
|
|
415
|
+
toolCalls: result.toolCalls.length
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
export {
|
|
420
|
+
AgentEventBus,
|
|
421
|
+
ContextManager,
|
|
422
|
+
ObotoAgent,
|
|
423
|
+
TriageSchema,
|
|
424
|
+
createEmptySession,
|
|
425
|
+
createRouterTool,
|
|
426
|
+
createTriageFunction,
|
|
427
|
+
fromChat,
|
|
428
|
+
sessionToHistory,
|
|
429
|
+
toChat
|
|
430
|
+
};
|
|
431
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/oboto-agent.ts","../src/event-bus.ts","../src/context-manager.ts","../src/triage.ts","../src/adapters/tools.ts","../src/adapters/memory.ts"],"sourcesContent":["import { z } from \"zod\";\nimport {\n LScriptRuntime,\n AgentLoop,\n type LScriptFunction,\n type ToolDefinition,\n type ChatMessage,\n type ToolCall,\n} from \"@sschepis/lmscript\";\nimport type { Session, ConversationMessage } from \"@sschepis/as-agent\";\nimport { MessageRole } from \"@sschepis/as-agent\";\nimport type { ObotoAgentConfig, AgentEventType, AgentEvent, TriageResult } from \"./types.js\";\nimport { AgentEventBus } from \"./event-bus.js\";\nimport { ContextManager } from \"./context-manager.js\";\nimport { createTriageFunction, type TriageInput } from \"./triage.js\";\nimport { createRouterTool } from \"./adapters/tools.js\";\nimport { toChat, fromChat, createEmptySession } from \"./adapters/memory.js\";\n\ntype EventHandler = (event: AgentEvent) => void;\n\n/** Free-form response schema for the main agent loop. */\nconst AgentResponseSchema = z.object({\n response: z.string().describe(\"The agent's response to the user\"),\n});\n\ntype AgentInput = { userInput: string; context: string };\n\n/**\n * ObotoAgent is the central orchestrator for dual-LLM agent execution.\n *\n * It binds together:\n * - lmscript (LLM I/O via local and remote providers)\n * - swiss-army-tool (tool execution via Router)\n * - as-agent (session state and conversation history)\n *\n * All interaction flows through an event-driven architecture.\n */\nexport class ObotoAgent {\n private bus = new AgentEventBus();\n private localRuntime: LScriptRuntime;\n private remoteRuntime: LScriptRuntime;\n private contextManager: ContextManager;\n private routerTool: ToolDefinition<any, any>;\n private triageFn: ReturnType<typeof createTriageFunction>;\n private session: Session;\n private isProcessing = false;\n private interrupted = false;\n private systemPrompt: string;\n private maxIterations: number;\n private config: ObotoAgentConfig;\n\n constructor(config: ObotoAgentConfig) {\n this.config = config;\n this.localRuntime = new LScriptRuntime({ provider: config.localModel });\n this.remoteRuntime = new LScriptRuntime({ provider: config.remoteModel });\n this.session = config.session ?? createEmptySession();\n this.systemPrompt = config.systemPrompt ?? \"You are a helpful AI assistant with access to tools.\";\n this.maxIterations = config.maxIterations ?? 10;\n\n this.contextManager = new ContextManager(\n this.localRuntime,\n config.localModelName,\n config.maxContextTokens ?? 8192\n );\n\n this.routerTool = createRouterTool(config.router);\n this.triageFn = createTriageFunction(config.localModelName);\n\n // Push system prompt into context\n this.contextManager.push({\n role: \"system\",\n content: this.systemPrompt,\n });\n }\n\n // ── Public API ─────────────────────────────────────────────────────\n\n /** Subscribe to agent events. Returns an unsubscribe function. */\n on(type: AgentEventType, handler: EventHandler): () => void {\n return this.bus.on(type, handler);\n }\n\n /** Subscribe to an event for a single emission. */\n once(type: AgentEventType, handler: EventHandler): () => void {\n return this.bus.once(type, handler);\n }\n\n /** Submit user input to the agent. Triggers the execution loop. */\n async submitInput(text: string): Promise<void> {\n if (this.isProcessing) {\n this.interrupt(text);\n return;\n }\n\n this.isProcessing = true;\n this.interrupted = false;\n\n try {\n await this.executionLoop(text);\n } catch (err) {\n this.bus.emit(\"error\", {\n message: err instanceof Error ? err.message : String(err),\n error: err,\n });\n } finally {\n this.isProcessing = false;\n }\n }\n\n /**\n * Interrupt the current execution loop.\n * Optionally inject new directives into the context.\n */\n interrupt(newDirectives?: string): void {\n this.interrupted = true;\n this.bus.emit(\"interruption\", { newDirectives });\n\n if (newDirectives) {\n const msg: ConversationMessage = {\n role: MessageRole.User,\n blocks: [{ kind: \"text\", text: `[INTERRUPTION] ${newDirectives}` }],\n };\n this.session.messages.push(msg);\n this.contextManager.push(toChat(msg));\n this.bus.emit(\"state_updated\", { reason: \"interruption\" });\n }\n }\n\n /** Get the current session state. */\n getSession(): Session {\n return this.session;\n }\n\n /** Whether the agent is currently processing input. */\n get processing(): boolean {\n return this.isProcessing;\n }\n\n /** Remove all event listeners. */\n removeAllListeners(): void {\n this.bus.removeAllListeners();\n }\n\n // ── Internal ───────────────────────────────────────────────────────\n\n private async executionLoop(userInput: string): Promise<void> {\n // 1. Emit user_input and record in session + context\n this.bus.emit(\"user_input\", { text: userInput });\n\n const userMsg: ConversationMessage = {\n role: MessageRole.User,\n blocks: [{ kind: \"text\", text: userInput }],\n };\n this.session.messages.push(userMsg);\n await this.contextManager.push(toChat(userMsg));\n this.bus.emit(\"state_updated\", { reason: \"user_input\" });\n\n // 2. Triage via local LLM\n const triageResult = await this.triage(userInput);\n this.bus.emit(\"triage_result\", triageResult);\n\n if (this.interrupted) return;\n\n // 3. If local can handle directly, emit and return\n if (!triageResult.escalate && triageResult.directResponse) {\n const response = triageResult.directResponse;\n this.bus.emit(\"agent_thought\", { text: response, model: \"local\" });\n\n const assistantMsg: ConversationMessage = {\n role: MessageRole.Assistant,\n blocks: [{ kind: \"text\", text: response }],\n };\n this.session.messages.push(assistantMsg);\n await this.contextManager.push(toChat(assistantMsg));\n this.bus.emit(\"state_updated\", { reason: \"assistant_response\" });\n this.bus.emit(\"turn_complete\", { model: \"local\", escalated: false });\n return;\n }\n\n // 4. Escalate to remote model with tool access\n const runtime = triageResult.escalate ? this.remoteRuntime : this.localRuntime;\n const modelName = triageResult.escalate\n ? this.config.remoteModelName\n : this.config.localModelName;\n\n if (triageResult.escalate) {\n this.bus.emit(\"agent_thought\", {\n text: triageResult.reasoning,\n model: \"local\",\n escalating: true,\n });\n }\n\n await this.executeWithModel(runtime, modelName, userInput);\n }\n\n private async triage(userInput: string): Promise<TriageResult> {\n const recentMessages = this.contextManager.getMessages().slice(-5);\n const recentContext = recentMessages\n .map((m) => {\n const text = typeof m.content === \"string\" ? m.content : \"[complex content]\";\n return `${m.role}: ${text}`;\n })\n .join(\"\\n\");\n\n const result = await this.localRuntime.execute(this.triageFn, {\n userInput,\n recentContext,\n availableTools: this.routerTool.description,\n });\n\n return result.data;\n }\n\n private async executeWithModel(\n runtime: LScriptRuntime,\n modelName: string,\n userInput: string\n ): Promise<void> {\n const contextMessages = this.contextManager.getMessages();\n const contextStr = contextMessages\n .filter((m) => m.role !== \"system\")\n .map((m) => {\n const text = typeof m.content === \"string\" ? m.content : \"[complex content]\";\n return `${m.role}: ${text}`;\n })\n .join(\"\\n\");\n\n const agentFn: LScriptFunction<AgentInput, typeof AgentResponseSchema> = {\n name: \"agent_execute\",\n model: modelName,\n system: this.systemPrompt,\n prompt: ({ context }) =>\n context\n ? `Conversation so far:\\n${context}\\n\\nRespond to the user's latest message. Use tools when needed.`\n : `Respond to the user.`,\n schema: AgentResponseSchema,\n temperature: 0.7,\n tools: [this.routerTool],\n };\n\n const agent = new AgentLoop(runtime, {\n maxIterations: this.maxIterations,\n onToolCall: (toolCall: ToolCall) => {\n const args = toolCall.arguments as { command?: string; kwargs?: Record<string, unknown> } | undefined;\n this.bus.emit(\"tool_execution_start\", {\n command: args?.command ?? toolCall.name,\n kwargs: args?.kwargs ?? {},\n });\n this.bus.emit(\"tool_execution_complete\", {\n command: args?.command ?? toolCall.name,\n kwargs: args?.kwargs ?? {},\n result: toolCall.result,\n });\n\n // Check for interruption\n if (this.interrupted) return false;\n },\n onIteration: (_iteration: number, response: string) => {\n this.bus.emit(\"agent_thought\", {\n text: response,\n model: modelName,\n iteration: _iteration,\n });\n if (this.interrupted) return false;\n },\n });\n\n const result = await agent.run(agentFn, {\n userInput,\n context: contextStr,\n });\n\n // Record final response in session and context\n const responseText = result.data.response;\n const assistantMsg: ConversationMessage = {\n role: MessageRole.Assistant,\n blocks: [{ kind: \"text\", text: responseText }],\n };\n this.session.messages.push(assistantMsg);\n await this.contextManager.push(toChat(assistantMsg));\n this.bus.emit(\"state_updated\", { reason: \"assistant_response\" });\n this.bus.emit(\"turn_complete\", {\n model: modelName,\n escalated: true,\n iterations: result.iterations,\n toolCalls: result.toolCalls.length,\n });\n }\n}\n","import type { AgentEventType, AgentEvent } from \"./types.js\";\n\ntype EventHandler = (event: AgentEvent) => void;\n\n/**\n * Platform-agnostic typed event bus.\n * Uses a plain Map instead of Node.js EventEmitter for browser/Deno/Bun compatibility.\n */\nexport class AgentEventBus {\n private listeners = new Map<AgentEventType, Set<EventHandler>>();\n\n /** Subscribe to an event type. Returns an unsubscribe function. */\n on(type: AgentEventType, handler: EventHandler): () => void {\n if (!this.listeners.has(type)) {\n this.listeners.set(type, new Set());\n }\n this.listeners.get(type)!.add(handler);\n return () => this.off(type, handler);\n }\n\n /** Unsubscribe a handler from an event type. */\n off(type: AgentEventType, handler: EventHandler): void {\n this.listeners.get(type)?.delete(handler);\n }\n\n /** Subscribe to an event type for a single emission. */\n once(type: AgentEventType, handler: EventHandler): () => void {\n const wrapper: EventHandler = (event) => {\n this.off(type, wrapper);\n handler(event);\n };\n return this.on(type, wrapper);\n }\n\n /** Emit an event to all subscribers. */\n emit(type: AgentEventType, payload: unknown): void {\n const event: AgentEvent = {\n type,\n payload,\n timestamp: Date.now(),\n };\n const handlers = this.listeners.get(type);\n if (handlers) {\n for (const handler of handlers) {\n handler(event);\n }\n }\n }\n\n /** Remove all listeners for all event types. */\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","import { z } from \"zod\";\nimport { ContextStack, type LScriptRuntime, type ChatMessage, type LScriptFunction } from \"@sschepis/lmscript\";\n\nconst SummarySchema = z.object({\n summary: z.string().describe(\"A dense summary of the conversation so far\"),\n});\n\ntype SummaryInput = { conversation: string };\n\n/**\n * Manages the sliding context window with automatic summarization.\n * Wraps lmscript's ContextStack and uses the local LLM for compression.\n */\nexport class ContextManager {\n private stack: ContextStack;\n private summarizeFn: LScriptFunction<SummaryInput, typeof SummarySchema>;\n\n constructor(\n private localRuntime: LScriptRuntime,\n localModelName: string,\n maxTokens: number\n ) {\n this.stack = new ContextStack({\n maxTokens,\n pruneStrategy: \"summarize\",\n });\n\n this.summarizeFn = {\n name: \"summarize_context\",\n model: localModelName,\n system:\n \"You are a summarization engine. Compress the given conversation into a dense, factual summary that preserves all key information, decisions, and context needed for continued operation. Be concise but thorough.\",\n prompt: ({ conversation }) => conversation,\n schema: SummarySchema,\n temperature: 0.2,\n maxRetries: 1,\n };\n\n this.stack.setSummarizer(async (messages: ChatMessage[]) => {\n const conversation = messages\n .map((m) => {\n const text =\n typeof m.content === \"string\"\n ? m.content\n : m.content\n .filter((b) => b.type === \"text\")\n .map((b) => (b as { text: string }).text)\n .join(\" \");\n return `${m.role}: ${text}`;\n })\n .join(\"\\n\");\n\n const result = await this.localRuntime.execute(this.summarizeFn, {\n conversation,\n });\n return result.data.summary;\n });\n }\n\n /** Append a message to the context. Triggers pruning if over budget. */\n async push(message: ChatMessage): Promise<void> {\n await this.stack.push(message);\n }\n\n /** Append multiple messages. */\n async pushAll(messages: ChatMessage[]): Promise<void> {\n await this.stack.pushAll(messages);\n }\n\n /** Get all messages in the current context window. */\n getMessages(): ChatMessage[] {\n return this.stack.getMessages();\n }\n\n /** Get estimated token count. */\n getTokenCount(): number {\n return this.stack.getTokenCount();\n }\n\n /** Clear all context. */\n clear(): void {\n this.stack.clear();\n }\n}\n","import { z } from \"zod\";\nimport type { LScriptFunction } from \"@sschepis/lmscript\";\n\n/** Zod schema for structured triage output. */\nexport const TriageSchema = z.object({\n escalate: z\n .boolean()\n .describe(\"True if the request needs a powerful model, false if answerable directly\"),\n reasoning: z\n .string()\n .describe(\"Brief explanation of the triage decision\"),\n directResponse: z\n .string()\n .optional()\n .describe(\"Direct answer if the request can be handled without escalation\"),\n});\n\nexport type TriageInput = {\n userInput: string;\n recentContext: string;\n availableTools: string;\n};\n\nconst TRIAGE_SYSTEM = `You are a fast triage classifier for an AI agent system.\nYour job is to decide whether a user's request can be answered directly (simple queries,\ncasual chat, short lookups) or needs to be escalated to a more powerful model\n(complex reasoning, multi-step tool usage, code generation, analysis).\n\nRules:\n- If the request is a greeting, simple question, or casual conversation: respond directly.\n- If the request needs tool calls, code analysis, or multi-step reasoning: escalate.\n- If unsure, escalate. It's better to over-escalate than to give a poor direct answer.\n- Keep directResponse under 200 words when answering directly.\n\nRespond with JSON matching the schema.`;\n\n/**\n * Create an LScriptFunction for local-LLM triage classification.\n * The local model evaluates whether input needs escalation to the remote model.\n */\nexport function createTriageFunction(\n modelName: string\n): LScriptFunction<TriageInput, typeof TriageSchema> {\n return {\n name: \"triage\",\n model: modelName,\n system: TRIAGE_SYSTEM,\n prompt: ({ userInput, recentContext, availableTools }) =>\n `Recent context:\\n${recentContext}\\n\\nAvailable tools: ${availableTools}\\n\\nUser: ${userInput}`,\n schema: TriageSchema,\n temperature: 0.1,\n maxRetries: 1,\n };\n}\n","import { z } from \"zod\";\nimport type { Router } from \"@sschepis/swiss-army-tool\";\nimport { generateToolSchema } from \"@sschepis/swiss-army-tool\";\nimport type { ToolDefinition } from \"@sschepis/lmscript\";\nimport type { BranchNode } from \"@sschepis/swiss-army-tool\";\n\n/** Parameter schema for the omni-tool bridge. */\nconst RouterToolParams = z.object({\n command: z.string().describe(\n \"The command or menu path (e.g., 'help', 'filesystem read', 'db query')\"\n ),\n kwargs: z\n .record(z.unknown())\n .optional()\n .default({})\n .describe(\"Key-value arguments for the command\"),\n});\n\n/**\n * Bridge a swiss-army-tool Router into an lmscript ToolDefinition.\n *\n * The LLM sees a single tool (\"terminal_interface\") with `command` and `kwargs`\n * parameters. When called, it routes through the swiss-army-tool command tree.\n */\nexport function createRouterTool(\n router: Router,\n root?: BranchNode\n): ToolDefinition<typeof RouterToolParams, string> {\n const schema = generateToolSchema({ root });\n\n return {\n name: schema.name,\n description: schema.description,\n parameters: RouterToolParams,\n execute: async (params) => {\n return router.execute(params.command, params.kwargs ?? {});\n },\n };\n}\n","import {\n MessageRole,\n type ConversationMessage,\n type ContentBlock as AsContentBlock,\n type Session,\n} from \"@sschepis/as-agent\";\nimport type { ChatMessage, Role } from \"@sschepis/lmscript\";\n\nconst ROLE_TO_STRING: Record<MessageRole, Role> = {\n [MessageRole.System]: \"system\",\n [MessageRole.User]: \"user\",\n [MessageRole.Assistant]: \"assistant\",\n [MessageRole.Tool]: \"user\",\n};\n\nconst STRING_TO_ROLE: Record<Role, MessageRole> = {\n system: MessageRole.System,\n user: MessageRole.User,\n assistant: MessageRole.Assistant,\n};\n\n/** Extract plain text from as-agent content blocks. */\nfunction blocksToText(blocks: AsContentBlock[]): string {\n return blocks\n .map((b) => {\n switch (b.kind) {\n case \"text\":\n return b.text;\n case \"tool_use\":\n return `[Tool call: ${b.name}(${b.input})]`;\n case \"tool_result\":\n return b.isError\n ? `[Tool error (${b.toolName}): ${b.output}]`\n : `[Tool result (${b.toolName}): ${b.output}]`;\n }\n })\n .join(\"\\n\");\n}\n\n/** Convert an as-agent ConversationMessage to an lmscript ChatMessage. */\nexport function toChat(msg: ConversationMessage): ChatMessage {\n return {\n role: ROLE_TO_STRING[msg.role] ?? \"user\",\n content: blocksToText(msg.blocks),\n };\n}\n\n/** Convert an lmscript ChatMessage to an as-agent ConversationMessage. */\nexport function fromChat(msg: ChatMessage): ConversationMessage {\n const text = typeof msg.content === \"string\"\n ? msg.content\n : msg.content\n .filter((b) => b.type === \"text\")\n .map((b) => (b as { text: string }).text)\n .join(\"\\n\");\n\n return {\n role: STRING_TO_ROLE[msg.role] ?? MessageRole.User,\n blocks: [{ kind: \"text\", text }],\n };\n}\n\n/** Convert an entire as-agent Session to an array of lmscript ChatMessages. */\nexport function sessionToHistory(session: Session): ChatMessage[] {\n return session.messages.map(toChat);\n}\n\n/** Create an empty as-agent Session. */\nexport function createEmptySession(): Session {\n return { version: 1, messages: [] };\n}\n"],"mappings":"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","names":["z","MessageRole","z","z","z","MessageRole"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sschepis/oboto-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Event-driven dual-LLM orchestration library for autonomous AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/sschepis/oboto-agent.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/sschepis/oboto-agent/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/sschepis/oboto-agent#readme",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"ai",
|
|
18
|
+
"agent",
|
|
19
|
+
"llm",
|
|
20
|
+
"orchestration",
|
|
21
|
+
"dual-llm",
|
|
22
|
+
"event-driven",
|
|
23
|
+
"tool-calling",
|
|
24
|
+
"typescript"
|
|
25
|
+
],
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup",
|
|
34
|
+
"dev": "tsup --watch",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"typecheck": "tsc --noEmit"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"zod": "^3.23.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@sschepis/lmscript": "^0.1.0",
|
|
47
|
+
"@sschepis/swiss-army-tool": "^0.1.0",
|
|
48
|
+
"@sschepis/as-agent": "^0.1.0"
|
|
49
|
+
},
|
|
50
|
+
"optionalDependencies": {
|
|
51
|
+
"@aleph-ai/tinyaleph": "^1.7.0",
|
|
52
|
+
"@sschepis/resolang": "^0.5.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@sschepis/lmscript": "file:../lmscript",
|
|
56
|
+
"@sschepis/swiss-army-tool": "file:../swiss-army-tool",
|
|
57
|
+
"@sschepis/as-agent": "file:../claw-code/assembly",
|
|
58
|
+
"tsup": "^8.0.0",
|
|
59
|
+
"typescript": "^5.5.0",
|
|
60
|
+
"vitest": "^2.0.0"
|
|
61
|
+
},
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"author": "Sebastian Schepis"
|
|
64
|
+
}
|