@modelprofile.com/flexharness 4.0.1 → 4.0.2

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.
@@ -441,6 +441,10 @@ export interface IFlexPermissionSnapshot {
441
441
  }
442
442
  export declare const FLEX_PROJECT_MANAGEMENT_SCHEMA_VERSION: 1;
443
443
  export declare const FLEX_SESSION_GENERATION_ID_MAX_BYTES = 128;
444
+ export interface IFlexSessionGeneration {
445
+ sessionGenerationId: string;
446
+ sessionGenerationSequence: number;
447
+ }
444
448
  export declare const FLEX_PROJECT_MANAGEMENT_LIMITS: Readonly<{
445
449
  maxGoalBytes: number;
446
450
  maxScratchpadBytes: number;
@@ -485,7 +489,7 @@ export interface IFlexProjectManagementWriteContext {
485
489
  toolCallId?: string;
486
490
  }
487
491
  export interface IFlexProjectManagementStore {
488
- load(storageKey: string, sessionId: string): Promise<TFlexProjectManagementRecord | undefined>;
492
+ load(storageKey: string, sessionId: string, sessionGeneration?: Readonly<IFlexSessionGeneration>): Promise<TFlexProjectManagementRecord | undefined>;
489
493
  save(storageKey: string, sessionId: string, snapshot: IFlexProjectManagementSnapshot, expectedRevision: number, writeContext: IFlexProjectManagementWriteContext): Promise<void>;
490
494
  tombstoneSession(storageKey: string, sessionId: string, tombstone: IFlexProjectManagementTombstone, expectedRevision: number): Promise<void>;
491
495
  purgeNamespace(storageKey: string): Promise<void>;
@@ -26,4 +26,4 @@ export const FLEX_REVERSION_MAX_AFFECTED_WORKSPACES = 64;
26
26
  export const FLEX_REVERSION_WORKSPACE_ID_MAX_BYTES = 512;
27
27
  export const FLEX_REVERSION_WORKSPACE_LABEL_MAX_BYTES = 2048;
28
28
  export const FLEX_REVERSION_REASON_CODE_MAX_BYTES = 128;
29
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2ludGVyZmFjZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBMGpCQSxNQUFNLENBQUMsTUFBTSxzQ0FBc0MsR0FBRyxDQUFVLENBQUM7QUFDakUsTUFBTSxDQUFDLE1BQU0sb0NBQW9DLEdBQUcsR0FBRyxDQUFDO0FBRXhELE1BQU0sQ0FBQyxNQUFNLDhCQUE4QixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDMUQsWUFBWSxFQUFFLENBQUMsR0FBRyxJQUFJO0lBQ3RCLGtCQUFrQixFQUFFLEdBQUcsR0FBRyxJQUFJO0lBQzlCLG1CQUFtQixFQUFFLENBQUMsR0FBRyxJQUFJO0lBQzdCLGNBQWMsRUFBRSxHQUFHO0lBQ25CLFFBQVEsRUFBRSxHQUFHO0lBQ2IsYUFBYSxFQUFFLElBQUk7SUFDbkIsZ0JBQWdCLEVBQUUsSUFBSSxHQUFHLElBQUk7Q0FDOUIsQ0FBQyxDQUFDO0FBeUxILE1BQU0sQ0FBQyxNQUFNLDZCQUE2QixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDekQsaUJBQWlCLEVBQUUsR0FBRztJQUN0QixXQUFXLEVBQUUsR0FBRztJQUNoQixpQkFBaUIsRUFBRSxJQUFJO0lBQ3ZCLDJCQUEyQixFQUFFLElBQUk7Q0FDbEMsQ0FBQyxDQUFDO0FBRUgsTUFBTSxDQUFDLE1BQU0sNkJBQTZCLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUN6RCxpQkFBaUIsRUFBRSxJQUFJO0lBQ3ZCLFdBQVcsRUFBRSxJQUFJO0lBQ2pCLGlCQUFpQixFQUFFLE1BQU07SUFDekIsMkJBQTJCLEVBQUUsTUFBTTtDQUNwQyxDQUFDLENBQUM7QUFFSCxNQUFNLENBQUMsTUFBTSxrQ0FBa0MsR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDO0FBQzdELE1BQU0sQ0FBQyxNQUFNLHNDQUFzQyxHQUFHLEVBQUUsQ0FBQztBQUN6RCxNQUFNLENBQUMsTUFBTSxxQ0FBcUMsR0FBRyxHQUFHLENBQUM7QUFDekQsTUFBTSxDQUFDLE1BQU0sd0NBQXdDLEdBQUcsSUFBSSxDQUFDO0FBQzdELE1BQU0sQ0FBQyxNQUFNLG9DQUFvQyxHQUFHLEdBQUcsQ0FBQyJ9
29
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2ludGVyZmFjZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBMGpCQSxNQUFNLENBQUMsTUFBTSxzQ0FBc0MsR0FBRyxDQUFVLENBQUM7QUFDakUsTUFBTSxDQUFDLE1BQU0sb0NBQW9DLEdBQUcsR0FBRyxDQUFDO0FBT3hELE1BQU0sQ0FBQyxNQUFNLDhCQUE4QixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDMUQsWUFBWSxFQUFFLENBQUMsR0FBRyxJQUFJO0lBQ3RCLGtCQUFrQixFQUFFLEdBQUcsR0FBRyxJQUFJO0lBQzlCLG1CQUFtQixFQUFFLENBQUMsR0FBRyxJQUFJO0lBQzdCLGNBQWMsRUFBRSxHQUFHO0lBQ25CLFFBQVEsRUFBRSxHQUFHO0lBQ2IsYUFBYSxFQUFFLElBQUk7SUFDbkIsZ0JBQWdCLEVBQUUsSUFBSSxHQUFHLElBQUk7Q0FDOUIsQ0FBQyxDQUFDO0FBMExILE1BQU0sQ0FBQyxNQUFNLDZCQUE2QixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDekQsaUJBQWlCLEVBQUUsR0FBRztJQUN0QixXQUFXLEVBQUUsR0FBRztJQUNoQixpQkFBaUIsRUFBRSxJQUFJO0lBQ3ZCLDJCQUEyQixFQUFFLElBQUk7Q0FDbEMsQ0FBQyxDQUFDO0FBRUgsTUFBTSxDQUFDLE1BQU0sNkJBQTZCLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUN6RCxpQkFBaUIsRUFBRSxJQUFJO0lBQ3ZCLFdBQVcsRUFBRSxJQUFJO0lBQ2pCLGlCQUFpQixFQUFFLE1BQU07SUFDekIsMkJBQTJCLEVBQUUsTUFBTTtDQUNwQyxDQUFDLENBQUM7QUFFSCxNQUFNLENBQUMsTUFBTSxrQ0FBa0MsR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDO0FBQzdELE1BQU0sQ0FBQyxNQUFNLHNDQUFzQyxHQUFHLEVBQUUsQ0FBQztBQUN6RCxNQUFNLENBQUMsTUFBTSxxQ0FBcUMsR0FBRyxHQUFHLENBQUM7QUFDekQsTUFBTSxDQUFDLE1BQU0sd0NBQXdDLEdBQUcsSUFBSSxDQUFDO0FBQzdELE1BQU0sQ0FBQyxNQUFNLG9DQUFvQyxHQUFHLEdBQUcsQ0FBQyJ9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelprofile.com/flexharness",
3
- "version": "4.0.1",
3
+ "version": "4.0.2",
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.md CHANGED
@@ -241,7 +241,7 @@ Public writes use `{ actor: 'application' }`. Tool writes use `{ actor: 'agent',
241
241
 
242
242
  `FLEX_PROJECT_MANAGEMENT_LIMITS` exports the hard UTF-8 and aggregate limits: goal 8 KiB, scratchpad 128 KiB, task content 8 KiB, task ID 512 bytes, title 2048 bytes, 512 tasks, and a 1 MiB serialized snapshot. The aggregate bound leaves room for worst-case JSON escaping of a controller-valid scratchpad. Loaded snapshots reject extra fields, duplicate IDs, invalid status/priority/timestamps, non-JSON data, wrong schema/revision, and every exceeded bound before use.
243
243
 
244
- `IFlexProjectManagementStore` is exact per `(storageKey, sessionId)`: `load`, CAS `save`, CAS `tombstoneSession`, and `purgeNamespace` must not collapse multiple sessions or storage namespaces. `load()` returns `TFlexProjectManagementRecord | undefined`. Same-generation live saves use normal revision CAS, and a same-generation tombstone permanently rejects later live saves. A higher `sessionGenerationSequence` with a different strong `sessionGenerationId` may replace only an older tombstone using expected revision `0`; it cannot replace a live record. This resets the PM revision for a recreated core session while stale saves and tombstones from older generations remain fenced. Deleting a recreated session that made no PM writes still replaces the prior-generation tombstone with a revision-1 tombstone for the new generation.
244
+ `IFlexProjectManagementStore` is exact per `(storageKey, sessionId)`: `load`, CAS `save`, CAS `tombstoneSession`, and `purgeNamespace` must not collapse multiple sessions or storage namespaces. `load()` returns `TFlexProjectManagementRecord | undefined` and receives the current session generation as an optional third argument; FlexHarness always supplies it, while two-argument callers and stores remain compatible. Same-generation live saves use normal revision CAS, and a same-generation tombstone permanently rejects later live saves. A higher `sessionGenerationSequence` with a different strong `sessionGenerationId` may replace only an older tombstone using expected revision `0`; it cannot replace a live record. This resets the PM revision for a recreated core session while stale saves and tombstones from older generations remain fenced. Deleting a recreated session that made no PM writes still replaces the prior-generation tombstone with a revision-1 tombstone for the new generation.
245
245
 
246
246
  Every newly created core session exposes and persists a strong random `sessionGenerationId` plus its monotonic `sessionGenerationSequence`. A legacy scope session without those fields is assigned a deterministic bounded ID derived from its immutable `storageKey`, `sessionId`, and `createdAt`; FlexHarness persists the repaired scope snapshot before accepting work. Grouped core deletion tombstones retain both fields after live metadata is removed.
247
247
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@modelprofile.com/flexharness',
6
- version: '4.0.1',
6
+ version: '4.0.2',
7
7
  description: 'Provider-neutral model-session runtime with durable history, permissions, typed events, and pluggable local or remote tool execution.'
8
8
  }
@@ -4391,7 +4391,11 @@ export class FlexHarness<TScope = unknown> {
4391
4391
  ): Promise<IFlexProjectManagementSnapshot> {
4392
4392
  const store = this.stores.projectManagement;
4393
4393
  const generation = requireSessionGeneration(stored.session);
4394
- const record = await store.load(state.storageKey, stored.session.sessionId);
4394
+ const record = await store.load(
4395
+ state.storageKey,
4396
+ stored.session.sessionId,
4397
+ Object.freeze({ ...generation }),
4398
+ );
4395
4399
  if (record === undefined) {
4396
4400
  return createEmptyFlexProjectManagementSnapshot(
4397
4401
  generation.sessionGenerationId,
@@ -8795,7 +8799,11 @@ export class FlexHarness<TScope = unknown> {
8795
8799
  const generation = requireSessionGeneration(sessionTombstone);
8796
8800
  let lastConflict: FlexHarnessStoreConflictError | undefined;
8797
8801
  for (let attempt = 0; attempt <= maxProjectManagementTombstoneConflicts; attempt++) {
8798
- const current = await store.load(storageKey, sessionId);
8802
+ const current = await store.load(
8803
+ storageKey,
8804
+ sessionId,
8805
+ Object.freeze({ ...generation }),
8806
+ );
8799
8807
  if (current !== undefined) {
8800
8808
  assertFlexProjectManagementRecord(current);
8801
8809
  if (current.sessionGenerationId === generation.sessionGenerationId) {
package/ts/interfaces.ts CHANGED
@@ -571,6 +571,11 @@ export interface IFlexPermissionSnapshot {
571
571
  export const FLEX_PROJECT_MANAGEMENT_SCHEMA_VERSION = 1 as const;
572
572
  export const FLEX_SESSION_GENERATION_ID_MAX_BYTES = 128;
573
573
 
574
+ export interface IFlexSessionGeneration {
575
+ sessionGenerationId: string;
576
+ sessionGenerationSequence: number;
577
+ }
578
+
574
579
  export const FLEX_PROJECT_MANAGEMENT_LIMITS = Object.freeze({
575
580
  maxGoalBytes: 8 * 1024,
576
581
  maxScratchpadBytes: 128 * 1024,
@@ -628,6 +633,7 @@ export interface IFlexProjectManagementStore {
628
633
  load(
629
634
  storageKey: string,
630
635
  sessionId: string,
636
+ sessionGeneration?: Readonly<IFlexSessionGeneration>,
631
637
  ): Promise<TFlexProjectManagementRecord | undefined>;
632
638
  save(
633
639
  storageKey: string,