agl 22.0.4 → 24.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.
Files changed (44) hide show
  1. package/changelog.md +21 -0
  2. package/dist_serve/bundle.js +2418 -2408
  3. package/dist_serve/bundle.js.map +1 -0
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/classes.authstore.js +2 -2
  6. package/dist_ts/classes.browserresourcehost.d.ts +1 -0
  7. package/dist_ts/classes.browserresourcehost.js +7 -1
  8. package/dist_ts/classes.controller.d.ts +37 -5
  9. package/dist_ts/classes.controller.js +1491 -249
  10. package/dist_ts/classes.flexservice.d.ts +5 -0
  11. package/dist_ts/classes.flexservice.js +131 -4
  12. package/dist_ts/classes.opencodeclient.d.ts +2 -0
  13. package/dist_ts/classes.opencodeclient.js +23 -1
  14. package/dist_ts/classes.validation.d.ts +9 -1
  15. package/dist_ts/classes.validation.js +62 -7
  16. package/dist_ts/interfaces.flexipc.d.ts +18 -1
  17. package/dist_ts/interfaces.flexipc.js +42 -8
  18. package/dist_ts_interfaces/browsertransport.d.ts +72 -0
  19. package/dist_ts_interfaces/browsertransport.js +47 -0
  20. package/dist_ts_interfaces/index.d.ts +1 -0
  21. package/dist_ts_interfaces/index.js +2 -1
  22. package/dist_ts_interfaces/interfaces.d.ts +42 -7
  23. package/dist_ts_interfaces/interfaces.js +5 -3
  24. package/dist_ts_interfaces/requests.d.ts +20 -21
  25. package/package.json +7 -7
  26. package/readme.md +12 -2
  27. package/ts/00_commitinfo_data.ts +1 -1
  28. package/ts/classes.authstore.ts +1 -1
  29. package/ts/classes.browserresourcehost.ts +7 -0
  30. package/ts/classes.controller.ts +1960 -305
  31. package/ts/classes.flexservice.ts +132 -2
  32. package/ts/classes.opencodeclient.ts +19 -0
  33. package/ts/classes.validation.ts +77 -6
  34. package/ts/interfaces.flexipc.ts +65 -7
  35. package/ts_interfaces/browsertransport.ts +126 -0
  36. package/ts_interfaces/index.ts +1 -0
  37. package/ts_interfaces/interfaces.ts +46 -11
  38. package/ts_interfaces/requests.ts +23 -30
  39. package/ts_web/00_commitinfo_data.ts +1 -1
  40. package/ts_web/classes.browserviewtransport.ts +532 -0
  41. package/ts_web/classes.controllersocketclient.ts +119 -29
  42. package/ts_web/elements.harnesscontrollerapp.ts +1727 -287
  43. package/ts_web/index.ts +1 -0
  44. package/ts_web/plugins.ts +2 -0
@@ -3,6 +3,7 @@ import * as interfaces from '../ts_interfaces/index.js';
3
3
 
4
4
  const controllerEventTypes: ReadonlySet<string> = new Set(interfaces.controllerEventTypes);
5
5
  const controllerRequestTimeoutMs = 35_000;
6
+ const textEncoder = new TextEncoder();
6
7
 
7
8
  const isQualifiedRuntimeId = (
8
9
  valueArg: unknown,
@@ -16,7 +17,7 @@ const isQualifiedRuntimeId = (
16
17
  && allowedHarnessIdsArg.has(candidate.harnessId as interfaces.TControllerHarnessId)
17
18
  && typeof candidate.nativeId === 'string'
18
19
  && candidate.nativeId.length > 0
19
- && new TextEncoder().encode(candidate.nativeId).byteLength <= 512
20
+ && textEncoder.encode(candidate.nativeId).byteLength <= 512
20
21
  );
21
22
  };
22
23
 
@@ -36,6 +37,14 @@ const textUpdateKeys = new Set([
36
37
  'sessionId', 'messageId', 'partId', 'text', 'status', 'order',
37
38
  'sourceUpdatedAt', 'revision', 'streamEpoch',
38
39
  ]);
40
+ const messageDeltaKeys = new Set([
41
+ 'sessionId', 'messageId', 'partId', 'delta', 'baseTextUtf8Bytes', 'textUtf8Bytes',
42
+ 'order', 'sourceUpdatedAt', 'revision', 'streamEpoch',
43
+ ]);
44
+ const messageDeltaRequiredKeys = [
45
+ 'sessionId', 'messageId', 'partId', 'delta', 'baseTextUtf8Bytes', 'textUtf8Bytes',
46
+ 'sourceUpdatedAt', 'revision', 'streamEpoch',
47
+ ] as const;
39
48
 
