@modelprofile.com/flexharness 1.0.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.
@@ -0,0 +1,195 @@
1
+ import * as plugins from './plugins.js';
2
+ import {
3
+ FlexHarnessStoreConflictError,
4
+ FlexHarnessStoreFormatError,
5
+ FlexHarnessValidationError,
6
+ } from './errors.js';
7
+ import type { IFlexHarnessSnapshot, IFlexHarnessStore } from './interfaces.js';
8
+ import { assertFlexHarnessSnapshot, cloneSerializable } from './utils.json.js';
9
+
10
+ function validateSave(
11
+ snapshot: IFlexHarnessSnapshot,
12
+ expectedRevision: number,
13
+ ): IFlexHarnessSnapshot {
14
+ assertFlexHarnessSnapshot(snapshot);
15
+ if (!Number.isSafeInteger(expectedRevision) || expectedRevision < 0) {
16
+ throw new FlexHarnessValidationError('expectedRevision must be a non-negative integer.');
17
+ }
18
+ if (snapshot.revision !== expectedRevision + 1) {
19
+ throw new FlexHarnessStoreFormatError(
20
+ `Snapshot revision ${snapshot.revision} must equal expected revision ${expectedRevision} plus one.`,
21
+ );
22
+ }
23
+ return cloneSerializable(snapshot);
24
+ }
25
+
26
+ export class InMemoryFlexHarnessStore implements IFlexHarnessStore {
27
+ private readonly snapshots = new Map<string, IFlexHarnessSnapshot>();
28
+
29
+ public async load(storageKey: string): Promise<IFlexHarnessSnapshot | undefined> {
30
+ const snapshot = this.snapshots.get(storageKey);
31
+ return snapshot ? cloneSerializable(snapshot) : undefined;
32
+ }
33
+
34
+ public async save(
35
+ storageKey: string,
36
+ snapshot: IFlexHarnessSnapshot,
37
+ expectedRevision: number,
38
+ ): Promise<void> {
39
+ const validated = validateSave(snapshot, expectedRevision);
40
+ const actualRevision = this.snapshots.get(storageKey)?.revision ?? 0;
41
+ if (actualRevision !== expectedRevision) {
42
+ throw new FlexHarnessStoreConflictError(storageKey, expectedRevision, actualRevision);
43
+ }
44
+ this.snapshots.set(storageKey, validated);
45
+ }
46
+ }
47
+
48
+ export interface IJsonFileFlexHarnessStoreOptions {
49
+ directory: string;
50
+ }
51
+
52
+ /**
53
+ * Atomic and CAS-safe across instances in this process. It intentionally does not
54
+ * claim cross-process safety because no operating-system lock is held.
55
+ */
56
+ export class JsonFileFlexHarnessStore implements IFlexHarnessStore {
57
+ private static readonly fileQueues = new Map<string, Promise<void>>();
58
+ private readonly directory: string;
59
+
60
+ constructor(options: IJsonFileFlexHarnessStoreOptions) {
61
+ if (!options.directory) {
62
+ throw new FlexHarnessValidationError('JsonFileFlexHarnessStore requires a directory.');
63
+ }
64
+ this.directory = plugins.path.resolve(options.directory);
65
+ }
66
+
67
+ public async load(storageKey: string): Promise<IFlexHarnessSnapshot | undefined> {
68
+ const filePath = this.filePath(storageKey);
69
+ return JsonFileFlexHarnessStore.inFileQueue(filePath, async () => {
70
+ await this.preparePath(filePath);
71
+ return this.readSnapshot(filePath);
72
+ });
73
+ }
74
+
75
+ public async save(
76
+ storageKey: string,
77
+ snapshot: IFlexHarnessSnapshot,
78
+ expectedRevision: number,
79
+ ): Promise<void> {
80
+ const validated = validateSave(snapshot, expectedRevision);
81
+ const filePath = this.filePath(storageKey);
82
+ await JsonFileFlexHarnessStore.inFileQueue(filePath, async () => {
83
+ await this.preparePath(filePath);
84
+ const current = await this.readSnapshot(filePath);
85
+ const actualRevision = current?.revision ?? 0;
86
+ if (actualRevision !== expectedRevision) {
87
+ throw new FlexHarnessStoreConflictError(storageKey, expectedRevision, actualRevision);
88
+ }
89
+ await this.writeSnapshot(filePath, validated);
90
+ });
91
+ }
92
+
93
+ private static inFileQueue<T>(filePath: string, operation: () => Promise<T>): Promise<T> {
94
+ const previous = JsonFileFlexHarnessStore.fileQueues.get(filePath) ?? Promise.resolve();
95
+ const result = previous.catch(() => undefined).then(operation);
96
+ const barrier = result.then(
97
+ () => undefined,
98
+ () => undefined,
99
+ );
100
+ JsonFileFlexHarnessStore.fileQueues.set(filePath, barrier);
101
+ void barrier.then(() => {
102
+ if (JsonFileFlexHarnessStore.fileQueues.get(filePath) === barrier) {
103
+ JsonFileFlexHarnessStore.fileQueues.delete(filePath);
104
+ }
105
+ });
106
+ return result;
107
+ }
108
+
109
+ private filePath(storageKey: string): string {
110
+ const digest = plugins.crypto.createHash('sha256').update(storageKey).digest('hex');
111
+ return plugins.path.join(this.directory, `${digest}.json`);
112
+ }
113
+
114
+ private async preparePath(filePath: string): Promise<void> {
115
+ await plugins.fs.mkdir(this.directory, { recursive: true, mode: 0o700 });
116
+ await plugins.fs.chmod(this.directory, 0o700);
117
+ await this.cleanupTemps(filePath);
118
+ }
119
+
120
+ private async cleanupTemps(filePath: string): Promise<void> {
121
+ const prefix = `${plugins.path.basename(filePath)}.`;
122
+ const entries = await plugins.fs.readdir(this.directory, { withFileTypes: true });
123
+ await Promise.all(
124
+ entries
125
+ .filter((entry) => entry.isFile() && entry.name.startsWith(prefix) && entry.name.endsWith('.tmp'))
126
+ .map(async (entry) => {
127
+ try {
128
+ await plugins.fs.unlink(plugins.path.join(this.directory, entry.name));
129
+ } catch (error) {
130
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
131
+ throw error;
132
+ }
133
+ }
134
+ }),
135
+ );
136
+ }
137
+
138
+ private async readSnapshot(filePath: string): Promise<IFlexHarnessSnapshot | undefined> {
139
+ let serialized: string;
140
+ try {
141
+ await plugins.fs.chmod(filePath, 0o600);
142
+ serialized = await plugins.fs.readFile(filePath, 'utf8');
143
+ } catch (error) {
144
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
145
+ return undefined;
146
+ }
147
+ throw error;
148
+ }
149
+
150
+ let parsed: unknown;
151
+ try {
152
+ parsed = JSON.parse(serialized);
153
+ } catch (error) {
154
+ throw new FlexHarnessStoreFormatError(`Malformed JSON snapshot at ${filePath}.`, {
155
+ cause: error,
156
+ });
157
+ }
158
+ try {
159
+ assertFlexHarnessSnapshot(parsed);
160
+ } catch (error) {
161
+ if (error instanceof FlexHarnessStoreFormatError) {
162
+ throw new FlexHarnessStoreFormatError(`Invalid snapshot at ${filePath}: ${error.message}`, {
163
+ cause: error,
164
+ });
165
+ }
166
+ throw error;
167
+ }
168
+ return cloneSerializable(parsed);
169
+ }
170
+
171
+ private async writeSnapshot(
172
+ filePath: string,
173
+ snapshot: IFlexHarnessSnapshot,
174
+ ): Promise<void> {
175
+ const tempPath = `${filePath}.${process.pid}.${plugins.crypto.randomUUID()}.tmp`;
176
+ let fileHandle: plugins.fs.FileHandle | undefined;
177
+ try {
178
+ fileHandle = await plugins.fs.open(tempPath, 'wx', 0o600);
179
+ await fileHandle.writeFile(`${JSON.stringify(snapshot, null, 2)}\n`, 'utf8');
180
+ await fileHandle.sync();
181
+ await fileHandle.close();
182
+ fileHandle = undefined;
183
+ await plugins.fs.chmod(tempPath, 0o600);
184
+ await plugins.fs.rename(tempPath, filePath);
185
+ await plugins.fs.chmod(filePath, 0o600);
186
+ } finally {
187
+ await fileHandle?.close().catch(() => undefined);
188
+ await plugins.fs.unlink(tempPath).catch((error: NodeJS.ErrnoException) => {
189
+ if (error.code !== 'ENOENT') {
190
+ throw error;
191
+ }
192
+ });
193
+ }
194
+ }
195
+ }
package/ts/errors.ts ADDED
@@ -0,0 +1,125 @@
1
+ import type { IFlexErrorInfo } from './interfaces.js';
2
+
3
+ export class FlexHarnessError extends Error {
4
+ public readonly code: string;
5
+
6
+ constructor(code: string, message: string, options?: ErrorOptions) {
7
+ super(message, options);
8
+ this.name = 'FlexHarnessError';
9
+ this.code = code;
10
+ }
11
+ }
12
+
13
+ export class FlexHarnessValidationError extends FlexHarnessError {
14
+ constructor(message: string, options?: ErrorOptions) {
15
+ super('FLEX_VALIDATION', message, options);
16
+ this.name = 'FlexHarnessValidationError';
17
+ }
18
+ }
19
+
20
+ export class FlexHarnessClosedError extends FlexHarnessError {
21
+ constructor() {
22
+ super('FLEX_CLOSED', 'FlexHarness is disposed.');
23
+ this.name = 'FlexHarnessClosedError';
24
+ }
25
+ }
26
+
27
+ export class FlexHarnessNotFoundError extends FlexHarnessError {
28
+ constructor(resource: string, id: string) {
29
+ super('FLEX_NOT_FOUND', `${resource} "${id}" was not found.`);
30
+ this.name = 'FlexHarnessNotFoundError';
31
+ }
32
+ }
33
+
34
+ export class FlexHarnessSessionBusyError extends FlexHarnessError {
35
+ constructor(sessionId: string, reason = 'already has an active run') {
36
+ super('FLEX_SESSION_BUSY', `Session "${sessionId}" ${reason}.`);
37
+ this.name = 'FlexHarnessSessionBusyError';
38
+ }
39
+ }
40
+
41
+ export class FlexHarnessAbortError extends FlexHarnessError {
42
+ constructor(message = 'The run was aborted.', options?: ErrorOptions) {
43
+ super('FLEX_ABORTED', message, options);
44
+ this.name = 'FlexHarnessAbortError';
45
+ }
46
+ }
47
+
48
+ export class FlexHarnessCallbackOverflowError extends FlexHarnessError {
49
+ constructor(message: string) {
50
+ super('FLEX_CALLBACK_OVERFLOW', message);
51
+ this.name = 'FlexHarnessCallbackOverflowError';
52
+ }
53
+ }
54
+
55
+ export class FlexHarnessPermissionRejectedError extends FlexHarnessError {
56
+ constructor(permissionId: string, options?: ErrorOptions) {
57
+ super('FLEX_PERMISSION_REJECTED', `Permission "${permissionId}" was rejected.`, options);
58
+ this.name = 'FlexHarnessPermissionRejectedError';
59
+ }
60
+ }
61
+
62
+ export class FlexHarnessPermissionStateError extends FlexHarnessError {
63
+ constructor(message: string) {
64
+ super('FLEX_PERMISSION_STATE', message);
65
+ this.name = 'FlexHarnessPermissionStateError';
66
+ }
67
+ }
68
+
69
+ export class FlexHarnessStoreConflictError extends FlexHarnessError {
70
+ public readonly storageKey: string;
71
+ public readonly expectedRevision: number;
72
+ public readonly actualRevision: number;
73
+
74
+ constructor(storageKey: string, expectedRevision: number, actualRevision: number) {
75
+ super(
76
+ 'FLEX_STORE_CONFLICT',
77
+ `Snapshot conflict for storage key "${storageKey}": expected revision ${expectedRevision}, found ${actualRevision}.`,
78
+ );
79
+ this.name = 'FlexHarnessStoreConflictError';
80
+ this.storageKey = storageKey;
81
+ this.expectedRevision = expectedRevision;
82
+ this.actualRevision = actualRevision;
83
+ }
84
+ }
85
+
86
+ export class FlexHarnessStoreFormatError extends FlexHarnessError {
87
+ constructor(message: string, options?: ErrorOptions) {
88
+ super('FLEX_STORE_FORMAT', message, options);
89
+ this.name = 'FlexHarnessStoreFormatError';
90
+ }
91
+ }
92
+
93
+ export class FlexHarnessRunError extends AggregateError {
94
+ public readonly code = 'FLEX_RUN_FAILED';
95
+
96
+ constructor(errors: unknown[]) {
97
+ super(errors, 'Multiple FlexHarness run or cleanup operations failed.');
98
+ this.name = 'FlexHarnessRunError';
99
+ }
100
+ }
101
+
102
+ export function errorToInfo(error: unknown): IFlexErrorInfo {
103
+ if (error instanceof FlexHarnessError) {
104
+ return {
105
+ name: error.name,
106
+ message: error.message,
107
+ code: error.code,
108
+ };
109
+ }
110
+ if (error instanceof Error) {
111
+ return {
112
+ name: error.name,
113
+ message: error.message,
114
+ ...('code' in error && typeof error.code === 'string' ? { code: error.code } : {}),
115
+ };
116
+ }
117
+ return {
118
+ name: 'Error',
119
+ message: String(error),
120
+ };
121
+ }
122
+
123
+ export function errorMessage(error: unknown): string {
124
+ return error instanceof Error ? error.message : String(error);
125
+ }
package/ts/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from './classes.flexharness.js';
2
+ export * from './classes.stores.js';
3
+ export * from './errors.js';
4
+ export * from './interfaces.js';
5
+ export { normalizeJsonValue } from './utils.json.js';
@@ -0,0 +1,369 @@
1
+ import type * as plugins from './plugins.js';
2
+
3
+ export type TJsonPrimitive = string | number | boolean | null;
4
+
5
+ export interface IJsonObject {
6
+ [key: string]: TJsonValue;
7
+ }
8
+
9
+ export type TJsonValue = TJsonPrimitive | IJsonObject | TJsonValue[];
10
+
11
+ export type TFlexAgentRunOptions = plugins.IAgentRunOptions;
12
+ export type TFlexAgentRunResult = plugins.IAgentRunResult;
13
+ export type TFlexAgentRunner = (options: TFlexAgentRunOptions) => Promise<TFlexAgentRunResult>;
14
+ export type TFlexAgentModel = TFlexAgentRunOptions['model'];
15
+ export type TFlexAgentPrompt = TFlexAgentRunOptions['prompt'];
16
+ export type TFlexAgentModelMessage = NonNullable<TFlexAgentRunOptions['messages']>[number];
17
+ export type TFlexAgentToolSet = NonNullable<TFlexAgentRunOptions['tools']>;
18
+ export type TFlexAgentProviderOptions = TFlexAgentRunOptions['providerOptions'];
19
+ export type TFlexAgentCacheSetting = TFlexAgentRunOptions['cache'];
20
+ export type TFlexAgentToolCallFinishEvent = Parameters<
21
+ NonNullable<TFlexAgentRunOptions['onToolCallFinish']>
22
+ >[0];
23
+
24
+ export interface IFlexTextPromptPart {
25
+ type: 'text';
26
+ text: string;
27
+ }
28
+
29
+ export interface IFlexImagePromptPart {
30
+ type: 'image';
31
+ data: string;
32
+ mediaType?: string;
33
+ name?: string;
34
+ }
35
+
36
+ export interface IFlexFilePromptPart {
37
+ type: 'file';
38
+ data: string;
39
+ mediaType: string;
40
+ name?: string;
41
+ }
42
+
43
+ export type TFlexPromptPart = IFlexTextPromptPart | IFlexImagePromptPart | IFlexFilePromptPart;
44
+ export type TFlexPrompt = string | TFlexPromptPart[];
45
+
46
+ export interface IFlexUsage {
47
+ inputTokens: number;
48
+ outputTokens: number;
49
+ totalTokens: number;
50
+ cacheReadTokens: number;
51
+ cacheWriteTokens: number;
52
+ }
53
+
54
+ export interface IFlexModelIdentity {
55
+ provider: string;
56
+ model: string;
57
+ displayName?: string;
58
+ }
59
+
60
+ export type TFlexActivityStatus =
61
+ | 'idle'
62
+ | 'running'
63
+ | 'waiting_permission'
64
+ | 'failed'
65
+ | 'cancelled';
66
+ export type TFlexMessageStatus = 'streaming' | 'completed' | 'failed' | 'cancelled';
67
+ export type TFlexToolPartStatus = 'running' | 'completed' | 'failed' | 'cancelled';
68
+ export type TFlexAttachmentSource = 'inline-base64' | 'data-url' | 'remote-url';
69
+
70
+ export interface IFlexTextMessagePart {
71
+ partId: string;
72
+ type: 'text';
73
+ text: string;
74
+ }
75
+
76
+ export interface IFlexReasoningMessagePart {
77
+ partId: string;
78
+ type: 'reasoning';
79
+ text: string;
80
+ status: 'running' | 'completed' | 'cancelled';
81
+ }
82
+
83
+ export interface IFlexToolMessagePart {
84
+ partId: string;
85
+ type: 'tool';
86
+ toolCallId: string;
87
+ toolName: string;
88
+ status: TFlexToolPartStatus;
89
+ input: TJsonValue;
90
+ output?: TJsonValue;
91
+ error?: string;
92
+ }
93
+
94
+ export interface IFlexAttachmentMessagePart {
95
+ partId: string;
96
+ type: 'attachment';
97
+ attachmentType: 'image' | 'file';
98
+ source: TFlexAttachmentSource;
99
+ sizeBytes?: number;
100
+ mediaType?: string;
101
+ name?: string;
102
+ }
103
+
104
+ export type TFlexMessagePart =
105
+ | IFlexTextMessagePart
106
+ | IFlexReasoningMessagePart
107
+ | IFlexToolMessagePart
108
+ | IFlexAttachmentMessagePart;
109
+
110
+ export interface IFlexMessage {
111
+ messageId: string;
112
+ sessionId: string;
113
+ runId: string;
114
+ role: 'user' | 'assistant';
115
+ status: TFlexMessageStatus;
116
+ createdAt: string;
117
+ completedAt?: string;
118
+ parts: TFlexMessagePart[];
119
+ model?: IFlexModelIdentity;
120
+ usage?: IFlexUsage;
121
+ error?: string;
122
+ }
123
+
124
+ export interface IFlexSessionActivity {
125
+ runId?: string;
126
+ status: TFlexActivityStatus;
127
+ startedAt?: string;
128
+ completedAt?: string;
129
+ error?: string;
130
+ }
131
+
132
+ export interface IFlexSession {
133
+ scopeId: string;
134
+ sessionId: string;
135
+ title?: string;
136
+ createdAt: string;
137
+ updatedAt: string;
138
+ archivedAt?: string;
139
+ status: TFlexActivityStatus;
140
+ activity: IFlexSessionActivity;
141
+ }
142
+
143
+ export interface IFlexCreateSessionOptions {
144
+ sessionId?: string;
145
+ title?: string;
146
+ }
147
+
148
+ export interface IFlexUpdateSessionOptions {
149
+ title?: string | null;
150
+ archived?: boolean;
151
+ }
152
+
153
+ export interface IFlexPromptOptions {
154
+ modelHint?: string;
155
+ system?: string;
156
+ maxSteps?: number;
157
+ }
158
+
159
+ export interface IFlexPromptResult {
160
+ runId: string;
161
+ sessionId: string;
162
+ userMessage: IFlexMessage;
163
+ assistantMessage: IFlexMessage;
164
+ model: IFlexModelIdentity;
165
+ usage: IFlexUsage;
166
+ finishReason: string;
167
+ steps: number;
168
+ }
169
+
170
+ export interface IFlexResolvedScope<TScope> {
171
+ storageKey: string;
172
+ scope: TScope;
173
+ }
174
+
175
+ export interface IFlexScopeResolver<TScope> {
176
+ resolveScope(scopeId: string): Promise<IFlexResolvedScope<TScope>> | IFlexResolvedScope<TScope>;
177
+ }
178
+
179
+ export interface IFlexModelResolverContext<TScope> {
180
+ scopeId: string;
181
+ scope: TScope;
182
+ sessionId: string;
183
+ runId: string;
184
+ modelHint?: string;
185
+ signal: AbortSignal;
186
+ }
187
+
188
+ export interface IFlexResolvedModel {
189
+ model: TFlexAgentModel;
190
+ identity: IFlexModelIdentity;
191
+ system?: string;
192
+ providerOptions?: TFlexAgentProviderOptions;
193
+ cache?: TFlexAgentCacheSetting;
194
+ maxSteps?: number;
195
+ }
196
+
197
+ export interface IFlexModelResolver<TScope> {
198
+ resolveModel(
199
+ context: IFlexModelResolverContext<TScope>,
200
+ ): Promise<IFlexResolvedModel> | IFlexResolvedModel;
201
+ }
202
+
203
+ export interface IFlexPermissionRequestInput {
204
+ kind: string;
205
+ description: string;
206
+ toolCallId?: string;
207
+ rememberKey?: string;
208
+ metadata?: TJsonValue;
209
+ }
210
+
211
+ export interface IFlexPermissionRequest extends IFlexPermissionRequestInput {
212
+ permissionId: string;
213
+ scopeId: string;
214
+ sessionId: string;
215
+ runId: string;
216
+ createdAt: string;
217
+ }
218
+
219
+ export type TFlexPermissionDecision = 'once' | 'always' | 'reject';
220
+
221
+ export interface IFlexToolProviderContext<TScope> {
222
+ scopeId: string;
223
+ scope: TScope;
224
+ sessionId: string;
225
+ runId: string;
226
+ signal: AbortSignal;
227
+ requestPermission(request: IFlexPermissionRequestInput): Promise<void>;
228
+ }
229
+
230
+ export interface IFlexToolHandle {
231
+ tools: TFlexAgentToolSet;
232
+ close?(): Promise<void> | void;
233
+ }
234
+
235
+ export interface IFlexToolProvider<TScope> {
236
+ provideTools(
237
+ context: IFlexToolProviderContext<TScope>,
238
+ ): Promise<IFlexToolHandle | undefined> | IFlexToolHandle | undefined;
239
+ }
240
+
241
+ export interface IFlexStoredSessionSnapshot {
242
+ session: IFlexSession;
243
+ messages: IFlexMessage[];
244
+ modelHistory: TJsonValue[];
245
+ rememberedPermissionKeys: string[];
246
+ }
247
+
248
+ export interface IFlexHarnessSnapshot {
249
+ schemaVersion: 1;
250
+ revision: number;
251
+ sessions: IFlexStoredSessionSnapshot[];
252
+ }
253
+
254
+ export interface IFlexHarnessStore {
255
+ load(storageKey: string): Promise<IFlexHarnessSnapshot | undefined>;
256
+ save(
257
+ storageKey: string,
258
+ snapshot: IFlexHarnessSnapshot,
259
+ expectedRevision: number,
260
+ ): Promise<void>;
261
+ }
262
+
263
+ export interface IFlexJsonLimits {
264
+ maxDepth?: number;
265
+ maxBytes?: number;
266
+ }
267
+
268
+ export interface IFlexCallbackLimits {
269
+ maxEvents?: number;
270
+ maxOutputBytes?: number;
271
+ maxParts?: number;
272
+ }
273
+
274
+ export interface IFlexHarnessOptions<TScope> {
275
+ scopeResolver: IFlexScopeResolver<TScope>;
276
+ modelResolver: IFlexModelResolver<TScope>;
277
+ toolProvider?: IFlexToolProvider<TScope>;
278
+ store?: IFlexHarnessStore;
279
+ runner?: TFlexAgentRunner;
280
+ toolOutputLimits?: IFlexJsonLimits;
281
+ callbackLimits?: IFlexCallbackLimits;
282
+ }
283
+
284
+ export interface IFlexEventBase {
285
+ readonly eventId: string;
286
+ readonly sequence: number;
287
+ readonly timestamp: string;
288
+ readonly scopeId: string;
289
+ readonly sessionId: string;
290
+ }
291
+
292
+ export interface IFlexSessionCreatedEvent extends IFlexEventBase {
293
+ readonly type: 'session.created';
294
+ readonly session: IFlexSession;
295
+ }
296
+
297
+ export interface IFlexSessionUpdatedEvent extends IFlexEventBase {
298
+ readonly type: 'session.updated';
299
+ readonly session: IFlexSession;
300
+ }
301
+
302
+ export interface IFlexSessionDeletedEvent extends IFlexEventBase {
303
+ readonly type: 'session.deleted';
304
+ readonly session: IFlexSession;
305
+ }
306
+
307
+ export interface IFlexRunStartedEvent extends IFlexEventBase {
308
+ readonly type: 'run.started';
309
+ readonly runId: string;
310
+ readonly messageId: string;
311
+ readonly session: IFlexSession;
312
+ }
313
+
314
+ export interface IFlexMessageChangedEvent extends IFlexEventBase {
315
+ readonly type: 'message.created' | 'message.updated';
316
+ readonly runId: string;
317
+ readonly messageId: string;
318
+ readonly message: IFlexMessage;
319
+ }
320
+
321
+ export interface IFlexPartChangedEvent extends IFlexEventBase {
322
+ readonly type: 'part.started' | 'part.delta' | 'part.completed';
323
+ readonly runId: string;
324
+ readonly messageId: string;
325
+ readonly partId: string;
326
+ readonly part: TFlexMessagePart;
327
+ readonly delta?: string;
328
+ }
329
+
330
+ export interface IFlexPermissionRequestedEvent extends IFlexEventBase {
331
+ readonly type: 'permission.requested';
332
+ readonly runId: string;
333
+ readonly request: IFlexPermissionRequest;
334
+ }
335
+
336
+ export interface IFlexPermissionResolvedEvent extends IFlexEventBase {
337
+ readonly type: 'permission.resolved';
338
+ readonly runId: string;
339
+ readonly request: IFlexPermissionRequest;
340
+ readonly decision: TFlexPermissionDecision;
341
+ }
342
+
343
+ export interface IFlexRunFinishedEvent extends IFlexEventBase {
344
+ readonly type: 'run.finished';
345
+ readonly runId: string;
346
+ readonly messageId: string;
347
+ readonly status: 'completed' | 'failed' | 'cancelled';
348
+ readonly message: IFlexMessage;
349
+ readonly error?: IFlexErrorInfo;
350
+ }
351
+
352
+ export interface IFlexErrorInfo {
353
+ name: string;
354
+ message: string;
355
+ code?: string;
356
+ }
357
+
358
+ export type TFlexHarnessEvent =
359
+ | IFlexSessionCreatedEvent
360
+ | IFlexSessionUpdatedEvent
361
+ | IFlexSessionDeletedEvent
362
+ | IFlexRunStartedEvent
363
+ | IFlexMessageChangedEvent
364
+ | IFlexPartChangedEvent
365
+ | IFlexPermissionRequestedEvent
366
+ | IFlexPermissionResolvedEvent
367
+ | IFlexRunFinishedEvent;
368
+
369
+ export type TFlexHarnessEventListener = (event: TFlexHarnessEvent) => void;
package/ts/plugins.ts ADDED
@@ -0,0 +1,15 @@
1
+ // node native scope
2
+ import * as crypto from 'node:crypto';
3
+ import * as fs from 'node:fs/promises';
4
+ import * as path from 'node:path';
5
+
6
+ export { crypto, fs, path };
7
+
8
+ // @push.rocks scope
9
+ import { runAgent } from '@push.rocks/smartagent';
10
+
11
+ export { runAgent };
12
+ export type {
13
+ IAgentRunOptions,
14
+ IAgentRunResult,
15
+ } from '@push.rocks/smartagent';