agl 22.0.4 → 23.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.
- package/changelog.md +10 -0
- package/dist_serve/bundle.js +2414 -2407
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.controller.d.ts +2 -0
- package/dist_ts/classes.controller.js +499 -22
- package/dist_ts/classes.flexservice.d.ts +5 -0
- package/dist_ts/classes.flexservice.js +131 -4
- package/dist_ts/classes.opencodeclient.d.ts +2 -0
- package/dist_ts/classes.opencodeclient.js +23 -1
- package/dist_ts/interfaces.flexipc.d.ts +18 -1
- package/dist_ts/interfaces.flexipc.js +42 -8
- package/dist_ts_interfaces/interfaces.d.ts +33 -2
- package/dist_ts_interfaces/interfaces.js +5 -2
- package/package.json +3 -3
- package/readme.md +6 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.controller.ts +589 -22
- package/ts/classes.flexservice.ts +132 -2
- package/ts/classes.opencodeclient.ts +19 -0
- package/ts/interfaces.flexipc.ts +65 -7
- package/ts_interfaces/interfaces.ts +36 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/classes.controllersocketclient.ts +113 -12
- package/ts_web/elements.harnesscontrollerapp.ts +1272 -167
|
@@ -64,7 +64,9 @@ import {
|
|
|
64
64
|
flexIpcMaximumPromptQueueEntries,
|
|
65
65
|
flexIpcMaximumQueuedSessionChangeBytes,
|
|
66
66
|
flexIpcMaximumQueuedSessionChanges,
|
|
67
|
+
flexIpcMaximumDeltaBytes,
|
|
67
68
|
flexIpcProtocolVersion,
|
|
69
|
+
flexIpcTargetDeltaBytes,
|
|
68
70
|
isFlexHostResponseResult,
|
|
69
71
|
} from './interfaces.flexipc.js';
|
|
70
72
|
import { openCodeOAuthAuthFromProviderCredential } from './functions.opencodeauth.js';
|
|
@@ -78,6 +80,24 @@ type TFlexHarnessEvent = plugins.flexharness.TFlexHarnessEvent;
|
|
|
78
80
|
|
|
79
81
|
const flexFailureStatusTimeoutMs = 1_000;
|
|
80
82
|
|
|
83
|
+
const fragmentUtf8 = (valueArg: string, targetBytesArg: number): string[] => {
|
|
84
|
+
const fragments: string[] = [];
|
|
85
|
+
let fragment = '';
|
|
86
|
+
let fragmentBytes = 0;
|
|
87
|
+
for (const codePoint of valueArg) {
|
|
88
|
+
const codePointBytes = Buffer.byteLength(codePoint, 'utf8');
|
|
89
|
+
if (fragment && fragmentBytes + codePointBytes > targetBytesArg) {
|
|
90
|
+
fragments.push(fragment);
|
|
91
|
+
fragment = '';
|
|
92
|
+
fragmentBytes = 0;
|
|
93
|
+
}
|
|
94
|
+
fragment += codePoint;
|
|
95
|
+
fragmentBytes += codePointBytes;
|
|
96
|
+
}
|
|
97
|
+
if (fragment) fragments.push(fragment);
|
|
98
|
+
return fragments;
|
|
99
|
+
};
|
|
100
|
+
|
|
81
101
|
const waitForFlexServiceDeadline = async <TValue>(
|
|
82
102
|
promiseArg: Promise<TValue>,
|
|
83
103
|
timeoutMsArg: number,
|
|
@@ -452,7 +472,9 @@ export class FlexService {
|
|
|
452
472
|
private browserFrameReservationCount = 0;
|
|
453
473
|
private browserFrameReservationBytes = 0;
|
|
454
474
|
private readonly sessionChangeQueue = new Map<string, TFlexChildEvent>();
|
|
475
|
+
private sessionDeltaTailKeys = new Map<string, string>();
|
|
455
476
|
private sessionChangeQueueBytes = 0;
|
|
477
|
+
private sessionChangeQueueTailKey: string | undefined;
|
|
456
478
|
private sessionChangeFlushRunning = false;
|
|
457
479
|
private hostRequestAdmissionOpen = true;
|
|
458
480
|
private admissionOpen = true;
|
|
@@ -2517,9 +2539,28 @@ export class FlexService {
|
|
|
2517
2539
|
part: eventArg.part,
|
|
2518
2540
|
});
|
|
2519
2541
|
}
|
|
2542
|
+
if (eventArg.type === 'part.delta') {
|
|
2543
|
+
this.queuePartDelta({
|
|
2544
|
+
type: 'part.delta',
|
|
2545
|
+
scopeId: eventArg.scopeId,
|
|
2546
|
+
sessionId: eventArg.sessionId,
|
|
2547
|
+
runId: eventArg.runId,
|
|
2548
|
+
messageId: eventArg.messageId,
|
|
2549
|
+
messageIndex: eventArg.messageIndex,
|
|
2550
|
+
partId: eventArg.partId,
|
|
2551
|
+
partIndex: eventArg.partIndex,
|
|
2552
|
+
sequence: eventArg.sequence,
|
|
2553
|
+
timestamp: eventArg.timestamp,
|
|
2554
|
+
partType: eventArg.partType,
|
|
2555
|
+
delta: eventArg.delta,
|
|
2556
|
+
baseTextUtf8Bytes: eventArg.baseTextUtf8Bytes,
|
|
2557
|
+
textUtf8Bytes: eventArg.textUtf8Bytes,
|
|
2558
|
+
});
|
|
2559
|
+
this.queueSessionChange(eventArg.scopeId, eventArg.sessionId);
|
|
2560
|
+
return;
|
|
2561
|
+
}
|
|
2520
2562
|
if (
|
|
2521
2563
|
(eventArg.type === 'part.started'
|
|
2522
|
-
|| eventArg.type === 'part.delta'
|
|
2523
2564
|
|| eventArg.type === 'part.completed')
|
|
2524
2565
|
&& eventArg.part.type === 'reasoning'
|
|
2525
2566
|
) {
|
|
@@ -2538,7 +2579,6 @@ export class FlexService {
|
|
|
2538
2579
|
}
|
|
2539
2580
|
if (
|
|
2540
2581
|
(eventArg.type === 'part.started'
|
|
2541
|
-
|| eventArg.type === 'part.delta'
|
|
2542
2582
|
|| eventArg.type === 'part.completed')
|
|
2543
2583
|
&& eventArg.part.type === 'text'
|
|
2544
2584
|
) {
|
|
@@ -2949,10 +2989,23 @@ export class FlexService {
|
|
|
2949
2989
|
}), 'utf8');
|
|
2950
2990
|
}
|
|
2951
2991
|
|
|
2992
|
+
private getSessionDeltaTailKeys(): Map<string, string> {
|
|
2993
|
+
this.sessionDeltaTailKeys ??= new Map<string, string>();
|
|
2994
|
+
return this.sessionDeltaTailKeys;
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2952
2997
|
private deleteSessionChangeQueueEntry(keyArg: string): void {
|
|
2953
2998
|
const existing = this.sessionChangeQueue.get(keyArg);
|
|
2954
2999
|
if (!existing) return;
|
|
2955
3000
|
this.sessionChangeQueue.delete(keyArg);
|
|
3001
|
+
const deltaTailKeys = this.getSessionDeltaTailKeys();
|
|
3002
|
+
for (const [partKey, tailKey] of deltaTailKeys) {
|
|
3003
|
+
if (tailKey === keyArg) deltaTailKeys.delete(partKey);
|
|
3004
|
+
}
|
|
3005
|
+
if (this.sessionChangeQueueTailKey === keyArg) {
|
|
3006
|
+
this.sessionChangeQueueTailKey = undefined;
|
|
3007
|
+
for (const key of this.sessionChangeQueue.keys()) this.sessionChangeQueueTailKey = key;
|
|
3008
|
+
}
|
|
2956
3009
|
this.sessionChangeQueueBytes = Math.max(
|
|
2957
3010
|
0,
|
|
2958
3011
|
(this.sessionChangeQueueBytes ?? 0) - this.sessionChangeEventBytes(existing),
|
|
@@ -2961,7 +3014,9 @@ export class FlexService {
|
|
|
2961
3014
|
|
|
2962
3015
|
private clearSessionChangeQueue(): void {
|
|
2963
3016
|
this.sessionChangeQueue.clear();
|
|
3017
|
+
this.getSessionDeltaTailKeys().clear();
|
|
2964
3018
|
this.sessionChangeQueueBytes = 0;
|
|
3019
|
+
this.sessionChangeQueueTailKey = undefined;
|
|
2965
3020
|
}
|
|
2966
3021
|
|
|
2967
3022
|
private setSessionChangeQueueEntry(keyArg: string, eventArg: TFlexChildEvent): boolean {
|
|
@@ -2978,6 +3033,7 @@ export class FlexService {
|
|
|
2978
3033
|
|| nextBytes > flexIpcMaximumQueuedSessionChangeBytes
|
|
2979
3034
|
) return false;
|
|
2980
3035
|
this.sessionChangeQueue.set(keyArg, eventArg);
|
|
3036
|
+
if (!existing) this.sessionChangeQueueTailKey = keyArg;
|
|
2981
3037
|
this.sessionChangeQueueBytes = nextBytes;
|
|
2982
3038
|
return true;
|
|
2983
3039
|
}
|
|
@@ -2987,6 +3043,7 @@ export class FlexService {
|
|
|
2987
3043
|
this.clearSessionChangeQueue();
|
|
2988
3044
|
this.sessionChangeQueue.set('*', event);
|
|
2989
3045
|
this.sessionChangeQueueBytes = this.sessionChangeEventBytes(event);
|
|
3046
|
+
this.sessionChangeQueueTailKey = '*';
|
|
2990
3047
|
}
|
|
2991
3048
|
|
|
2992
3049
|
private queueToolChange(eventArg: Extract<TFlexChildEvent, {
|
|
@@ -3026,6 +3083,78 @@ export class FlexService {
|
|
|
3026
3083
|
this.startSessionChangeFlush();
|
|
3027
3084
|
}
|
|
3028
3085
|
|
|
3086
|
+
private queuePartDelta(eventArg: Extract<TFlexChildEvent, { type: 'part.delta' }>): void {
|
|
3087
|
+
if (this.disposing || this.disposed || this.sessionChangeQueue.has('*')) return;
|
|
3088
|
+
const encodedDeltaBytes = Buffer.byteLength(eventArg.delta, 'utf8');
|
|
3089
|
+
const boundaryCorrection = eventArg.textUtf8Bytes
|
|
3090
|
+
- eventArg.baseTextUtf8Bytes
|
|
3091
|
+
- encodedDeltaBytes;
|
|
3092
|
+
const fragments = fragmentUtf8(eventArg.delta, flexIpcTargetDeltaBytes);
|
|
3093
|
+
if (fragments.length === 0) {
|
|
3094
|
+
this.collapseSessionChangeQueue();
|
|
3095
|
+
this.startSessionChangeFlush();
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
3098
|
+
let baseTextUtf8Bytes = eventArg.baseTextUtf8Bytes;
|
|
3099
|
+
for (const [fragmentIndex, fragment] of fragments.entries()) {
|
|
3100
|
+
const textUtf8Bytes = baseTextUtf8Bytes
|
|
3101
|
+
+ Buffer.byteLength(fragment, 'utf8')
|
|
3102
|
+
+ (fragmentIndex === 0 ? boundaryCorrection : 0);
|
|
3103
|
+
if (textUtf8Bytes <= baseTextUtf8Bytes) {
|
|
3104
|
+
this.collapseSessionChangeQueue();
|
|
3105
|
+
this.startSessionChangeFlush();
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
3108
|
+
const fragmentEvent: Extract<TFlexChildEvent, { type: 'part.delta' }> = {
|
|
3109
|
+
...eventArg,
|
|
3110
|
+
delta: fragment,
|
|
3111
|
+
baseTextUtf8Bytes,
|
|
3112
|
+
textUtf8Bytes,
|
|
3113
|
+
};
|
|
3114
|
+
if (!this.enqueuePartDeltaFragment(fragmentEvent)) {
|
|
3115
|
+
this.collapseSessionChangeQueue();
|
|
3116
|
+
this.startSessionChangeFlush();
|
|
3117
|
+
return;
|
|
3118
|
+
}
|
|
3119
|
+
baseTextUtf8Bytes = textUtf8Bytes;
|
|
3120
|
+
}
|
|
3121
|
+
if (baseTextUtf8Bytes !== eventArg.textUtf8Bytes) this.collapseSessionChangeQueue();
|
|
3122
|
+
this.startSessionChangeFlush();
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3125
|
+
private enqueuePartDeltaFragment(
|
|
3126
|
+
eventArg: Extract<TFlexChildEvent, { type: 'part.delta' }>,
|
|
3127
|
+
): boolean {
|
|
3128
|
+
const partKey = `${eventArg.scopeId}\0${eventArg.sessionId}\0${eventArg.partType}\0${eventArg.partId}`;
|
|
3129
|
+
const deltaTailKeys = this.getSessionDeltaTailKeys();
|
|
3130
|
+
const tailKey = deltaTailKeys.get(partKey);
|
|
3131
|
+
const tail = tailKey ? this.sessionChangeQueue.get(tailKey) : undefined;
|
|
3132
|
+
if (
|
|
3133
|
+
tail?.type === 'part.delta'
|
|
3134
|
+
&& tailKey === this.sessionChangeQueueTailKey
|
|
3135
|
+
&& tail.textUtf8Bytes === eventArg.baseTextUtf8Bytes
|
|
3136
|
+
&& tail.runId === eventArg.runId
|
|
3137
|
+
&& tail.messageId === eventArg.messageId
|
|
3138
|
+
&& tail.messageIndex === eventArg.messageIndex
|
|
3139
|
+
&& tail.partIndex === eventArg.partIndex
|
|
3140
|
+
&& Buffer.byteLength(tail.delta, 'utf8') + Buffer.byteLength(eventArg.delta, 'utf8')
|
|
3141
|
+
<= flexIpcMaximumDeltaBytes
|
|
3142
|
+
) {
|
|
3143
|
+
const merged: Extract<TFlexChildEvent, { type: 'part.delta' }> = {
|
|
3144
|
+
...tail,
|
|
3145
|
+
sequence: eventArg.sequence,
|
|
3146
|
+
timestamp: eventArg.timestamp,
|
|
3147
|
+
delta: `${tail.delta}${eventArg.delta}`,
|
|
3148
|
+
textUtf8Bytes: eventArg.textUtf8Bytes,
|
|
3149
|
+
};
|
|
3150
|
+
return this.setSessionChangeQueueEntry(tailKey!, merged);
|
|
3151
|
+
}
|
|
3152
|
+
const key = `${partKey}\0${eventArg.baseTextUtf8Bytes}\0${eventArg.sequence}`;
|
|
3153
|
+
if (!this.setSessionChangeQueueEntry(key, eventArg)) return false;
|
|
3154
|
+
deltaTailKeys.set(partKey, key);
|
|
3155
|
+
return true;
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3029
3158
|
|
|
3030
3159
|
private queueSessionChange(scopeIdArg: string, sessionIdArg: string): void {
|
|
3031
3160
|
const globalKey = '*';
|
|
@@ -3069,6 +3198,7 @@ export class FlexService {
|
|
|
3069
3198
|
}
|
|
3070
3199
|
} catch {
|
|
3071
3200
|
this.clearSessionChangeQueue();
|
|
3201
|
+
await this.closeTransport().catch(() => undefined);
|
|
3072
3202
|
} finally {
|
|
3073
3203
|
this.sessionChangeFlushRunning = false;
|
|
3074
3204
|
if (this.sessionChangeQueue.size > 0 && !this.disposing && !this.disposed) {
|
|
@@ -54,6 +54,7 @@ const maxLiveToolExecutionBytes = controllerMaxToolEventBytes - (8 * 1024);
|
|
|
54
54
|
const maxLiveMessageSnapshotBytes = controllerMaxLiveMessageEventBytes - (8 * 1024);
|
|
55
55
|
const maxLiveMessagePartCacheEntries = 512;
|
|
56
56
|
const maxLiveMessagePartCacheBytes = 4 * 1024 * 1024;
|
|
57
|
+
const maxLiveMessageEventIds = 4_096;
|
|
57
58
|
const maxMetadataStringBytes = 16 * 1024;
|
|
58
59
|
const maxMetadataNodes = 4096;
|
|
59
60
|
const maxCollectionEntries = 512;
|
|
@@ -1816,6 +1817,7 @@ export class OpenCodeClientAdapter {
|
|
|
1816
1817
|
private readonly sessionMetricsCache = new Map<string, ISessionMetricsCacheEntry>();
|
|
1817
1818
|
private readonly liveMessagePartCache = new Map<string, IOpenCodeLiveMessagePartCacheEntry>();
|
|
1818
1819
|
private liveMessagePartCacheBytes = 0;
|
|
1820
|
+
private readonly liveMessageEventIds = new Set<string>();
|
|
1819
1821
|
|
|
1820
1822
|
constructor(options: IOpenCodeClientOptions) {
|
|
1821
1823
|
this.directory = plugins.fs.realpathSync.native(options.directory);
|
|
@@ -3651,6 +3653,21 @@ export class OpenCodeClientAdapter {
|
|
|
3651
3653
|
private clearLiveMessagePartCache(): void {
|
|
3652
3654
|
this.liveMessagePartCache.clear();
|
|
3653
3655
|
this.liveMessagePartCacheBytes = 0;
|
|
3656
|
+
this.liveMessageEventIds.clear();
|
|
3657
|
+
}
|
|
3658
|
+
|
|
3659
|
+
private acceptLiveMessageEventId(payloadArg: Record<string, unknown>): boolean {
|
|
3660
|
+
if (typeof payloadArg.id !== 'string' || Buffer.byteLength(payloadArg.id, 'utf8') > 512) {
|
|
3661
|
+
return true;
|
|
3662
|
+
}
|
|
3663
|
+
if (this.liveMessageEventIds.has(payloadArg.id)) return false;
|
|
3664
|
+
this.liveMessageEventIds.add(payloadArg.id);
|
|
3665
|
+
while (this.liveMessageEventIds.size > maxLiveMessageEventIds) {
|
|
3666
|
+
const oldest = this.liveMessageEventIds.values().next().value;
|
|
3667
|
+
if (oldest === undefined) break;
|
|
3668
|
+
this.liveMessageEventIds.delete(oldest);
|
|
3669
|
+
}
|
|
3670
|
+
return true;
|
|
3654
3671
|
}
|
|
3655
3672
|
|
|
3656
3673
|
private clearLiveMessagePartsForSession(directoryArg: string, sessionIdArg: string): void {
|
|
@@ -3743,6 +3760,7 @@ export class OpenCodeClientAdapter {
|
|
|
3743
3760
|
};
|
|
3744
3761
|
|
|
3745
3762
|
if (payload.type === 'message.part.updated') {
|
|
3763
|
+
if (!this.acceptLiveMessageEventId(payload)) return undefined;
|
|
3746
3764
|
if (!isRecord(properties.part)) {
|
|
3747
3765
|
this.clearLiveMessagePartCache();
|
|
3748
3766
|
return {
|
|
@@ -3819,6 +3837,7 @@ export class OpenCodeClientAdapter {
|
|
|
3819
3837
|
}
|
|
3820
3838
|
|
|
3821
3839
|
if (payload.type === 'message.part.delta') {
|
|
3840
|
+
if (!this.acceptLiveMessageEventId(payload)) return undefined;
|
|
3822
3841
|
try {
|
|
3823
3842
|
const validatedSessionId = requireString(
|
|
3824
3843
|
properties.sessionID,
|
package/ts/interfaces.flexipc.ts
CHANGED
|
@@ -32,8 +32,10 @@ export type TFlexTurnReversionCaptureInspectionV2 =
|
|
|
32
32
|
export type TFlexTurnReversionApplyInspection =
|
|
33
33
|
plugins.flexharness.IFlexTurnReversionApplyInspection;
|
|
34
34
|
|
|
35
|
-
export const flexIpcProtocolVersion =
|
|
35
|
+
export const flexIpcProtocolVersion = 15 as const;
|
|
36
36
|
export const flexIpcMaximumFrameBytes = 2 * 1024 * 1024;
|
|
37
|
+
export const flexIpcTargetDeltaBytes = 16 * 1024;
|
|
38
|
+
export const flexIpcMaximumDeltaBytes = 32 * 1024;
|
|
37
39
|
export const flexIpcMaximumPendingRequests = 64;
|
|
38
40
|
export const flexIpcMaximumBrowserChannels = 128;
|
|
39
41
|
export const flexIpcMaximumBrowserFrameBytes = 256 * 1024 + 4;
|
|
@@ -764,6 +766,22 @@ export type TFlexChildEvent =
|
|
|
764
766
|
status: 'running' | 'completed';
|
|
765
767
|
part: TFlexTextMessagePart;
|
|
766
768
|
}
|
|
769
|
+
| {
|
|
770
|
+
type: 'part.delta';
|
|
771
|
+
scopeId: string;
|
|
772
|
+
sessionId: string;
|
|
773
|
+
runId: string;
|
|
774
|
+
messageId: string;
|
|
775
|
+
messageIndex: number;
|
|
776
|
+
partId: string;
|
|
777
|
+
partIndex: number;
|
|
778
|
+
sequence: number;
|
|
779
|
+
timestamp: string;
|
|
780
|
+
partType: 'text' | 'reasoning';
|
|
781
|
+
delta: string;
|
|
782
|
+
baseTextUtf8Bytes: number;
|
|
783
|
+
textUtf8Bytes: number;
|
|
784
|
+
}
|
|
767
785
|
| {
|
|
768
786
|
type: 'prompt.queued';
|
|
769
787
|
scopeId: string;
|
|
@@ -945,6 +963,20 @@ const isBoundedString = (
|
|
|
945
963
|
&& (allowEmptyArg || valueArg.length > 0)
|
|
946
964
|
&& Buffer.byteLength(valueArg, 'utf8') <= maximumBytesArg;
|
|
947
965
|
|
|
966
|
+
const hasExactDeltaUtf8Coordinates = (
|
|
967
|
+
deltaArg: string,
|
|
968
|
+
baseTextUtf8BytesArg: number,
|
|
969
|
+
textUtf8BytesArg: number,
|
|
970
|
+
): boolean => {
|
|
971
|
+
const encodedDeltaBytes = Buffer.byteLength(deltaArg, 'utf8');
|
|
972
|
+
const increment = textUtf8BytesArg - baseTextUtf8BytesArg;
|
|
973
|
+
if (increment === encodedDeltaBytes) return true;
|
|
974
|
+
const firstCodeUnit = deltaArg.charCodeAt(0);
|
|
975
|
+
return firstCodeUnit >= 0xDC00
|
|
976
|
+
&& firstCodeUnit <= 0xDFFF
|
|
977
|
+
&& increment === encodedDeltaBytes - 2;
|
|
978
|
+
};
|
|
979
|
+
|
|
948
980
|
const isIdentifier = (valueArg: unknown): valueArg is string =>
|
|
949
981
|
isBoundedString(valueArg, 512) && identifierPattern.test(valueArg);
|
|
950
982
|
|
|
@@ -1244,15 +1276,15 @@ const isUsage = (valueArg: unknown): boolean => isFlexPlainObject(valueArg)
|
|
|
1244
1276
|
])
|
|
1245
1277
|
&& Object.values(valueArg).every(isNonNegativeInteger);
|
|
1246
1278
|
|
|
1247
|
-
const isMessagePart = (valueArg: unknown): boolean => {
|
|
1279
|
+
const isMessagePart = (valueArg: unknown, textByteLimitArg = 96 * 1024): boolean => {
|
|
1248
1280
|
if (!isFlexPlainObject(valueArg) || !isIdentifier(valueArg.partId)) return false;
|
|
1249
1281
|
switch (valueArg.type) {
|
|
1250
1282
|
case 'text':
|
|
1251
1283
|
return hasFlexExactKeys(valueArg, ['partId', 'type', 'text'])
|
|
1252
|
-
&& isBoundedString(valueArg.text,
|
|
1284
|
+
&& isBoundedString(valueArg.text, textByteLimitArg, true);
|
|
1253
1285
|
case 'reasoning':
|
|
1254
1286
|
return hasFlexExactKeys(valueArg, ['partId', 'type', 'text', 'status'])
|
|
1255
|
-
&& isBoundedString(valueArg.text,
|
|
1287
|
+
&& isBoundedString(valueArg.text, textByteLimitArg, true)
|
|
1256
1288
|
&& ['running', 'completed', 'cancelled'].includes(String(valueArg.status));
|
|
1257
1289
|
case 'tool':
|
|
1258
1290
|
return hasFlexExactKeys(
|
|
@@ -1302,7 +1334,7 @@ export const isFlexPublicMessage = (valueArg: unknown): valueArg is TFlexMessage
|
|
|
1302
1334
|
&& (valueArg.completedAt === undefined || isIsoDate(valueArg.completedAt))
|
|
1303
1335
|
&& Array.isArray(valueArg.parts)
|
|
1304
1336
|
&& valueArg.parts.length <= 2000
|
|
1305
|
-
&& valueArg.parts.every(isMessagePart)
|
|
1337
|
+
&& valueArg.parts.every((partArg) => isMessagePart(partArg))
|
|
1306
1338
|
&& (valueArg.model === undefined || isModelIdentity(valueArg.model))
|
|
1307
1339
|
&& (valueArg.usage === undefined || isUsage(valueArg.usage))
|
|
1308
1340
|
&& (valueArg.error === undefined || isBoundedString(valueArg.error, 2048));
|
|
@@ -2339,7 +2371,7 @@ const isFlexChildEvent = (valueArg: unknown): valueArg is TFlexChildEvent => {
|
|
|
2339
2371
|
&& isIsoDate(valueArg.timestamp)
|
|
2340
2372
|
&& isFlexPlainObject(valueArg.part)
|
|
2341
2373
|
&& valueArg.part.type === 'reasoning'
|
|
2342
|
-
&& isMessagePart(valueArg.part);
|
|
2374
|
+
&& isMessagePart(valueArg.part, flexIpcMaximumFrameBytes - (16 * 1024));
|
|
2343
2375
|
case 'text.updated':
|
|
2344
2376
|
return hasFlexExactKeys(valueArg, [
|
|
2345
2377
|
'type', 'scopeId', 'sessionId', 'runId', 'messageId',
|
|
@@ -2357,7 +2389,33 @@ const isFlexChildEvent = (valueArg: unknown): valueArg is TFlexChildEvent => {
|
|
|
2357
2389
|
&& ['running', 'completed'].includes(String(valueArg.status))
|
|
2358
2390
|
&& isFlexPlainObject(valueArg.part)
|
|
2359
2391
|
&& valueArg.part.type === 'text'
|
|
2360
|
-
&& isMessagePart(valueArg.part);
|
|
2392
|
+
&& isMessagePart(valueArg.part, flexIpcMaximumFrameBytes - (16 * 1024));
|
|
2393
|
+
case 'part.delta':
|
|
2394
|
+
return hasFlexExactKeys(valueArg, [
|
|
2395
|
+
'type', 'scopeId', 'sessionId', 'runId', 'messageId', 'messageIndex', 'partId',
|
|
2396
|
+
'partIndex', 'sequence', 'timestamp', 'partType', 'delta',
|
|
2397
|
+
'baseTextUtf8Bytes', 'textUtf8Bytes',
|
|
2398
|
+
])
|
|
2399
|
+
&& isIdentifier(valueArg.scopeId)
|
|
2400
|
+
&& isIdentifier(valueArg.sessionId)
|
|
2401
|
+
&& isIdentifier(valueArg.runId)
|
|
2402
|
+
&& isIdentifier(valueArg.messageId)
|
|
2403
|
+
&& isIdentifier(valueArg.partId)
|
|
2404
|
+
&& isNonNegativeInteger(valueArg.messageIndex)
|
|
2405
|
+
&& isNonNegativeInteger(valueArg.partIndex)
|
|
2406
|
+
&& isNonNegativeInteger(valueArg.sequence)
|
|
2407
|
+
&& valueArg.sequence >= 1
|
|
2408
|
+
&& isIsoDate(valueArg.timestamp)
|
|
2409
|
+
&& (valueArg.partType === 'text' || valueArg.partType === 'reasoning')
|
|
2410
|
+
&& isBoundedString(valueArg.delta, flexIpcMaximumDeltaBytes)
|
|
2411
|
+
&& isNonNegativeInteger(valueArg.baseTextUtf8Bytes)
|
|
2412
|
+
&& isNonNegativeInteger(valueArg.textUtf8Bytes)
|
|
2413
|
+
&& valueArg.textUtf8Bytes > valueArg.baseTextUtf8Bytes
|
|
2414
|
+
&& hasExactDeltaUtf8Coordinates(
|
|
2415
|
+
valueArg.delta,
|
|
2416
|
+
valueArg.baseTextUtf8Bytes,
|
|
2417
|
+
valueArg.textUtf8Bytes,
|
|
2418
|
+
);
|
|
2361
2419
|
case 'prompt.queued':
|
|
2362
2420
|
case 'prompt.started':
|
|
2363
2421
|
case 'prompt.running':
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
|
|
3
|
-
export const controllerProtocolVersion =
|
|
3
|
+
export const controllerProtocolVersion = 23 as const;
|
|
4
4
|
export const controllerUpgradeManagementVersion = 2 as const;
|
|
5
5
|
export const controllerPackageName = 'agl' as const;
|
|
6
6
|
export const controllerMaxToolEventBytes = 48 * 1024;
|
|
7
7
|
export const controllerMaxLiveMessageEventBytes = 128 * 1024;
|
|
8
|
+
export const controllerLiveMessageDeltaTargetBytes = 16 * 1024;
|
|
8
9
|
export const controllerInitialMessageBundleLimit = 20;
|
|
9
10
|
export const controllerMaximumMessageBundles = 200;
|
|
10
11
|
export const controllerMaximumTranscriptBytes = 512 * 1024;
|
|
@@ -475,6 +476,34 @@ export interface IControllerTextUpdate {
|
|
|
475
476
|
streamEpoch: number;
|
|
476
477
|
}
|
|
477
478
|
|
|
479
|
+
export interface IControllerReasoningDelta {
|
|
480
|
+
sessionId: IControllerRuntimeId;
|
|
481
|
+
messageId: IControllerRuntimeId;
|
|
482
|
+
partId: IControllerRuntimeId;
|
|
483
|
+
delta: string;
|
|
484
|
+
baseTextUtf8Bytes: number;
|
|
485
|
+
textUtf8Bytes: number;
|
|
486
|
+
/** Authoritative source coordinates; required for Flex and absent for OpenCode. */
|
|
487
|
+
order?: IControllerTranscriptOrder;
|
|
488
|
+
sourceUpdatedAt: number;
|
|
489
|
+
revision: number;
|
|
490
|
+
streamEpoch: number;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export interface IControllerTextDelta {
|
|
494
|
+
sessionId: IControllerRuntimeId;
|
|
495
|
+
messageId: IControllerRuntimeId;
|
|
496
|
+
partId: IControllerRuntimeId;
|
|
497
|
+
delta: string;
|
|
498
|
+
baseTextUtf8Bytes: number;
|
|
499
|
+
textUtf8Bytes: number;
|
|
500
|
+
/** Authoritative source coordinates; required for Flex and absent for OpenCode. */
|
|
501
|
+
order?: IControllerTranscriptOrder;
|
|
502
|
+
sourceUpdatedAt: number;
|
|
503
|
+
revision: number;
|
|
504
|
+
streamEpoch: number;
|
|
505
|
+
}
|
|
506
|
+
|
|
478
507
|
export interface IControllerToolStreamCursor {
|
|
479
508
|
streamEpoch: number;
|
|
480
509
|
revision: number;
|
|
@@ -788,7 +817,9 @@ export const controllerEventTypes = [
|
|
|
788
817
|
'session.history.changed',
|
|
789
818
|
'session.tool.updated',
|
|
790
819
|
'session.reasoning.updated',
|
|
820
|
+
'session.reasoning.delta',
|
|
791
821
|
'session.text.updated',
|
|
822
|
+
'session.text.delta',
|
|
792
823
|
'permissions.changed',
|
|
793
824
|
'harness.changed',
|
|
794
825
|
'projects.changed',
|
|
@@ -813,8 +844,12 @@ export interface IControllerEvent {
|
|
|
813
844
|
toolExecution?: IControllerToolExecution;
|
|
814
845
|
/** Present only on a live OpenCode or Flex reasoning snapshot. */
|
|
815
846
|
reasoningUpdate?: IControllerReasoningUpdate;
|
|
847
|
+
/** Present only on an exact live OpenCode or Flex reasoning delta. */
|
|
848
|
+
reasoningDelta?: IControllerReasoningDelta;
|
|
816
849
|
/** Present only on a live OpenCode or Flex text snapshot. */
|
|
817
850
|
textUpdate?: IControllerTextUpdate;
|
|
851
|
+
/** Present only on an exact live OpenCode or Flex text delta. */
|
|
852
|
+
textDelta?: IControllerTextDelta;
|
|
818
853
|
/** Present only on a session.draft.changed event. */
|
|
819
854
|
sessionDraftUpdate?: IControllerSessionDraftUpdate;
|
|
820
855
|
/** Ordered harness stream barrier carried by harness.changed. */
|