@primo-ai/core 0.1.4 → 0.1.5
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/dist/agent.d.ts +17 -11
- package/dist/agent.js +64 -104
- package/dist/event-bus.d.ts +6 -1
- package/dist/event-bus.js +17 -1
- package/dist/hook-manager.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +4 -4
package/dist/agent.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentConfig, CheckpointStore, PipelineStageConfig, Processor, SessionManager, Tracer } from '@primo-ai/sdk';
|
|
1
|
+
import type { AgentConfig, AgentSimpleConfig, CheckpointStore, PipelineStageConfig, Processor, SessionManager, Tracer } from '@primo-ai/sdk';
|
|
2
2
|
import { PipelineRunner } from './pipeline.js';
|
|
3
3
|
import { serialize } from './serialize.js';
|
|
4
4
|
import { ToolRegistry } from './tool-registry.js';
|
|
@@ -41,6 +41,8 @@ export declare class Agent {
|
|
|
41
41
|
private sessionManager?;
|
|
42
42
|
private contextBuilder;
|
|
43
43
|
private activeAbortController;
|
|
44
|
+
private lastContext?;
|
|
45
|
+
constructor(config: AgentSimpleConfig);
|
|
44
46
|
constructor(config: AgentConfig, deps?: AgentDependencies);
|
|
45
47
|
use(factory: Processor | PluginFactory): void;
|
|
46
48
|
teardown(): Promise<void>;
|
|
@@ -50,6 +52,12 @@ export declare class Agent {
|
|
|
50
52
|
get eventBus(): import('./event-bus.js').EventBus;
|
|
51
53
|
get eventSystem(): import('./event-system.js').EventSystem;
|
|
52
54
|
get state(): import('./state-machine.js').AgentState;
|
|
55
|
+
/** Subscribe to an event type. Returns an unsubscribe function. */
|
|
56
|
+
on(eventType: string, handler: (data?: unknown) => void): () => void;
|
|
57
|
+
/** Subscribe to an event type for at most one emission. */
|
|
58
|
+
once(eventType: string, handler: (data?: unknown) => void): void;
|
|
59
|
+
/** Remove a specific handler for an event type. */
|
|
60
|
+
off(eventType: string, handler: (data?: unknown) => void): void;
|
|
53
61
|
get _contextBuilder(): ContextBuilder;
|
|
54
62
|
run(input: string, signal?: globalThis.AbortSignal): Promise<AgentRunResult>;
|
|
55
63
|
resume(sessionId: string, signal?: globalThis.AbortSignal): Promise<AgentRunResult>;
|
|
@@ -57,22 +65,20 @@ export declare class Agent {
|
|
|
57
65
|
streamEvents(input: string, signal?: globalThis.AbortSignal): AsyncGenerator<import('@primo-ai/sdk').StreamEvent>;
|
|
58
66
|
/** Abort a running agent. Idempotent — no-op if agent is not running. */
|
|
59
67
|
abort(): void;
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
* with a new user message.
|
|
63
|
-
*/
|
|
64
|
-
continue(sessionId: string, message: string, signal?: globalThis.AbortSignal): Promise<AgentRunResult>;
|
|
65
|
-
/**
|
|
66
|
-
* Continue an existing session with streaming. Yields StreamEvents for the
|
|
67
|
-
* continued conversation.
|
|
68
|
-
*/
|
|
69
|
-
continueStream(sessionId: string, message: string, signal?: globalThis.AbortSignal): AsyncGenerator<import('@primo-ai/sdk').StreamEvent>;
|
|
68
|
+
/** Clear conversation state so the next run/stream starts a fresh session. */
|
|
69
|
+
reset(): void;
|
|
70
70
|
/** Clear the cached model so the next run re-resolves from the factory. */
|
|
71
71
|
invalidateModel(): void;
|
|
72
72
|
/** Auto-invalidate cached model when the error indicates auth failure or model-not-found. */
|
|
73
73
|
private autoInvalidateModel;
|
|
74
74
|
private getLLM;
|
|
75
|
+
private buildContext;
|
|
75
76
|
private createContext;
|
|
76
77
|
private registerTools;
|
|
77
78
|
private registerBuiltinProcessors;
|
|
78
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Create an Agent from a simple config. Convenience wrapper for single-agent usage.
|
|
82
|
+
* For advanced usage (Dynamic config, providerOptions, custom dependencies), use `new Agent(config, deps)`.
|
|
83
|
+
*/
|
|
84
|
+
export declare function createAgent(config: AgentSimpleConfig): Agent;
|
package/dist/agent.js
CHANGED
|
@@ -23,6 +23,7 @@ export class Agent {
|
|
|
23
23
|
sessionManager;
|
|
24
24
|
contextBuilder;
|
|
25
25
|
activeAbortController = null;
|
|
26
|
+
lastContext;
|
|
26
27
|
constructor(config, deps) {
|
|
27
28
|
this.config = config;
|
|
28
29
|
this.modelFactory = deps?.modelFactory ?? new ModelFactory();
|
|
@@ -72,6 +73,18 @@ export class Agent {
|
|
|
72
73
|
get state() {
|
|
73
74
|
return this.orchestrator.state;
|
|
74
75
|
}
|
|
76
|
+
/** Subscribe to an event type. Returns an unsubscribe function. */
|
|
77
|
+
on(eventType, handler) {
|
|
78
|
+
return this.eventBus.subscribe(eventType, handler);
|
|
79
|
+
}
|
|
80
|
+
/** Subscribe to an event type for at most one emission. */
|
|
81
|
+
once(eventType, handler) {
|
|
82
|
+
this.eventBus.once(eventType, handler);
|
|
83
|
+
}
|
|
84
|
+
/** Remove a specific handler for an event type. */
|
|
85
|
+
off(eventType, handler) {
|
|
86
|
+
this.eventBus.unsubscribe(eventType, handler);
|
|
87
|
+
}
|
|
75
88
|
get _contextBuilder() {
|
|
76
89
|
return this.contextBuilder;
|
|
77
90
|
}
|
|
@@ -79,7 +92,7 @@ export class Agent {
|
|
|
79
92
|
if (signal?.aborted)
|
|
80
93
|
throw new DOMException('Agent run aborted', 'AbortError');
|
|
81
94
|
this._pluginManager.freezeHarnessInstances();
|
|
82
|
-
const context = await this.
|
|
95
|
+
const context = await this.buildContext(input);
|
|
83
96
|
const hm = this._pluginManager.hookManager;
|
|
84
97
|
// agent.start hook
|
|
85
98
|
await hm.invoke('agent.start', { sessionId: context.request.sessionId, request: context.request, agentConfig: this.config }, {});
|
|
@@ -97,6 +110,7 @@ export class Agent {
|
|
|
97
110
|
sessionId: context.request.sessionId,
|
|
98
111
|
autoCheckpoint: this._autoCheckpoint,
|
|
99
112
|
});
|
|
113
|
+
this.lastContext = finalCtx;
|
|
100
114
|
return {
|
|
101
115
|
response: finalCtx.iteration.response ?? '',
|
|
102
116
|
tokenUsage: finalCtx.session.totalTokenUsage ?? { input: 0, output: 0 },
|
|
@@ -151,7 +165,7 @@ export class Agent {
|
|
|
151
165
|
async *stream(input, signal) {
|
|
152
166
|
if (signal?.aborted)
|
|
153
167
|
throw new DOMException('Agent stream aborted', 'AbortError');
|
|
154
|
-
const context = await this.
|
|
168
|
+
const context = await this.buildContext(input);
|
|
155
169
|
const hm = this._pluginManager.hookManager;
|
|
156
170
|
// agent.start hook
|
|
157
171
|
await hm.invoke('agent.start', { sessionId: context.request.sessionId, request: context.request, agentConfig: this.config }, {});
|
|
@@ -162,13 +176,22 @@ export class Agent {
|
|
|
162
176
|
signal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
163
177
|
}
|
|
164
178
|
try {
|
|
165
|
-
|
|
179
|
+
let finalCtx;
|
|
180
|
+
for await (const event of this.orchestrator.streamEvents(context, {
|
|
166
181
|
maxIterations: maxIter,
|
|
167
182
|
signal: controller.signal,
|
|
168
183
|
modelString: this.config.model,
|
|
169
184
|
sessionId: context.request.sessionId,
|
|
170
185
|
autoCheckpoint: this._autoCheckpoint,
|
|
171
|
-
})
|
|
186
|
+
})) {
|
|
187
|
+
if (event.type === 'text_delta')
|
|
188
|
+
yield event.text;
|
|
189
|
+
if (event.type === 'suspended')
|
|
190
|
+
yield ` [suspended: ${event.reason}]`;
|
|
191
|
+
if (event.type === 'complete')
|
|
192
|
+
finalCtx = event.context;
|
|
193
|
+
}
|
|
194
|
+
this.lastContext = finalCtx;
|
|
172
195
|
}
|
|
173
196
|
finally {
|
|
174
197
|
this.activeAbortController = null;
|
|
@@ -181,7 +204,7 @@ export class Agent {
|
|
|
181
204
|
async *streamEvents(input, signal) {
|
|
182
205
|
if (signal?.aborted)
|
|
183
206
|
throw new DOMException('Agent stream aborted', 'AbortError');
|
|
184
|
-
const context = await this.
|
|
207
|
+
const context = await this.buildContext(input);
|
|
185
208
|
const hm = this._pluginManager.hookManager;
|
|
186
209
|
await hm.invoke('agent.start', { sessionId: context.request.sessionId, request: context.request, agentConfig: this.config }, {});
|
|
187
210
|
const maxIter = typeof this.config.maxIterations === 'number' ? this.config.maxIterations : 10;
|
|
@@ -191,13 +214,19 @@ export class Agent {
|
|
|
191
214
|
signal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
192
215
|
}
|
|
193
216
|
try {
|
|
194
|
-
|
|
217
|
+
let finalCtx;
|
|
218
|
+
for await (const event of this.orchestrator.streamEvents(context, {
|
|
195
219
|
maxIterations: maxIter,
|
|
196
220
|
signal: controller.signal,
|
|
197
221
|
modelString: this.config.model,
|
|
198
222
|
sessionId: context.request.sessionId,
|
|
199
223
|
autoCheckpoint: this._autoCheckpoint,
|
|
200
|
-
})
|
|
224
|
+
})) {
|
|
225
|
+
if (event.type === 'complete')
|
|
226
|
+
finalCtx = event.context;
|
|
227
|
+
yield event;
|
|
228
|
+
}
|
|
229
|
+
this.lastContext = finalCtx;
|
|
201
230
|
}
|
|
202
231
|
finally {
|
|
203
232
|
this.activeAbortController = null;
|
|
@@ -215,103 +244,9 @@ export class Agent {
|
|
|
215
244
|
this.activeAbortController?.abort();
|
|
216
245
|
this.activeAbortController = null;
|
|
217
246
|
}
|
|
218
|
-
/**
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
*/
|
|
222
|
-
async continue(sessionId, message, signal) {
|
|
223
|
-
if (signal?.aborted)
|
|
224
|
-
throw new DOMException('Agent continue aborted', 'AbortError');
|
|
225
|
-
if (!this.sessionManager)
|
|
226
|
-
throw new Error('Session manager is required for continue()');
|
|
227
|
-
const context = await this.sessionManager.restore(sessionId);
|
|
228
|
-
context.request.input = message;
|
|
229
|
-
if (context.session.messageHistory) {
|
|
230
|
-
context.session.messageHistory.push({ role: 'user', content: message });
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
context.session.messageHistory = [{ role: 'user', content: message }];
|
|
234
|
-
}
|
|
235
|
-
context.iteration = { step: 0 };
|
|
236
|
-
this._pluginManager.freezeHarnessInstances();
|
|
237
|
-
const hm = this._pluginManager.hookManager;
|
|
238
|
-
await hm.invoke('agent.start', { sessionId: context.request.sessionId, request: context.request, agentConfig: this.config }, {});
|
|
239
|
-
const maxIter = typeof this.config.maxIterations === 'number' ? this.config.maxIterations : 10;
|
|
240
|
-
const controller = new AbortController();
|
|
241
|
-
this.activeAbortController = controller;
|
|
242
|
-
if (signal) {
|
|
243
|
-
signal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
244
|
-
}
|
|
245
|
-
try {
|
|
246
|
-
const { context: finalCtx, compatRetries } = await this.orchestrator.runLoop(context, {
|
|
247
|
-
maxIterations: maxIter,
|
|
248
|
-
signal: controller.signal,
|
|
249
|
-
modelString: this.config.model,
|
|
250
|
-
sessionId: context.request.sessionId,
|
|
251
|
-
autoCheckpoint: this._autoCheckpoint,
|
|
252
|
-
});
|
|
253
|
-
return {
|
|
254
|
-
response: finalCtx.iteration.response ?? '',
|
|
255
|
-
tokenUsage: finalCtx.session.totalTokenUsage ?? { input: 0, output: 0 },
|
|
256
|
-
sessionId: context.request.sessionId,
|
|
257
|
-
compatRetries,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
catch (error) {
|
|
261
|
-
this.autoInvalidateModel(error);
|
|
262
|
-
throw error;
|
|
263
|
-
}
|
|
264
|
-
finally {
|
|
265
|
-
this.activeAbortController = null;
|
|
266
|
-
try {
|
|
267
|
-
await hm.invoke('agent.end', { sessionId: context.request.sessionId }, {});
|
|
268
|
-
}
|
|
269
|
-
catch { /* hook error must not mask original */ }
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Continue an existing session with streaming. Yields StreamEvents for the
|
|
274
|
-
* continued conversation.
|
|
275
|
-
*/
|
|
276
|
-
async *continueStream(sessionId, message, signal) {
|
|
277
|
-
if (signal?.aborted)
|
|
278
|
-
throw new DOMException('Agent continue stream aborted', 'AbortError');
|
|
279
|
-
if (!this.sessionManager)
|
|
280
|
-
throw new Error('Session manager is required for continueStream()');
|
|
281
|
-
const context = await this.sessionManager.restore(sessionId);
|
|
282
|
-
context.request.input = message;
|
|
283
|
-
if (context.session.messageHistory) {
|
|
284
|
-
context.session.messageHistory.push({ role: 'user', content: message });
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
context.session.messageHistory = [{ role: 'user', content: message }];
|
|
288
|
-
}
|
|
289
|
-
context.iteration = { step: 0 };
|
|
290
|
-
this._pluginManager.freezeHarnessInstances();
|
|
291
|
-
const hm = this._pluginManager.hookManager;
|
|
292
|
-
await hm.invoke('agent.start', { sessionId: context.request.sessionId, request: context.request, agentConfig: this.config }, {});
|
|
293
|
-
const maxIter = typeof this.config.maxIterations === 'number' ? this.config.maxIterations : 10;
|
|
294
|
-
const controller = new AbortController();
|
|
295
|
-
this.activeAbortController = controller;
|
|
296
|
-
if (signal) {
|
|
297
|
-
signal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
298
|
-
}
|
|
299
|
-
try {
|
|
300
|
-
yield* this.orchestrator.streamEvents(context, {
|
|
301
|
-
maxIterations: maxIter,
|
|
302
|
-
signal: controller.signal,
|
|
303
|
-
modelString: this.config.model,
|
|
304
|
-
sessionId: context.request.sessionId,
|
|
305
|
-
autoCheckpoint: this._autoCheckpoint,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
finally {
|
|
309
|
-
this.activeAbortController = null;
|
|
310
|
-
try {
|
|
311
|
-
await hm.invoke('agent.end', { sessionId: context.request.sessionId }, {});
|
|
312
|
-
}
|
|
313
|
-
catch { /* hook error must not mask original */ }
|
|
314
|
-
}
|
|
247
|
+
/** Clear conversation state so the next run/stream starts a fresh session. */
|
|
248
|
+
reset() {
|
|
249
|
+
this.lastContext = undefined;
|
|
315
250
|
}
|
|
316
251
|
/** Clear the cached model so the next run re-resolves from the factory. */
|
|
317
252
|
invalidateModel() {
|
|
@@ -334,6 +269,24 @@ export class Agent {
|
|
|
334
269
|
tracer: this._tracer,
|
|
335
270
|
});
|
|
336
271
|
}
|
|
272
|
+
async buildContext(input) {
|
|
273
|
+
if (this.lastContext) {
|
|
274
|
+
return {
|
|
275
|
+
request: { input, sessionId: this.lastContext.request.sessionId },
|
|
276
|
+
agent: { config: { ...this.config }, promptFragments: [], toolDeclarations: [] },
|
|
277
|
+
iteration: { step: 0 },
|
|
278
|
+
session: {
|
|
279
|
+
messageHistory: [
|
|
280
|
+
...(this.lastContext.session.messageHistory ?? []),
|
|
281
|
+
{ role: 'user', content: input },
|
|
282
|
+
],
|
|
283
|
+
totalTokenUsage: this.lastContext.session.totalTokenUsage,
|
|
284
|
+
custom: { ...this.lastContext.session.custom },
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return this.createContext(input);
|
|
289
|
+
}
|
|
337
290
|
async createContext(input) {
|
|
338
291
|
let sessionId = crypto.randomUUID();
|
|
339
292
|
if (this.sessionManager) {
|
|
@@ -373,6 +326,13 @@ export class Agent {
|
|
|
373
326
|
this.runner.register(processOutputProcessor);
|
|
374
327
|
}
|
|
375
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Create an Agent from a simple config. Convenience wrapper for single-agent usage.
|
|
331
|
+
* For advanced usage (Dynamic config, providerOptions, custom dependencies), use `new Agent(config, deps)`.
|
|
332
|
+
*/
|
|
333
|
+
export function createAgent(config) {
|
|
334
|
+
return new Agent(config);
|
|
335
|
+
}
|
|
376
336
|
function isAuthOrNotFoundError(error) {
|
|
377
337
|
if (error instanceof AuthError || error instanceof ModelNotFoundError)
|
|
378
338
|
return true;
|
package/dist/event-bus.d.ts
CHANGED
|
@@ -4,5 +4,10 @@ export declare class EventBus {
|
|
|
4
4
|
constructor(onError?: ((error: unknown, eventType: string) => void) | undefined);
|
|
5
5
|
emit(eventType: string, data?: unknown): void;
|
|
6
6
|
subscribe(eventType: string, handler: (data?: unknown) => void): () => void;
|
|
7
|
-
|
|
7
|
+
/** Remove a specific handler for an event type. */
|
|
8
|
+
unsubscribe(eventType: string, handler: (data?: unknown) => void): void;
|
|
9
|
+
/** Register a handler that fires at most once, then auto-unsubscribes. */
|
|
10
|
+
once(eventType: string, handler: (data?: unknown) => void): void;
|
|
11
|
+
/** Promise-based once: returns a Promise that resolves on the next emission. */
|
|
12
|
+
oncePromise(eventType: string): Promise<unknown>;
|
|
8
13
|
}
|
package/dist/event-bus.js
CHANGED
|
@@ -32,7 +32,23 @@ export class EventBus {
|
|
|
32
32
|
set.add(handler);
|
|
33
33
|
return () => set.delete(handler);
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
/** Remove a specific handler for an event type. */
|
|
36
|
+
unsubscribe(eventType, handler) {
|
|
37
|
+
const set = this.handlers.get(eventType);
|
|
38
|
+
if (set) {
|
|
39
|
+
set.delete(handler);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Register a handler that fires at most once, then auto-unsubscribes. */
|
|
43
|
+
once(eventType, handler) {
|
|
44
|
+
const wrapped = (data) => {
|
|
45
|
+
handler(data);
|
|
46
|
+
this.unsubscribe(eventType, wrapped);
|
|
47
|
+
};
|
|
48
|
+
this.subscribe(eventType, wrapped);
|
|
49
|
+
}
|
|
50
|
+
/** Promise-based once: returns a Promise that resolves on the next emission. */
|
|
51
|
+
oncePromise(eventType) {
|
|
36
52
|
return new Promise((resolve) => {
|
|
37
53
|
const unsub = this.subscribe(eventType, (data) => {
|
|
38
54
|
unsub();
|
package/dist/hook-manager.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { PipelineRunner, type PipelineRunnerOptions } from './pipeline.js';
|
|
2
|
-
export { Agent, type AgentDependencies, type AgentRunResult } from './agent.js';
|
|
2
|
+
export { Agent, createAgent, type AgentDependencies, type AgentRunResult } from './agent.js';
|
|
3
3
|
export { LLMInvoker, type LLMInvokerOptions, type LLMInvokeInput, type LLMInvokeResult, type LLMStreamHandle } from './llm-invoker.js';
|
|
4
4
|
export { resolveModel, registerProvider, parseModel, type ParsedModel } from './model-resolver.js';
|
|
5
5
|
export { streamWithRetry, type RetryOptions } from './retry.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @primo-ai/core — Agent Loop, Processor Pipeline, Context, Tool Registry
|
|
2
2
|
export { PipelineRunner } from './pipeline.js';
|
|
3
|
-
export { Agent } from './agent.js';
|
|
3
|
+
export { Agent, createAgent } from './agent.js';
|
|
4
4
|
export { LLMInvoker } from './llm-invoker.js';
|
|
5
5
|
export { resolveModel, registerProvider, parseModel } from './model-resolver.js';
|
|
6
6
|
export { streamWithRetry } from './retry.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primo-ai/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"ai": "^6.0.177",
|
|
22
22
|
"js-tiktoken": "^1.0.21",
|
|
23
23
|
"zod": "^4.4.3",
|
|
24
|
-
"@primo-ai/
|
|
25
|
-
"@primo-ai/
|
|
26
|
-
"@primo-ai/
|
|
24
|
+
"@primo-ai/observability": "0.1.5",
|
|
25
|
+
"@primo-ai/tools": "0.1.5",
|
|
26
|
+
"@primo-ai/sdk": "0.1.5"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/better-sqlite3": "^7.6.13",
|