40
49
  const isOptionalFiniteNumber = (valueArg: unknown): boolean => (
41
50
  valueArg === undefined || (typeof valueArg === 'number' && Number.isFinite(valueArg))
@@ -53,12 +62,26 @@ const isTranscriptOrder = (valueArg: unknown): boolean => {
53
62
 
54
63
  const serializedBytesWithin = (valueArg: unknown, byteLimitArg: number): boolean => {
55
64
  try {
56
- return new TextEncoder().encode(JSON.stringify(valueArg)).byteLength <= byteLimitArg;
65
+ return textEncoder.encode(JSON.stringify(valueArg)).byteLength <= byteLimitArg;
57
66
  } catch {
58
67
  return false;
59
68
  }
60
69
  };
61
70
 
71
+ const hasExactDeltaUtf8Coordinates = (
72
+ deltaArg: string,
73
+ baseTextUtf8BytesArg: number,
74
+ textUtf8BytesArg: number,
75
+ ): boolean => {
76
+ const encodedDeltaBytes = textEncoder.encode(deltaArg).byteLength;
77
+ const increment = textUtf8BytesArg - baseTextUtf8BytesArg;
78
+ if (increment === encodedDeltaBytes) return true;
79
+ const firstCodeUnit = deltaArg.charCodeAt(0);
80
+ return firstCodeUnit >= 0xDC00
81
+ && firstCodeUnit <= 0xDFFF
82
+ && increment === encodedDeltaBytes - 2;
83
+ };
84
+
62
85
  const isDraftAttachment = (valueArg: unknown): valueArg is interfaces.IControllerDraftAttachment => {
63
86
  if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) return false;
64
87
  const candidate = valueArg as Partial<interfaces.IControllerDraftAttachment>;
@@ -68,13 +91,13 @@ const isDraftAttachment = (valueArg: unknown): valueArg is interfaces.IControlle
68
91
  ))
69
92
  || typeof candidate.id !== 'string'
70
93
  || candidate.id.length === 0
71
- || new TextEncoder().encode(candidate.id).byteLength > 512
94
+ || textEncoder.encode(candidate.id).byteLength > 512
72
95
  || typeof candidate.name !== 'string'
73
96
  || candidate.name.length === 0
74
- || new TextEncoder().encode(candidate.name).byteLength > 255
97
+ || textEncoder.encode(candidate.name).byteLength > 255
75
98
  || typeof candidate.mediaType !== 'string'
76
99
  || candidate.mediaType.length === 0
77
- || new TextEncoder().encode(candidate.mediaType).byteLength > 129
100
+ || textEncoder.encode(candidate.mediaType).byteLength > 129
78
101
  || !Number.isSafeInteger(candidate.size)
79
102
  || (candidate.size ?? -1) < 0
80
103
  || (candidate.size ?? 0) > interfaces.controllerMaxDraftAttachmentBytes
