@modelprofile.com/flexharness 4.1.2 → 5.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.
@@ -42,7 +42,7 @@ import type {
42
42
  IFlexMessagePage,
43
43
  IFlexMessagePageOptions,
44
44
  IFlexModelIdentity,
45
- IFlexPartChangedEvent,
45
+ IFlexPartSnapshotEvent,
46
46
  IFlexPermissionRequest,
47
47
  IFlexPermissionRequestInput,
48
48
  IFlexPermissionSnapshot,
@@ -74,6 +74,7 @@ import type {
74
74
  IFlexSchedulePromptOptions,
75
75
  IFlexScopeSnapshot,
76
76
  IFlexSession,
77
+ IFlexSessionGeneration,
77
78
  IFlexSessionReversionGroup,
78
79
  IFlexSessionReversionInfo,
79
80
  IFlexSessionTombstone,
@@ -321,6 +322,7 @@ interface IActiveRun {
321
322
  callbackEventCount: number;
322
323
  callbackOutputBytes: number;
323
324
  callbackParts: TFlexMessagePart[];
325
+ partTextUtf8Bytes: Map<string, number>;
324
326
  callbacksClosed: boolean;
325
327
  reasoningPartIds: Map<string, string>;
326
328
  toolPartIds: Map<string, string>;
@@ -518,7 +520,7 @@ function requireSessionGeneration(
518
520
  IFlexSession | IFlexSessionTombstone,
519
521
  'sessionGenerationId' | 'sessionGenerationSequence'
520
522
  >,
521
- ): { sessionGenerationId: string; sessionGenerationSequence: number } {
523
+ ): IFlexSessionGeneration {
522
524
  if (
523
525
  !session.sessionGenerationId
524
526
  || !Number.isSafeInteger(session.sessionGenerationSequence)
@@ -1848,6 +1850,7 @@ export class FlexHarness<TScope = unknown> {
1848
1850
  scope,
1849
1851
  storageKey: state.storageKey,
1850
1852
  sessionId: stored.session.sessionId,
1853
+ ...requireSessionGeneration(stored.session),
1851
1854
  rawArguments,
1852
1855
  arguments: arguments_,
1853
1856
  signal,
@@ -2511,6 +2514,7 @@ export class FlexHarness<TScope = unknown> {
2511
2514
  ): Promise<void> {
2512
2515
  const pending = stored.pendingReversion;
2513
2516
  if (pending?.kind !== 'apply') return;
2517
+ const sessionGeneration = requireSessionGeneration(stored.session);
2514
2518
  const segmentMap = new Map(stored.reversionSegments.map((segment) => [segment.runId, segment]));
2515
2519
  if (
2516
2520
  pending.segmentRunIds.some((runId) =>
@@ -2551,6 +2555,7 @@ export class FlexHarness<TScope = unknown> {
2551
2555
  scope,
2552
2556
  storageKey: state.storageKey,
2553
2557
  sessionId: stored.session.sessionId,
2558
+ ...sessionGeneration,
2554
2559
  runId,
2555
2560
  captureId,
2556
2561
  reference: cloneSerializable(reference),
@@ -2684,6 +2689,7 @@ export class FlexHarness<TScope = unknown> {
2684
2689
  scopeId,
2685
2690
  scope,
2686
2691
  stored.session.sessionId,
2692
+ requireSessionGeneration(stored.session),
2687
2693
  pending.runId,
2688
2694
  pending.captureId,
2689
2695
  pending.state === 'prepared' ? 'finalizing' : pending.state,
@@ -3110,6 +3116,7 @@ export class FlexHarness<TScope = unknown> {
3110
3116
  callbackEventCount: 0,
3111
3117
  callbackOutputBytes: 0,
3112
3118
  callbackParts: [],
3119
+ partTextUtf8Bytes: new Map(),
3113
3120
  callbacksClosed: false,
3114
3121
  reasoningPartIds: new Map(),
3115
3122
  toolPartIds: new Map(),
@@ -3997,6 +4004,7 @@ export class FlexHarness<TScope = unknown> {
3997
4004
  scopeId: run.scopeId,
3998
4005
  scope: run.scope as TScope,
3999
4006
  sessionId: run.sessionId,
4007
+ ...requireSessionGeneration(run.stored.session),
4000
4008
  runId: run.runId,
4001
4009
  ...resolverRelationship,
4002
4010
  signal,
@@ -4965,6 +4973,7 @@ export class FlexHarness<TScope = unknown> {
4965
4973
  scope: run.scope as TScope,
4966
4974
  storageKey: run.state.storageKey,
4967
4975
  sessionId: run.sessionId,
4976
+ ...requireSessionGeneration(run.stored.session),
4968
4977
  runId: run.runId,
4969
4978
  captureId,
4970
4979
  signal,
@@ -4999,6 +5008,7 @@ export class FlexHarness<TScope = unknown> {
4999
5008
  scope: TScope,
5000
5009
  storageKey: string,
5001
5010
  sessionId: string,
5011
+ sessionGeneration: Readonly<IFlexSessionGeneration>,
5002
5012
  runId: string,
5003
5013
  captureId: string,
5004
5014
  ): Promise<TNormalizedCaptureInspection> {
@@ -5009,6 +5019,7 @@ export class FlexHarness<TScope = unknown> {
5009
5019
  scope,
5010
5020
  storageKey,
5011
5021
  sessionId,
5022
+ ...sessionGeneration,
5012
5023
  runId,
5013
5024
  captureId,
5014
5025
  signal,
@@ -5042,6 +5053,7 @@ export class FlexHarness<TScope = unknown> {
5042
5053
  scope: TScope,
5043
5054
  storageKey: string,
5044
5055
  sessionId: string,
5056
+ sessionGeneration: Readonly<IFlexSessionGeneration>,
5045
5057
  runId: string,
5046
5058
  captureId: string,
5047
5059
  ): Promise<TFlexTurnReversionFinalizedOutcome> {
@@ -5051,6 +5063,7 @@ export class FlexHarness<TScope = unknown> {
5051
5063
  scope,
5052
5064
  storageKey,
5053
5065
  sessionId,
5066
+ ...sessionGeneration,
5054
5067
  runId,
5055
5068
  captureId,
5056
5069
  signal,
@@ -5070,6 +5083,7 @@ export class FlexHarness<TScope = unknown> {
5070
5083
  scopeId: string,
5071
5084
  scope: TScope,
5072
5085
  sessionId: string,
5086
+ sessionGeneration: Readonly<IFlexSessionGeneration>,
5073
5087
  runId: string,
5074
5088
  captureId: string,
5075
5089
  durableState: 'preparing' | 'prepared' | 'finalizing',
@@ -5081,6 +5095,7 @@ export class FlexHarness<TScope = unknown> {
5081
5095
  scope,
5082
5096
  state.storageKey,
5083
5097
  sessionId,
5098
+ sessionGeneration,
5084
5099
  runId,
5085
5100
  captureId,
5086
5101
  );
@@ -5105,6 +5120,7 @@ export class FlexHarness<TScope = unknown> {
5105
5120
  scope,
5106
5121
  state.storageKey,
5107
5122
  sessionId,
5123
+ sessionGeneration,
5108
5124
  runId,
5109
5125
  captureId,
5110
5126
  );
@@ -5114,6 +5130,7 @@ export class FlexHarness<TScope = unknown> {
5114
5130
  scope,
5115
5131
  state.storageKey,
5116
5132
  sessionId,
5133
+ sessionGeneration,
5117
5134
  runId,
5118
5135
  captureId,
5119
5136
  );
@@ -5266,6 +5283,7 @@ export class FlexHarness<TScope = unknown> {
5266
5283
  run.scope as TScope,
5267
5284
  run.state.storageKey,
5268
5285
  run.sessionId,
5286
+ requireSessionGeneration(run.stored.session),
5269
5287
  run.runId,
5270
5288
  captureId,
5271
5289
  );
@@ -5347,6 +5365,7 @@ export class FlexHarness<TScope = unknown> {
5347
5365
  run.scopeId,
5348
5366
  run.scope as TScope,
5349
5367
  run.sessionId,
5368
+ requireSessionGeneration(run.stored.session),
5350
5369
  run.runId,
5351
5370
  captureId,
5352
5371
  pending.state === 'prepared' ? 'finalizing' : pending.state,
@@ -5596,6 +5615,7 @@ export class FlexHarness<TScope = unknown> {
5596
5615
  }
5597
5616
  return;
5598
5617
  }
5618
+ const sessionGeneration = requireSessionGeneration(stored.session);
5599
5619
  while (stored.pendingReversionReleases.length > 0) {
5600
5620
  const release = stored.pendingReversionReleases[0];
5601
5621
  if (this.reversionProviderProtocolVersion() !== release.protocolVersion) {
@@ -5610,6 +5630,7 @@ export class FlexHarness<TScope = unknown> {
5610
5630
  scope,
5611
5631
  storageKey: state.storageKey,
5612
5632
  sessionId: stored.session.sessionId,
5633
+ ...sessionGeneration,
5613
5634
  runId: release.runId,
5614
5635
  captureId: release.captureId,
5615
5636
  reference: cloneSerializable(release.reference),
@@ -5647,11 +5668,13 @@ export class FlexHarness<TScope = unknown> {
5647
5668
  private async releaseDeletedSessionReversions(
5648
5669
  state: IStorageState,
5649
5670
  storageKey: string,
5650
- sessionId: string,
5671
+ tombstone: IFlexSessionTombstone,
5651
5672
  scopeId: string,
5652
5673
  scope: TScope,
5653
5674
  stored?: IStoredSessionState,
5654
5675
  ): Promise<void> {
5676
+ const sessionId = tombstone.sessionId;
5677
+ const sessionGeneration = requireSessionGeneration(stored?.session ?? tombstone);
5655
5678
  if (stored) await stored.projectionQueue;
5656
5679
  let durableProjection = await this.stores.projections.load(storageKey, sessionId);
5657
5680
  if (!durableProjection) return;
@@ -5710,6 +5733,7 @@ export class FlexHarness<TScope = unknown> {
5710
5733
  scope,
5711
5734
  storageKey,
5712
5735
  sessionId,
5736
+ ...sessionGeneration,
5713
5737
  runId,
5714
5738
  captureId,
5715
5739
  reference: cloneSerializable(reference),
@@ -5823,6 +5847,7 @@ export class FlexHarness<TScope = unknown> {
5823
5847
  scope,
5824
5848
  storageKey,
5825
5849
  sessionId,
5850
+ sessionGeneration,
5826
5851
  capture.runId,
5827
5852
  capture.captureId,
5828
5853
  );
@@ -5852,6 +5877,7 @@ export class FlexHarness<TScope = unknown> {
5852
5877
  scope,
5853
5878
  storageKey,
5854
5879
  sessionId,
5880
+ sessionGeneration,
5855
5881
  capture.runId,
5856
5882
  capture.captureId,
5857
5883
  );
@@ -5861,6 +5887,7 @@ export class FlexHarness<TScope = unknown> {
5861
5887
  scope,
5862
5888
  storageKey,
5863
5889
  sessionId,
5890
+ sessionGeneration,
5864
5891
  capture.runId,
5865
5892
  capture.captureId,
5866
5893
  );
@@ -5926,6 +5953,7 @@ export class FlexHarness<TScope = unknown> {
5926
5953
  scope,
5927
5954
  storageKey,
5928
5955
  sessionId,
5956
+ ...sessionGeneration,
5929
5957
  runId: release.runId,
5930
5958
  captureId: release.captureId,
5931
5959
  reference: cloneSerializable(release.reference),
@@ -6062,14 +6090,25 @@ export class FlexHarness<TScope = unknown> {
6062
6090
  if (!run || !delta) return;
6063
6091
  const lastPart = run.callbackParts.at(-1);
6064
6092
  const created = lastPart?.type !== 'text';
6065
- if (!this.reserveCallbackCapacity(run, created ? 2 : 1, Buffer.byteLength(delta), created ? 1 : 0)) return;
6066
6093
  const part = lastPart?.type === 'text'
6067
6094
  ? lastPart
6068
6095
  : { partId: plugins.crypto.randomUUID(), type: 'text' as const, text: '' };
6069
- if (created) run.callbackParts.push(part);
6096
+ const accounting = this.textAppendAccounting(run, part, delta, created);
6097
+ if (!accounting) return;
6098
+ if (!this.reserveCallbackCapacity(
6099
+ run,
6100
+ created ? 2 : 1,
6101
+ accounting.textUtf8Bytes - accounting.baseTextUtf8Bytes,
6102
+ created ? 1 : 0,
6103
+ )) return;
6104
+ if (created) {
6105
+ run.callbackParts.push(part);
6106
+ run.partTextUtf8Bytes.set(part.partId, 0);
6107
+ this.emitPartEvent(run, 'part.started', part);
6108
+ }
6070
6109
  part.text += delta;
6071
- if (created) this.emitPartEvent(run, 'part.started', part);
6072
- this.emitPartEvent(run, 'part.delta', part, delta);
6110
+ run.partTextUtf8Bytes.set(part.partId, accounting.textUtf8Bytes);
6111
+ this.emitPartDeltaEvent(run, part, delta, accounting);
6073
6112
  }
6074
6113
 
6075
6114
  private onReasoningStart(state: IStorageState, sessionId: string, id: string): void {
@@ -6084,6 +6123,7 @@ export class FlexHarness<TScope = unknown> {
6084
6123
  };
6085
6124
  run.reasoningPartIds.set(id, part.partId);
6086
6125
  run.callbackParts.push(part);
6126
+ run.partTextUtf8Bytes.set(part.partId, 0);
6087
6127
  this.emitPartEvent(run, 'part.started', part);
6088
6128
  }
6089
6129
 
@@ -6094,9 +6134,17 @@ export class FlexHarness<TScope = unknown> {
6094
6134
  const partId = run.reasoningPartIds.get(id);
6095
6135
  const part = run.callbackParts.find((entry) => entry.partId === partId && entry.type === 'reasoning');
6096
6136
  if (!part || part.type !== 'reasoning' || part.status !== 'running') return;
6097
- if (!this.reserveCallbackCapacity(run, 1, Buffer.byteLength(delta), 0)) return;
6137
+ const accounting = this.textAppendAccounting(run, part, delta);
6138
+ if (!accounting) return;
6139
+ if (!this.reserveCallbackCapacity(
6140
+ run,
6141
+ 1,
6142
+ accounting.textUtf8Bytes - accounting.baseTextUtf8Bytes,
6143
+ 0,
6144
+ )) return;
6098
6145
  part.text += delta;
6099
- this.emitPartEvent(run, 'part.delta', part, delta);
6146
+ run.partTextUtf8Bytes.set(part.partId, accounting.textUtf8Bytes);
6147
+ this.emitPartDeltaEvent(run, part, delta, accounting);
6100
6148
  }
6101
6149
 
6102
6150
  private onReasoningEnd(state: IStorageState, sessionId: string, id: string, text: string): void {
@@ -6107,12 +6155,44 @@ export class FlexHarness<TScope = unknown> {
6107
6155
  const part = run.callbackParts.find((entry) => entry.partId === partId && entry.type === 'reasoning');
6108
6156
  if (!part || part.type !== 'reasoning' || part.status !== 'running') return;
6109
6157
  const missingText = part.text ? '' : text;
6110
- if (!this.reserveCallbackCapacity(run, 1, Buffer.byteLength(missingText), 0)) return;
6111
- if (missingText) part.text = missingText;
6158
+ const missingTextUtf8Bytes = Buffer.byteLength(missingText, 'utf8');
6159
+ if (!this.reserveCallbackCapacity(run, 1, missingTextUtf8Bytes, 0)) return;
6160
+ if (missingText) {
6161
+ part.text = missingText;
6162
+ run.partTextUtf8Bytes.set(part.partId, missingTextUtf8Bytes);
6163
+ }
6112
6164
  part.status = 'completed';
6113
6165
  this.emitPartEvent(run, 'part.completed', part);
6114
6166
  }
6115
6167
 
6168
+ private textAppendAccounting(
6169
+ run: IActiveRun,
6170
+ part: Extract<TFlexMessagePart, { type: 'text' | 'reasoning' }>,
6171
+ delta: string,
6172
+ created = false,
6173
+ ): { baseTextUtf8Bytes: number; textUtf8Bytes: number } | undefined {
6174
+ const baseTextUtf8Bytes = created ? 0 : run.partTextUtf8Bytes.get(part.partId);
6175
+ if (baseTextUtf8Bytes === undefined) {
6176
+ throw new Error('Flex part text byte accounting is unavailable.');
6177
+ }
6178
+ const trailingCodeUnit = part.text.charCodeAt(part.text.length - 1);
6179
+ const leadingCodeUnit = delta.charCodeAt(0);
6180
+ const joinsSurrogatePair = trailingCodeUnit >= 0xD800
6181
+ && trailingCodeUnit <= 0xDBFF
6182
+ && leadingCodeUnit >= 0xDC00
6183
+ && leadingCodeUnit <= 0xDFFF;
6184
+ const textUtf8Bytes = baseTextUtf8Bytes
6185
+ + Buffer.byteLength(delta, 'utf8')
6186
+ - (joinsSurrogatePair ? 2 : 0);
6187
+ if (!Number.isSafeInteger(textUtf8Bytes)) {
6188
+ this.failCallback(run, this.trustInternalError(new FlexHarnessCallbackOverflowError(
6189
+ 'Callback text byte accounting exceeded the safe integer range.',
6190
+ )));
6191
+ return undefined;
6192
+ }
6193
+ return { baseTextUtf8Bytes, textUtf8Bytes };
6194
+ }
6195
+
6116
6196
  private onToolStart(
6117
6197
  state: IStorageState,
6118
6198
  sessionId: string,
@@ -6211,7 +6291,10 @@ export class FlexHarness<TScope = unknown> {
6211
6291
  const nextOutputBytes = run.callbackOutputBytes + outputBytes;
6212
6292
  const nextParts = run.callbackParts.length + partCount;
6213
6293
  if (
6214
- nextEvents > this.callbackLimits.maxEvents
6294
+ !Number.isSafeInteger(nextEvents)
6295
+ || !Number.isSafeInteger(nextOutputBytes)
6296
+ || !Number.isSafeInteger(nextParts)
6297
+ || nextEvents > this.callbackLimits.maxEvents
6215
6298
  || nextOutputBytes > this.callbackLimits.maxOutputBytes
6216
6299
  || nextParts > this.callbackLimits.maxParts
6217
6300
  ) {
@@ -6650,6 +6733,7 @@ export class FlexHarness<TScope = unknown> {
6650
6733
  scopeId: context.scopeId,
6651
6734
  scope: context.scope,
6652
6735
  sessionId: context.sessionId,
6736
+ ...requireSessionGeneration(run.stored.session),
6653
6737
  runId: context.runId,
6654
6738
  ...(context.parentSessionId === undefined ? {} : { parentSessionId: context.parentSessionId }),
6655
6739
  ...(context.agent === undefined ? {} : { agent: context.agent }),
@@ -7059,7 +7143,7 @@ export class FlexHarness<TScope = unknown> {
7059
7143
  await this.releaseDeletedSessionReversions(
7060
7144
  state,
7061
7145
  storageKey,
7062
- tombstone.sessionId,
7146
+ tombstone,
7063
7147
  scopeId,
7064
7148
  scope,
7065
7149
  );
@@ -8305,7 +8389,7 @@ export class FlexHarness<TScope = unknown> {
8305
8389
  await this.releaseDeletedSessionReversions(
8306
8390
  state,
8307
8391
  state.storageKey,
8308
- sessionId,
8392
+ tombstone,
8309
8393
  releaseContext.scopeId,
8310
8394
  releaseContext.scope,
8311
8395
  retained?.stored,
@@ -9329,20 +9413,10 @@ export class FlexHarness<TScope = unknown> {
9329
9413
 
9330
9414
  private emitPartEvent(
9331
9415
  run: IActiveRun,
9332
- type: IFlexPartChangedEvent['type'],
9416
+ type: IFlexPartSnapshotEvent['type'],
9333
9417
  part: TFlexMessagePart,
9334
- delta?: string,
9335
9418
  ): void {
9336
- const messageIndex = run.stored.messages.findIndex(
9337
- (message) => message.messageId === run.assistantMessageId,
9338
- );
9339
- const partIndex = run.callbackParts.findIndex((entry) => entry.partId === part.partId);
9340
- if (
9341
- messageIndex < 0
9342
- || run.stored.messages[messageIndex]?.messageId !== run.assistantMessageId
9343
- || partIndex < 0
9344
- || run.callbackParts[partIndex]?.partId !== part.partId
9345
- ) throw new Error('Flex part event source coordinates are unavailable.');
9419
+ const { messageIndex, partIndex } = this.partEventCoordinates(run, part.partId);
9346
9420
  this.emitEvent(run.scopeId, run.sessionId, {
9347
9421
  type,
9348
9422
  runId: run.runId,
@@ -9351,10 +9425,46 @@ export class FlexHarness<TScope = unknown> {
9351
9425
  partId: part.partId,
9352
9426
  partIndex,
9353
9427
  part: publicSnapshot(part),
9354
- ...(delta === undefined ? {} : { delta: truncateUtf8(delta, maxTransferTextBytes) }),
9355
9428
  });
9356
9429
  }
9357
9430
 
9431
+ private emitPartDeltaEvent(
9432
+ run: IActiveRun,
9433
+ part: Extract<TFlexMessagePart, { type: 'text' | 'reasoning' }>,
9434
+ delta: string,
9435
+ accounting: { baseTextUtf8Bytes: number; textUtf8Bytes: number },
9436
+ ): void {
9437
+ const { messageIndex, partIndex } = this.partEventCoordinates(run, part.partId);
9438
+ this.emitEvent(run.scopeId, run.sessionId, {
9439
+ type: 'part.delta',
9440
+ runId: run.runId,
9441
+ messageId: run.assistantMessageId,
9442
+ messageIndex,
9443
+ partId: part.partId,
9444
+ partIndex,
9445
+ partType: part.type,
9446
+ delta,
9447
+ ...accounting,
9448
+ });
9449
+ }
9450
+
9451
+ private partEventCoordinates(
9452
+ run: IActiveRun,
9453
+ partId: string,
9454
+ ): { messageIndex: number; partIndex: number } {
9455
+ const messageIndex = run.stored.messages.findIndex(
9456
+ (message) => message.messageId === run.assistantMessageId,
9457
+ );
9458
+ const partIndex = run.callbackParts.findIndex((entry) => entry.partId === partId);
9459
+ if (
9460
+ messageIndex < 0
9461
+ || run.stored.messages[messageIndex]?.messageId !== run.assistantMessageId
9462
+ || partIndex < 0
9463
+ || run.callbackParts[partIndex]?.partId !== partId
9464
+ ) throw new Error('Flex part event source coordinates are unavailable.');
9465
+ return { messageIndex, partIndex };
9466
+ }
9467
+
9358
9468
  private emitEvent(scopeId: string, sessionId: string, details: TEventDetails): void {
9359
9469
  const event = deepFreeze(cloneSerializable({
9360
9470
  eventId: plugins.crypto.randomUUID(),
package/ts/interfaces.ts CHANGED
@@ -207,7 +207,7 @@ export interface IFlexSlashCommandDescriptor {
207
207
  workspaceReversion: TFlexSlashCommandWorkspaceReversion;
208
208
  }
209
209
 
210
- export interface IFlexSlashCommandHandlerContext<TScope> {
210
+ export interface IFlexSlashCommandHandlerContext<TScope> extends IFlexSessionGeneration {
211
211
  readonly scopeId: string;
212
212
  readonly scope: TScope;
213
213
  readonly storageKey: string;
@@ -386,7 +386,7 @@ export interface IFlexPermissionRequest extends IFlexPermissionRequestInput {
386
386
 
387
387
  export type TFlexPermissionDecision = 'once' | 'always' | 'reject';
388
388
 
389
- export interface IFlexToolProviderContext<TScope> {
389
+ export interface IFlexToolProviderContext<TScope> extends IFlexSessionGeneration {
390
390
  scopeId: string;
391
391
  scope: TScope;
392
392
  sessionId: string;
@@ -408,7 +408,7 @@ export interface IFlexToolProvider<TScope> {
408
408
  ): Promise<IFlexToolHandle | undefined> | IFlexToolHandle | undefined;
409
409
  }
410
410
 
411
- export interface IFlexResourceToolProviderResolverContext<TScope> {
411
+ export interface IFlexResourceToolProviderResolverContext<TScope> extends IFlexSessionGeneration {
412
412
  scopeId: string;
413
413
  scope: TScope;
414
414
  sessionId: string;
@@ -793,7 +793,7 @@ export const FLEX_REVERSION_WORKSPACE_ID_MAX_BYTES = 512;
793
793
  export const FLEX_REVERSION_WORKSPACE_LABEL_MAX_BYTES = 2048;
794
794
  export const FLEX_REVERSION_REASON_CODE_MAX_BYTES = 128;
795
795
 
796
- export interface IFlexTurnReversionBaseContext<TScope> {
796
+ export interface IFlexTurnReversionBaseContext<TScope> extends IFlexSessionGeneration {
797
797
  readonly scopeId: string;
798
798
  readonly scope: TScope;
799
799
  readonly storageKey: string;
@@ -1107,8 +1107,7 @@ export interface IFlexMessageChangedEvent extends IFlexEventBase {
1107
1107
  readonly message: IFlexMessage;
1108
1108
  }
1109
1109
 
1110
- export interface IFlexPartChangedEvent extends IFlexEventBase {
1111
- readonly type: 'part.started' | 'part.delta' | 'part.updated' | 'part.completed';
1110
+ export interface IFlexPartEventBase extends IFlexEventBase {
1112
1111
  readonly runId: string;
1113
1112
  readonly messageId: string;
1114
1113
  /** Zero-based position in the session's authoritative message sequence. */
@@ -1116,10 +1115,23 @@ export interface IFlexPartChangedEvent extends IFlexEventBase {
1116
1115
  readonly partId: string;
1117
1116
  /** Zero-based position in the message's authoritative part sequence. */
1118
1117
  readonly partIndex: number;
1118
+ }
1119
+
1120
+ export interface IFlexPartSnapshotEvent extends IFlexPartEventBase {
1121
+ readonly type: 'part.started' | 'part.updated' | 'part.completed';
1119
1122
  readonly part: TFlexMessagePart;
1120
- readonly delta?: string;
1121
1123
  }
1122
1124
 
1125
+ export interface IFlexPartDeltaEvent extends IFlexPartEventBase {
1126
+ readonly type: 'part.delta';
1127
+ readonly partType: 'text' | 'reasoning';
1128
+ readonly delta: string;
1129
+ readonly baseTextUtf8Bytes: number;
1130
+ readonly textUtf8Bytes: number;
1131
+ }
1132
+
1133
+ export type TFlexPartEvent = IFlexPartSnapshotEvent | IFlexPartDeltaEvent;
1134
+
1123
1135
  export interface IFlexPermissionRequestedEvent extends IFlexEventBase {
1124
1136
  readonly type: 'permission.requested';
1125
1137
  readonly runId: string;
@@ -1167,7 +1179,7 @@ export type TFlexHarnessEvent =
1167
1179
  | IFlexSessionDeletedEvent
1168
1180
  | IFlexRunStartedEvent
1169
1181
  | IFlexMessageChangedEvent
1170
- | IFlexPartChangedEvent
1182
+ | TFlexPartEvent
1171
1183
  | IFlexPermissionRequestedEvent
1172
1184
  | IFlexPermissionResolvedEvent
1173
1185
  | IFlexRunFinishedEvent