@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 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
@@ -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
+ }