@@ -101,7 +124,7 @@ const isSessionDraftUpdate = (
101
124
  || (candidate.text === undefined && candidate.attachments === undefined)
102
125
  || (candidate.text !== undefined && (
103
126
  typeof candidate.text !== 'string'
104
- || new TextEncoder().encode(candidate.text).byteLength > interfaces.controllerMaxDraftTextBytes
127
+ || textEncoder.encode(candidate.text).byteLength > interfaces.controllerMaxDraftTextBytes
105
128
  ))
106
129
  || (candidate.attachments !== undefined && (
107
130
  !Array.isArray(candidate.attachments)
@@ -130,7 +153,7 @@ const isControllerToolExecution = (
130
153
  && isQualifiedRuntimeId(candidate.callId, executionHarnessIds)
131
154
  && typeof candidate.toolName === 'string'
132
155
  && candidate.toolName.length > 0
133
- && new TextEncoder().encode(candidate.toolName).byteLength <= 2048
156
+ && textEncoder.encode(candidate.toolName).byteLength <= 2048
134
157
  && ['pending', 'running', 'completed', 'error'].includes(candidate.status ?? '')
135
158
  && (candidate.exitCode === undefined
136
159
  || (Number.isSafeInteger(candidate.exitCode) && candidate.exitCode >= 0))
@@ -166,7 +189,7 @@ const isControllerReasoningUpdate = (
166
189
  && isQualifiedRuntimeId(candidate.messageId, harnessIds)
167
190
  && isQualifiedRuntimeId(candidate.partId, harnessIds)
168
191
  && typeof candidate.text === 'string'
169
- && new TextEncoder().encode(candidate.text).byteLength
192
+ && textEncoder.encode(candidate.text).byteLength
170
193
  <= interfaces.controllerMaxLiveMessageEventBytes
171
194
  && ['running', 'completed', 'cancelled'].includes(candidate.status ?? '')
172
195
  && (candidate.order === undefined || isTranscriptOrder(candidate.order))
@@ -193,7 +216,7 @@ const isControllerTextUpdate = (
193
216
  && isQualifiedRuntimeId(candidate.messageId, harnessIds)
194
217
  && isQualifiedRuntimeId(candidate.partId, harnessIds)
195
218
  && typeof candidate.text === 'string'
196
- && new TextEncoder().encode(candidate.text).byteLength
219
+ && textEncoder.encode(candidate.text).byteLength
197
220
  <= interfaces.controllerMaxLiveMessageEventBytes
198
221
  && ['running', 'completed'].includes(candidate.status ?? '')
199
222
  && (candidate.order === undefined || isTranscriptOrder(candidate.order))
@@ -207,6 +230,48 @@ const isControllerTextUpdate = (
207
230
  && (candidate.streamEpoch ?? -1) >= 0;
208
231
  };
209
232
 
233
+ const isControllerMessageDelta = (
234
+ valueArg: unknown,
235
+ ): valueArg is interfaces.IControllerReasoningDelta | interfaces.IControllerTextDelta => {
236
+ if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) return false;
237
+ const candidate = valueArg as Partial<interfaces.IControllerReasoningDelta>;
238
+ if (!isQualifiedRuntimeId(candidate.sessionId, sessionHarnessIds)) return false;
239
+ const harnessIds: ReadonlySet<interfaces.TControllerHarnessId> = new Set([
240
+ candidate.sessionId.harnessId,
241
+ ]);
242
+ const keys = Object.keys(candidate);
243
+ const hasOrder = Object.prototype.hasOwnProperty.call(candidate, 'order');
244
+ return keys.length === messageDeltaRequiredKeys.length + (hasOrder ? 1 : 0)
245
+ && keys.every((key) => messageDeltaKeys.has(key))
246
+ && messageDeltaRequiredKeys.every((key) => (
247
+ Object.prototype.hasOwnProperty.call(candidate, key)
248
+ ))
249
+ && isQualifiedRuntimeId(candidate.messageId, harnessIds)
250
+ && isQualifiedRuntimeId(candidate.partId, harnessIds)
251
+ && typeof candidate.delta === 'string'
252
+ && candidate.delta.length > 0
253
+ && textEncoder.encode(candidate.delta).byteLength
254
+ <= interfaces.controllerMaxLiveMessageEventBytes
255
+ && Number.isSafeInteger(candidate.baseTextUtf8Bytes)
256
+ && (candidate.baseTextUtf8Bytes ?? -1) >= 0
257
+ && Number.isSafeInteger(candidate.textUtf8Bytes)
258
+ && (candidate.textUtf8Bytes ?? -1) > (candidate.baseTextUtf8Bytes ?? -1)
259
+ && hasExactDeltaUtf8Coordinates(
260
+ candidate.delta,
261
+ candidate.baseTextUtf8Bytes!,
262
+ candidate.textUtf8Bytes!,
263
+ )
264
+ && (!hasOrder || isTranscriptOrder(candidate.order))
265
+ && (candidate.sessionId.harnessId !== 'flex' || hasOrder)
266
+ && (candidate.sessionId.harnessId !== 'opencode' || !hasOrder)
267
+ && typeof candidate.sourceUpdatedAt === 'number'
268
+ && Number.isFinite(candidate.sourceUpdatedAt)
269
+ && Number.isSafeInteger(candidate.revision)
270
+ && (candidate.revision ?? -1) >= 0
271
+ && Number.isSafeInteger(candidate.streamEpoch)
272
+ && (candidate.streamEpoch ?? -1) >= 0;
273
+ };
274
+
210
275
  export interface IControllerSocketClientOptions {
211
276
  onStatus: (status: plugins.typedsocket.TConnectionStatus) => void;
212
277
  onControllerEvent: (event: interfaces.IControllerEvent) => void | Promise<void>;
@@ -214,18 +279,11 @@ export interface IControllerSocketClientOptions {
214
279
  onTerminalOutput?: (
215
280
  output: interfaces.IReq_ControllerTerminalOutput['request'],
216
281
  ) => void | Promise<void>;
217
- onBrowserViewEvent?: (
218
- event: TControllerBrowserViewEventMessage,
219
- ) => void | Promise<void>;
220
282
  /** Fires when the transport fail-closes the connection (e.g. frame limits). */
221
283
  onTransportClose?: (closeCode: number, reason: string) => void;
284
+ onReconnectExhausted?: (attempts: number, endpoint: string) => void;
222
285
  }
223
286
 
224
- export type TControllerBrowserViewEventMessage =
225
- plugins.typedrequest.TReverseVirtualStreamDirections<
226
- interfaces.IReq_ControllerBrowserViewEvent['request']
227
- >;
228
-
229
287
  export interface IControllerSocketRequestOptions
230
288
  extends plugins.typedrequest.ITypedRequestFireOptions {
231
289
  abortSignal?: AbortSignal;
@@ -240,9 +298,18 @@ export const isControllerEvent = (valueArg: unknown): valueArg is interfaces.ICo
240
298
  const hasSessionMarker = candidate.sessionStatus !== undefined || candidate.sessionError !== undefined;
241
299
  const hasToolExecution = candidate.toolExecution !== undefined;
242
300
  const hasReasoningUpdate = candidate.reasoningUpdate !== undefined;
301
+ const hasReasoningDelta = candidate.reasoningDelta !== undefined;
243
302
  const hasTextUpdate = candidate.textUpdate !== undefined;
303
+ const hasTextDelta = candidate.textDelta !== undefined;
244
304
  const hasDraftUpdate = candidate.sessionDraftUpdate !== undefined;
245
305
  const hasUpgrade = candidate.upgrade !== undefined;
306
+ const livePayloadCount = [
307
+ hasToolExecution,
308
+ hasReasoningUpdate,
309
+ hasReasoningDelta,
310
+ hasTextUpdate,
311
+ hasTextDelta,
312
+ ].filter(Boolean).length;
246
313
  return (
247
314
  typeof candidate.type === 'string'
248
315
  && controllerEventTypes.has(candidate.type as interfaces.TControllerEventType)
@@ -269,14 +336,19 @@ export const isControllerEvent = (valueArg: unknown): valueArg is interfaces.ICo
269
336
  && (candidate.toolExecution === undefined || isControllerToolExecution(candidate.toolExecution))
270
337
  && (candidate.reasoningUpdate === undefined
271
338
  || isControllerReasoningUpdate(candidate.reasoningUpdate))
339
+ && (candidate.reasoningDelta === undefined
340
+ || isControllerMessageDelta(candidate.reasoningDelta))
272
341
  && (candidate.textUpdate === undefined || isControllerTextUpdate(candidate.textUpdate))
342
+ && (candidate.textDelta === undefined || isControllerMessageDelta(candidate.textDelta))
273
343
  && (candidate.sessionDraftUpdate === undefined
274
344
  || isSessionDraftUpdate(candidate.sessionDraftUpdate))
345
+ && livePayloadCount <= 1
275
346
  && (!hasToolExecution || serializedBytesWithin(
276
347
  candidate,
277
348
  interfaces.controllerMaxToolEventBytes,
278
349
  ))
279
- && (!(hasReasoningUpdate || hasTextUpdate) || serializedBytesWithin(
350
+ && (!(hasReasoningUpdate || hasReasoningDelta || hasTextUpdate || hasTextDelta)
351
+ || serializedBytesWithin(
280
352
  candidate,
281
353
  interfaces.controllerMaxLiveMessageEventBytes,
282
354
  ))
@@ -312,6 +384,16 @@ export const isControllerEvent = (valueArg: unknown): valueArg is interfaces.ICo
312
384
  === interfaces.controllerRuntimeIdKey(candidate.reasoningUpdate!.sessionId)
313
385
  ))
314
386
  && (hasReasoningUpdate || candidate.type !== 'session.reasoning.updated')
387
+ && (!hasReasoningDelta || (
388
+ candidate.type === 'session.reasoning.delta'
389
+ && (candidate.harnessId === 'opencode' || candidate.harnessId === 'flex')
390
+ && typeof candidate.projectId === 'string'
391
+ && isQualifiedRuntimeId(candidate.sessionId, sessionHarnessIds)
392
+ && candidate.harnessId === candidate.reasoningDelta!.sessionId.harnessId
393
+ && interfaces.controllerRuntimeIdKey(candidate.sessionId)
394
+ === interfaces.controllerRuntimeIdKey(candidate.reasoningDelta!.sessionId)
395
+ ))
396
+ && (hasReasoningDelta || candidate.type !== 'session.reasoning.delta')
315
397
  && (!hasTextUpdate || (
316
398
  candidate.type === 'session.text.updated'
317
399
  && (candidate.harnessId === 'opencode' || candidate.harnessId === 'flex')
@@ -322,6 +404,16 @@ export const isControllerEvent = (valueArg: unknown): valueArg is interfaces.ICo
322
404
  === interfaces.controllerRuntimeIdKey(candidate.textUpdate!.sessionId)
323
405
  ))
324
406
  && (hasTextUpdate || candidate.type !== 'session.text.updated')
407
+ && (!hasTextDelta || (
408
+ candidate.type === 'session.text.delta'
409
+ && (candidate.harnessId === 'opencode' || candidate.harnessId === 'flex')
410
+ && typeof candidate.projectId === 'string'
411
+ && isQualifiedRuntimeId(candidate.sessionId, sessionHarnessIds)
412
+ && candidate.harnessId === candidate.textDelta!.sessionId.harnessId
413
+ && interfaces.controllerRuntimeIdKey(candidate.sessionId)
414
+ === interfaces.controllerRuntimeIdKey(candidate.textDelta!.sessionId)
415
+ ))
416
+ && (hasTextDelta || candidate.type !== 'session.text.delta')
325
417
  && (!hasDraftUpdate || (
326
418
  candidate.type === 'session.draft.changed'
327
419
  && typeof candidate.projectId === 'string'
@@ -337,7 +429,9 @@ export const isControllerEvent = (valueArg: unknown): valueArg is interfaces.ICo
337
429
  candidate.type === 'harness.changed'
338
430
  && (candidate.harnessId === 'opencode' || candidate.harnessId === 'flex')
339
431
  && !hasReasoningUpdate
432
+ && !hasReasoningDelta
340
433
  && !hasTextUpdate
434
+ && !hasTextDelta
341
435
  ))
342
436
  && (candidate.upgrade === undefined || (
343
437
  typeof candidate.upgrade === 'object'
@@ -345,10 +439,10 @@ export const isControllerEvent = (valueArg: unknown): valueArg is interfaces.ICo
345
439
  && Object.keys(candidate.upgrade).length === 3
346
440
  && typeof candidate.upgrade.fromVersion === 'string'
347
441
  && candidate.upgrade.fromVersion.length > 0
348
- && new TextEncoder().encode(candidate.upgrade.fromVersion).byteLength <= 128
442
+ && textEncoder.encode(candidate.upgrade.fromVersion).byteLength <= 128
349
443
  && typeof candidate.upgrade.toVersion === 'string'
350
444
  && candidate.upgrade.toVersion.length > 0
351
- && new TextEncoder().encode(candidate.upgrade.toVersion).byteLength <= 128
445
+ && textEncoder.encode(candidate.upgrade.toVersion).byteLength <= 128
352
446
  && ['preparing', 'pausing', 'installing', 'restarting', 'continuing', 'completed', 'failed']
353
447
  .includes(candidate.upgrade.phase)
354
448
  ))
@@ -401,15 +495,6 @@ export class ControllerSocketClient {
401
495
  },
402
496
  ),
403
497
  );
404
- this.typedRouter.addTypedHandler(
405
- new plugins.typedrequest.TypedHandler<interfaces.IReq_ControllerBrowserViewEvent>(
406
- 'controller.browser.view.event',
407
- async (eventArg) => {
408
- await this.options.onBrowserViewEvent?.(eventArg);
409
- return {};
410
- },
411
- ),
412
- );
413
498
  }
414
499
 
415
500
  public get status(): plugins.typedsocket.TConnectionStatus {
@@ -507,6 +592,11 @@ export class ControllerSocketClient {
507
592
  diagnosticArg.closeCode,
508
593
  typeof diagnosticArg.reason === 'string' ? diagnosticArg.reason : '',
509
594
  );
595
+ } else if (diagnosticArg.kind === 'reconnectExhausted') {
596
+ this.options.onReconnectExhausted?.(
597
+ diagnosticArg.attempts,
598
+ diagnosticArg.endpoint,
599
+ );
510
600
  }
511
601
  });
512
602