@modelprofile.com/flexharness 3.0.1 → 3.2.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/changelog.md +18 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.flexharness.d.ts +24 -2
- package/dist_ts/classes.flexharness.js +447 -78
- package/dist_ts/errors.d.ts +3 -0
- package/dist_ts/errors.js +7 -1
- package/dist_ts/interfaces.d.ts +38 -3
- package/package.json +1 -1
- package/readme.hints.md +8 -0
- package/readme.md +33 -7
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.flexharness.ts +552 -76
- package/ts/errors.ts +7 -0
- package/ts/interfaces.ts +51 -3
package/dist_ts/errors.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ export declare class FlexHarnessNotFoundError extends FlexHarnessError {
|
|
|
15
15
|
export declare class FlexHarnessSessionBusyError extends FlexHarnessError {
|
|
16
16
|
constructor(sessionId: string, reason?: string);
|
|
17
17
|
}
|
|
18
|
+
export declare class FlexHarnessQueueFullError extends FlexHarnessError {
|
|
19
|
+
constructor(message: string);
|
|
20
|
+
}
|
|
18
21
|
export declare class FlexHarnessAbortError extends FlexHarnessError {
|
|
19
22
|
constructor(message?: string, options?: ErrorOptions);
|
|
20
23
|
}
|
package/dist_ts/errors.js
CHANGED
|
@@ -30,6 +30,12 @@ export class FlexHarnessSessionBusyError extends FlexHarnessError {
|
|
|
30
30
|
this.name = 'FlexHarnessSessionBusyError';
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
export class FlexHarnessQueueFullError extends FlexHarnessError {
|
|
34
|
+
constructor(message) {
|
|
35
|
+
super('FLEX_QUEUE_FULL', message);
|
|
36
|
+
this.name = 'FlexHarnessQueueFullError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
33
39
|
export class FlexHarnessAbortError extends FlexHarnessError {
|
|
34
40
|
constructor(message = 'The run was aborted.', options) {
|
|
35
41
|
super('FLEX_ABORTED', message, options);
|
|
@@ -118,4 +124,4 @@ export function errorToInfo(error) {
|
|
|
118
124
|
export function errorMessage(error) {
|
|
119
125
|
return error instanceof Error ? error.message : String(error);
|
|
120
126
|
}
|
|
121
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
127
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvZXJyb3JzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE1BQU0sT0FBTyxnQkFBaUIsU0FBUSxLQUFLO0lBQ3pCLElBQUksQ0FBUztJQUU3QixZQUFZLElBQVksRUFBRSxPQUFlLEVBQUUsT0FBc0I7UUFDL0QsS0FBSyxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQztRQUN4QixJQUFJLENBQUMsSUFBSSxHQUFHLGtCQUFrQixDQUFDO1FBQy9CLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO0lBQ25CLENBQUM7Q0FDRjtBQUVELE1BQU0sT0FBTywwQkFBMkIsU0FBUSxnQkFBZ0I7SUFDOUQsWUFBWSxPQUFlLEVBQUUsT0FBc0I7UUFDakQsS0FBSyxDQUFDLGlCQUFpQixFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQztRQUMzQyxJQUFJLENBQUMsSUFBSSxHQUFHLDRCQUE0QixDQUFDO0lBQzNDLENBQUM7Q0FDRjtBQUVELE1BQU0sT0FBTyxzQkFBdUIsU0FBUSxnQkFBZ0I7SUFDMUQ7UUFDRSxLQUFLLENBQUMsYUFBYSxFQUFFLDBCQUEwQixDQUFDLENBQUM7UUFDakQsSUFBSSxDQUFDLElBQUksR0FBRyx3QkFBd0IsQ0FBQztJQUN2QyxDQUFDO0NBQ0Y7QUFFRCxNQUFNLE9BQU8sd0JBQXlCLFNBQVEsZ0JBQWdCO0lBQzVELFlBQVksUUFBZ0IsRUFBRSxFQUFVO1FBQ3RDLEtBQUssQ0FBQyxnQkFBZ0IsRUFBRSxHQUFHLFFBQVEsS0FBSyxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDOUQsSUFBSSxDQUFDLElBQUksR0FBRywwQkFBMEIsQ0FBQztJQUN6QyxDQUFDO0NBQ0Y7QUFFRCxNQUFNLE9BQU8sMkJBQTRCLFNBQVEsZ0JBQWdCO0lBQy9ELFlBQVksU0FBaUIsRUFBRSxNQUFNLEdBQUcsMkJBQTJCO1FBQ2pFLEtBQUssQ0FBQyxtQkFBbUIsRUFB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
|
package/dist_ts/interfaces.d.ts
CHANGED
|
@@ -122,6 +122,20 @@ export interface IFlexPromptOptions {
|
|
|
122
122
|
system?: string;
|
|
123
123
|
maxSteps?: number;
|
|
124
124
|
}
|
|
125
|
+
export type TFlexPromptQueueStatus = 'queued' | 'starting' | 'scheduled' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
126
|
+
export interface IFlexPromptQueueEntry {
|
|
127
|
+
queueId: string;
|
|
128
|
+
queueSequence: number;
|
|
129
|
+
scopeId: string;
|
|
130
|
+
sessionId: string;
|
|
131
|
+
status: TFlexPromptQueueStatus;
|
|
132
|
+
queuedAt: string;
|
|
133
|
+
runId?: string;
|
|
134
|
+
scheduleKey?: string;
|
|
135
|
+
startedAt?: string;
|
|
136
|
+
finishedAt?: string;
|
|
137
|
+
error?: IFlexErrorInfo;
|
|
138
|
+
}
|
|
125
139
|
export interface IFlexPromptResult {
|
|
126
140
|
runId: string;
|
|
127
141
|
sessionId: string;
|
|
@@ -132,10 +146,13 @@ export interface IFlexPromptResult {
|
|
|
132
146
|
finishReason: string;
|
|
133
147
|
steps: number;
|
|
134
148
|
}
|
|
135
|
-
export interface
|
|
136
|
-
|
|
149
|
+
export interface IFlexPromptQueueAdmission {
|
|
150
|
+
queueId: string;
|
|
137
151
|
completion: Promise<IFlexPromptResult>;
|
|
138
152
|
}
|
|
153
|
+
export interface IFlexPromptAdmission extends IFlexPromptQueueAdmission {
|
|
154
|
+
runId: string;
|
|
155
|
+
}
|
|
139
156
|
export interface IFlexSchedulePromptOptions extends IFlexPromptOptions {
|
|
140
157
|
debounceMs?: number;
|
|
141
158
|
}
|
|
@@ -277,6 +294,13 @@ export interface IFlexCallbackLimits {
|
|
|
277
294
|
maxOutputBytes?: number;
|
|
278
295
|
maxParts?: number;
|
|
279
296
|
}
|
|
297
|
+
export interface IFlexPromptQueueLimits {
|
|
298
|
+
maxOutstandingPromptsPerSession?: number;
|
|
299
|
+
maxOutstandingBytesPerSession?: number;
|
|
300
|
+
maxPendingAdmissions?: number;
|
|
301
|
+
maxPendingAdmissionBytes?: number;
|
|
302
|
+
maxTerminalEntriesPerSession?: number;
|
|
303
|
+
}
|
|
280
304
|
export type TFlexExternalErrorSource = 'modelResolver' | 'toolProvider' | 'toolExecution' | 'toolCallback' | 'toolCleanup' | 'agentSession' | 'persistence';
|
|
281
305
|
export interface IFlexExternalErrorContext {
|
|
282
306
|
source: TFlexExternalErrorSource;
|
|
@@ -318,6 +342,7 @@ export interface IFlexHarnessOptions<TScope> {
|
|
|
318
342
|
agentSessionPolicy?: IFlexAgentSessionPolicy;
|
|
319
343
|
toolOutputLimits?: IFlexJsonLimits;
|
|
320
344
|
callbackLimits?: IFlexCallbackLimits;
|
|
345
|
+
promptQueueLimits?: IFlexPromptQueueLimits;
|
|
321
346
|
externalErrorProjector?: TFlexExternalErrorProjector;
|
|
322
347
|
}
|
|
323
348
|
export interface IFlexUncertainToolExecution {
|
|
@@ -385,7 +410,11 @@ export interface IFlexPartChangedEvent extends IFlexEventBase {
|
|
|
385
410
|
readonly type: 'part.started' | 'part.delta' | 'part.completed';
|
|
386
411
|
readonly runId: string;
|
|
387
412
|
readonly messageId: string;
|
|
413
|
+
/** Zero-based position in the session's authoritative message sequence. */
|
|
414
|
+
readonly messageIndex: number;
|
|
388
415
|
readonly partId: string;
|
|
416
|
+
/** Zero-based position in the message's authoritative part sequence. */
|
|
417
|
+
readonly partIndex: number;
|
|
389
418
|
readonly part: TFlexMessagePart;
|
|
390
419
|
readonly delta?: string;
|
|
391
420
|
}
|
|
@@ -408,10 +437,16 @@ export interface IFlexRunFinishedEvent extends IFlexEventBase {
|
|
|
408
437
|
readonly message: IFlexMessage;
|
|
409
438
|
readonly error?: IFlexErrorInfo;
|
|
410
439
|
}
|
|
440
|
+
export interface IFlexPromptQueueEvent extends IFlexEventBase {
|
|
441
|
+
readonly type: 'prompt.queued' | 'prompt.started' | 'prompt.running' | 'prompt.finished';
|
|
442
|
+
readonly queueId: string;
|
|
443
|
+
readonly runId?: string;
|
|
444
|
+
readonly entry: IFlexPromptQueueEntry;
|
|
445
|
+
}
|
|
411
446
|
export interface IFlexErrorInfo {
|
|
412
447
|
name: string;
|
|
413
448
|
message: string;
|
|
414
449
|
code?: string;
|
|
415
450
|
}
|
|
416
|
-
export type TFlexHarnessEvent = IFlexSessionCreatedEvent | IFlexSessionUpdatedEvent | IFlexSessionDeletedEvent | IFlexRunStartedEvent | IFlexMessageChangedEvent | IFlexPartChangedEvent | IFlexPermissionRequestedEvent | IFlexPermissionResolvedEvent | IFlexRunFinishedEvent;
|
|
451
|
+
export type TFlexHarnessEvent = IFlexSessionCreatedEvent | IFlexSessionUpdatedEvent | IFlexSessionDeletedEvent | IFlexRunStartedEvent | IFlexMessageChangedEvent | IFlexPartChangedEvent | IFlexPermissionRequestedEvent | IFlexPermissionResolvedEvent | IFlexRunFinishedEvent | IFlexPromptQueueEvent;
|
|
417
452
|
export type TFlexHarnessEventListener = (event: TFlexHarnessEvent) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelprofile.com/flexharness",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Provider-neutral model-session runtime with durable history, permissions, typed events, and pluggable local or remote tool execution.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
package/readme.hints.md
CHANGED
|
@@ -31,3 +31,11 @@ Implementation findings for flexharness.
|
|
|
31
31
|
- Every tool `execute` result and every async-iterable yield is converted to bounded JSON before SmartAgent observes it. Thrown errors and iterator failures are not converted or swallowed.
|
|
32
32
|
- JSON byte limits are propagated through traversal. Large strings never enter normalized output, and array/object traversal stops once the remaining allowance is reserved for deterministic truncation metadata.
|
|
33
33
|
- Model callbacks use bounded synchronous run-local parts. Adjacent text/reasoning deltas coalesce; no per-delta snapshot is written. Callback event, byte, or part overflow aborts internally and is classified as failure, not owner cancellation.
|
|
34
|
+
|
|
35
|
+
## Prompt queue boundary
|
|
36
|
+
|
|
37
|
+
- Each loaded session owns one bounded runtime FIFO. Queue records retain private normalized prompt input and options only until terminal settlement; public queue entries and events expose IDs, status, timestamps, schedule keys, and projected errors only.
|
|
38
|
+
- Never-started queue entries are intentionally not persisted. Scope and projection stores are public/redacted, Agent events are canonical model transactions, and the SmartAgent job store is reserved for background execution state. Reusing any of them for queued prompt payloads would violate its trust or lifecycle contract.
|
|
39
|
+
- `enqueuePrompt()` is the runtime acceptance point. Existing `startPrompt()` and `schedulePrompt()` wait for the same entry's durable promotion, preserving their canonical reservation guarantee.
|
|
40
|
+
- Promotion installs active ownership before its first await and checks cancellation, lifecycle, and exact stored-session identity after every durable boundary. Cancellation before start events interrupts and rolls back the canonical claim; cancellation afterward uses the active AgentSession path.
|
|
41
|
+
- Deletion tombstones before queue cancellation. Scope retirement, namespace fencing, and disposal fence promotion before cancelling queued and active entries. Queue listeners remain available through deletion and retirement and clear only after disposal has emitted terminal events.
|
package/readme.md
CHANGED
|
@@ -82,6 +82,13 @@ const harness = new FlexHarness<IProjectScope>({
|
|
|
82
82
|
maxOutputBytes: 1024 * 1024,
|
|
83
83
|
maxParts: 2_000,
|
|
84
84
|
},
|
|
85
|
+
promptQueueLimits: {
|
|
86
|
+
maxOutstandingPromptsPerSession: 16,
|
|
87
|
+
maxOutstandingBytesPerSession: 64 * 1024 * 1024,
|
|
88
|
+
maxPendingAdmissions: 64,
|
|
89
|
+
maxPendingAdmissionBytes: 128 * 1024 * 1024,
|
|
90
|
+
maxTerminalEntriesPerSession: 64,
|
|
91
|
+
},
|
|
85
92
|
externalErrorProjector: (_error, context) => ({
|
|
86
93
|
name: 'ModelOperationError',
|
|
87
94
|
message: `The ${context.source} operation failed.`,
|
|
@@ -155,7 +162,11 @@ await harness.updateSession(scopeId, sessionId, { title: 'Renamed', archived: tr
|
|
|
155
162
|
await harness.updateSession(scopeId, sessionId, { title: null, archived: false });
|
|
156
163
|
await harness.deleteSession(scopeId, sessionId);
|
|
157
164
|
await harness.prompt(scopeId, sessionId, prompt, options);
|
|
165
|
+
const queued = await harness.enqueuePrompt(scopeId, sessionId, prompt, options);
|
|
166
|
+
console.log(queued.queueId);
|
|
167
|
+
await queued.completion;
|
|
158
168
|
const admission = await harness.startPrompt(scopeId, sessionId, prompt, options);
|
|
169
|
+
console.log(admission.queueId);
|
|
159
170
|
console.log(admission.runId);
|
|
160
171
|
await admission.completion;
|
|
161
172
|
const scheduled = await harness.schedulePrompt(
|
|
@@ -166,6 +177,9 @@ const scheduled = await harness.schedulePrompt(
|
|
|
166
177
|
{ debounceMs: 250 },
|
|
167
178
|
);
|
|
168
179
|
await harness.cancelScheduledPrompt(scopeId, sessionId, scheduled.scheduleKey);
|
|
180
|
+
await harness.getPromptQueueEntry(scopeId, sessionId, queued.queueId);
|
|
181
|
+
await harness.listPromptQueueEntries(scopeId, sessionId);
|
|
182
|
+
await harness.cancelPrompt(scopeId, sessionId, queued.queueId);
|
|
169
183
|
await harness.abort(scopeId, sessionId);
|
|
170
184
|
await harness.listPendingPermissions(scopeId, sessionId);
|
|
171
185
|
await harness.respondToPermission(scopeId, sessionId, permissionId, 'once');
|
|
@@ -184,19 +198,25 @@ await harness.retireScope(scopeId);
|
|
|
184
198
|
await harness.dispose();
|
|
185
199
|
```
|
|
186
200
|
|
|
187
|
-
Only one run may be active in a session.
|
|
201
|
+
Only one run may be active in a session. Additional prompts enter a bounded FIFO owned by FlexHarness, while different sessions can run concurrently. `enqueuePrompt()` resolves with `{ queueId, completion }` after the immutable prompt and options have been accepted into that runtime queue and `prompt.queued` has been emitted. `startPrompt()` keeps its durable-admission behavior: it waits for its FIFO turn and resolves with `{ queueId, runId, completion }` only after the canonical generation claim, run ID, and initial public audit messages have been durably reserved and the corresponding start events have been emitted. `prompt()` preserves the simpler behavior by awaiting completion internally.
|
|
202
|
+
|
|
203
|
+
Queue entries expose `queued`, `starting`, `scheduled`, `running`, `completed`, `failed`, and `cancelled` status through `getPromptQueueEntry()` and `listPromptQueueEntries()`. The list is ordered by process-local `queueSequence`. `getPromptQueueEntry()` throws `FlexHarnessNotFoundError` for an unknown or evicted ID. `cancelPrompt()` cancels one exact queue ID: a waiting entry leaves the FIFO and releases capacity immediately but remains queryable as `cancelled` until terminal retention evicts it; a promoted entry uses the canonical run cancellation path. Cancelling a terminal entry returns `false`, while an unknown ID throws. `abort()` remains scoped to the currently active run.
|
|
204
|
+
|
|
205
|
+
The displayed queue limits are the defaults. Outstanding count and byte limits apply per session and include every non-terminal queued or active prompt until it settles. Pending-admission limits apply to the complete harness while scope aliases are unresolved. Terminal retention applies per session. Exceeding an admission limit throws `FlexHarnessQueueFullError`.
|
|
188
206
|
|
|
189
|
-
`
|
|
207
|
+
Queue payloads, status records, and `prompt.*` queue events are process-local. The existing stores do not have a private generic queue domain: projections are deliberately redacted, Agent events are canonical conversation transactions, and jobs are SmartAgent background executions. FlexHarness therefore never writes a never-started prompt into those unrelated domains. A process restart drops never-started entries; a prompt that reached durable run admission continues to use the existing canonical recovery policy and is repaired to a safe terminal state instead of being replayed.
|
|
208
|
+
|
|
209
|
+
`schedulePrompt()` waits for its FIFO turn, performs the same durable admission, exposes session status `scheduled`, and starts model preparation after its bounded `debounceMs` delay. Schedule keys remain unique across waiting and active prompts. `cancelScheduledPrompt()` returns `true` only while the matching schedule key can still be cancelled. Cancelling while it is still waiting rejects the `schedulePrompt()` call itself; cancelling after durable admission rejects the returned completion and marks its reserved audit messages cancelled.
|
|
190
210
|
|
|
191
211
|
The reservation save is the admission point. A save failure produces no start events or active audit. If disposal begins while that save is in flight and the save commits, admission still resolves and its completion settles as cancelled; disposal waits for terminal finalization.
|
|
192
212
|
|
|
193
213
|
`listMessagePage()` returns the newest contiguous page in chronological order. `limit` must be an integer from 1 through 50 and defaults to 50. `nextCursor` is opaque, limited to 4096 UTF-8 bytes, bound to the resolved storage namespace and session, and remains stable when newer messages are appended. Mismatched and stale cursors fail validation. `getMessage()` performs an exact lookup. Transfer identifiers are limited to 512 bytes, text and reasoning parts to 96 KiB, complete messages to 480 KiB, and complete page envelopes to 512 KiB. A page may therefore contain fewer messages than requested. Oversized text is truncated and an otherwise oversized parts collection is replaced with an explicit elision marker; metadata that still cannot fit fails validation. Canonical private Agent events are unchanged.
|
|
194
214
|
|
|
195
|
-
`updateSession()` supports title replacement, explicit title clearing with `null`, and archive state through `archived`. Archived sessions expose `archivedAt`. Updates are rejected while the session has
|
|
215
|
+
`updateSession()` supports title replacement, explicit title clearing with `null`, and archive state through `archived`. Archived sessions expose `archivedAt`. Updates are rejected while the session has any outstanding prompt or pending permission. Deletion first hides the session behind a durable tombstone, then cancels queued and active work, rejects pending permissions, emits terminal queue events, waits for runtime cleanup, purges runtime queue status, and removes the complete persisted session across public projections, canonical Agent events and archives, remembered permission grants, and background job state. If cleanup fails, the tombstone remains and the operation is retried by a later `deleteSession()`, namespace load, `retireScope()`, or `dispose()` call.
|
|
196
216
|
|
|
197
217
|
`abort()` returns `true` only while cancellation is still accepted. Terminal persistence is the run's commit point; once it starts, `abort()` returns `false` and the already-fixed terminal outcome completes while the session remains busy.
|
|
198
218
|
|
|
199
|
-
`retireScope()` stops runtime ownership for the complete resolved storage namespace without deleting its durable snapshot. It does not load a namespace that has no cached or in-flight state. For loaded state, it preserves and waits for persistence that has already started, while later queued reads, writes, and run admissions reject with `FlexHarnessAbortError`. It
|
|
219
|
+
`retireScope()` stops runtime ownership for the complete resolved storage namespace without deleting its durable snapshot. It does not load a namespace that has no cached or in-flight state. For loaded state, it preserves and waits for persistence that has already started, while later queued reads, writes, and run admissions reject with `FlexHarnessAbortError`. It cancels queued prompts and cancellable runs, rejects pending permissions, emits queue terminal events, waits for committing runs, terminal persistence, queue drains, tool-handle closure, and detached tool-provider cleanup, then purges queue status and clears and evicts the cached state. Failed cleanup ownership remains cached so a later `retireScope()` or `dispose()` call can retry it. Calls through storage-key aliases share the same retirement drain. A later call can load the durable namespace again after successful retirement if the application still resolves it.
|
|
200
220
|
|
|
201
221
|
Normal retirement-induced cancellation does not make `retireScope()` reject. Unexpected failures observed through run finalization or scoped cleanup are surfaced without dropping the resources that still require cleanup. One such failure is thrown directly; multiple failures are reported through `FlexHarnessRunError`. Calling retirement or disposal again retries retained cleanup ownership.
|
|
202
222
|
|
|
@@ -270,7 +290,7 @@ Transactional tool calls persist an execution intent before the tool side effect
|
|
|
270
290
|
const unsubscribe = harness.subscribe((event) => {
|
|
271
291
|
switch (event.type) {
|
|
272
292
|
case 'part.delta':
|
|
273
|
-
renderDelta(event.sessionId, event.
|
|
293
|
+
renderDelta(event.sessionId, event.messageIndex, event.partIndex, event.delta);
|
|
274
294
|
break;
|
|
275
295
|
case 'permission.requested':
|
|
276
296
|
showPermission(event.request);
|
|
@@ -284,13 +304,19 @@ const unsubscribe = harness.subscribe((event) => {
|
|
|
284
304
|
case 'run.finished':
|
|
285
305
|
markRunFinished(event.runId, event.status);
|
|
286
306
|
break;
|
|
307
|
+
case 'prompt.queued':
|
|
308
|
+
case 'prompt.started':
|
|
309
|
+
case 'prompt.running':
|
|
310
|
+
case 'prompt.finished':
|
|
311
|
+
renderQueueStatus(event.queueId, event.entry.status);
|
|
312
|
+
break;
|
|
287
313
|
}
|
|
288
314
|
});
|
|
289
315
|
|
|
290
316
|
unsubscribe();
|
|
291
317
|
```
|
|
292
318
|
|
|
293
|
-
Events are discriminated, sequenced, deeply immutable snapshots. Listener exceptions are isolated from runs and other listeners. Events contain public IDs and snapshots only; they do not expose the resolved scope object, storage key, model object, or provider options.
|
|
319
|
+
Events are discriminated, sequenced, deeply immutable snapshots. Listener exceptions are isolated from runs and other listeners. Every accepted queue entry emits `prompt.queued` and exactly one `prompt.finished`. Durable promotion additionally emits `prompt.started`, and actual model preparation emits `prompt.running`; cancellation or failure can omit either intermediate event. Existing durable run/message terminal events precede `prompt.finished`. Every `part.started`, `part.delta`, and `part.completed` event carries zero-based `messageIndex` and `partIndex` coordinates from the session's authoritative message and part sequences. Events contain public IDs and snapshots only; they do not expose prompt payloads, the resolved scope object, storage key, model object, or provider options.
|
|
294
320
|
|
|
295
321
|
## Stores
|
|
296
322
|
|
|
@@ -353,7 +379,7 @@ Model and tool resolution share the run signal. Synchronous throws are observed
|
|
|
353
379
|
|
|
354
380
|
Tool-handle close settles before a turn can be successful. After model generation resolves, FlexHarness stages its terminal projection before canonical acceptance; earlier execution failures are finalized as interrupted and then published from that durable outcome. Cleanup failure prevents canonical acceptance. If canonical finalization or public promotion fails, FlexHarness fences the namespace; the next load repairs public state from the durable canonical outcome and any hidden terminal stage.
|
|
355
381
|
|
|
356
|
-
`dispose()` is asynchronous and idempotent. It marks the harness closed,
|
|
382
|
+
`dispose()` is asynchronous and idempotent. It marks the harness closed, settles unresolved queue admissions, cancels queued prompts and cancellable active runs, rejects pending permissions, emits queue terminal events, waits for committing runs, queue drains, all run finalizers, state save tails, and tracked detached tool-provider cleanup, purges runtime queue records, then clears listeners. Loaded state caches are cleared after all cleanup succeeds; a failed drain retains its cache and cleanup ownership so a later `dispose()` call can retry it. Multiple run or cleanup failures are reported through `FlexHarnessRunError`.
|
|
357
383
|
|
|
358
384
|
If `dispose()` overlaps a storage namespace already being retired, both calls await the same storage drain and cleanup runs once. A retirement call begun after disposal starts rejects with `FlexHarnessClosedError`.
|
|
359
385
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@modelprofile.com/flexharness',
|
|
6
|
-
version: '3.0
|
|
6
|
+
version: '3.2.0',
|
|
7
7
|
description: 'Provider-neutral model-session runtime with durable history, permissions, typed events, and pluggable local or remote tool execution.'
|
|
8
8
|
}
|