@modelprofile.com/flexharness 5.2.0 → 5.3.1
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 +19 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.flexharness.d.ts +12 -0
- package/dist_ts/classes.flexharness.js +413 -32
- package/dist_ts/interfaces.d.ts +45 -3
- package/dist_ts/interfaces.js +1 -1
- package/dist_ts/utils.json.js +77 -2
- package/package.json +1 -1
- package/readme.hints.md +2 -2
- package/readme.md +50 -8
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.flexharness.ts +556 -32
- package/ts/interfaces.ts +52 -1
- package/ts/utils.json.ts +116 -1
package/ts/interfaces.ts
CHANGED
|
@@ -441,6 +441,7 @@ export interface IFlexSessionTombstone {
|
|
|
441
441
|
rootSessionId?: string;
|
|
442
442
|
depth?: number;
|
|
443
443
|
parentSessionId?: string;
|
|
444
|
+
subagent?: IFlexSubagentProvenance;
|
|
444
445
|
}
|
|
445
446
|
|
|
446
447
|
export interface IFlexScopeSnapshot {
|
|
@@ -582,6 +583,16 @@ export interface IFlexSessionGeneration {
|
|
|
582
583
|
sessionGenerationSequence: number;
|
|
583
584
|
}
|
|
584
585
|
|
|
586
|
+
export interface IFlexSubagentProvenance {
|
|
587
|
+
readonly parentSessionId: string;
|
|
588
|
+
readonly parentSessionGenerationId: string;
|
|
589
|
+
readonly parentSessionGenerationSequence: number;
|
|
590
|
+
readonly originParentRunId: string;
|
|
591
|
+
readonly originParentToolCallId: string;
|
|
592
|
+
readonly agent: string;
|
|
593
|
+
readonly depth: number;
|
|
594
|
+
}
|
|
595
|
+
|
|
585
596
|
export interface IFlexSessionGenerationCohortEntry extends IFlexSessionGeneration {
|
|
586
597
|
sessionId: string;
|
|
587
598
|
}
|
|
@@ -649,11 +660,15 @@ export interface IFlexProjectManagementWriteContext {
|
|
|
649
660
|
toolCallId?: string;
|
|
650
661
|
}
|
|
651
662
|
|
|
663
|
+
export interface IFlexProjectManagementSessionContext extends IFlexSessionGeneration {
|
|
664
|
+
readonly subagent?: Readonly<IFlexSubagentProvenance>;
|
|
665
|
+
}
|
|
666
|
+
|
|
652
667
|
export interface IFlexProjectManagementStore {
|
|
653
668
|
load(
|
|
654
669
|
storageKey: string,
|
|
655
670
|
sessionId: string,
|
|
656
|
-
|
|
671
|
+
sessionContext?: Readonly<IFlexProjectManagementSessionContext>,
|
|
657
672
|
): Promise<TFlexProjectManagementRecord | undefined>;
|
|
658
673
|
save(
|
|
659
674
|
storageKey: string,
|
|
@@ -667,6 +682,7 @@ export interface IFlexProjectManagementStore {
|
|
|
667
682
|
sessionId: string,
|
|
668
683
|
tombstone: IFlexProjectManagementTombstone,
|
|
669
684
|
expectedRevision: number,
|
|
685
|
+
sessionContext?: Readonly<IFlexProjectManagementSessionContext>,
|
|
670
686
|
): Promise<void>;
|
|
671
687
|
purgeNamespace(storageKey: string): Promise<void>;
|
|
672
688
|
}
|
|
@@ -944,6 +960,7 @@ export interface IFlexRedoSessionResult {
|
|
|
944
960
|
export type TFlexExternalErrorSource =
|
|
945
961
|
| 'modelResolver'
|
|
946
962
|
| 'toolProvider'
|
|
963
|
+
| 'delegatedRunAdmissionProvider'
|
|
947
964
|
| 'toolExecution'
|
|
948
965
|
| 'toolCallback'
|
|
949
966
|
| 'toolCleanup'
|
|
@@ -964,6 +981,39 @@ export type TFlexExternalErrorProjector = (
|
|
|
964
981
|
context: IFlexExternalErrorContext,
|
|
965
982
|
) => IFlexErrorInfo;
|
|
966
983
|
|
|
984
|
+
export interface IFlexDelegatedRunAdmissionContext<TScope> extends IFlexSessionGeneration {
|
|
985
|
+
readonly scopeId: string;
|
|
986
|
+
readonly scope: TScope;
|
|
987
|
+
readonly storageKey: string;
|
|
988
|
+
readonly sessionId: string;
|
|
989
|
+
readonly sessionGenerationId: string;
|
|
990
|
+
readonly sessionGenerationSequence: number;
|
|
991
|
+
readonly queueId: string;
|
|
992
|
+
readonly runId: string;
|
|
993
|
+
readonly parentSessionId: string;
|
|
994
|
+
readonly parentSessionGenerationId: string;
|
|
995
|
+
readonly parentSessionGenerationSequence: number;
|
|
996
|
+
readonly parentQueueId: string;
|
|
997
|
+
readonly parentRunId: string;
|
|
998
|
+
readonly parentToolCallId: string;
|
|
999
|
+
readonly originParentRunId: string;
|
|
1000
|
+
readonly originParentToolCallId: string;
|
|
1001
|
+
readonly agent: string;
|
|
1002
|
+
readonly depth: number;
|
|
1003
|
+
readonly signal: AbortSignal;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
export interface IFlexDelegatedRunAdmissionLease {
|
|
1007
|
+
/** Must be idempotent and safe to retry after a rejected close attempt. */
|
|
1008
|
+
close(): Promise<void> | void;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
export interface IFlexDelegatedRunAdmissionProvider<TScope> {
|
|
1012
|
+
acquireDelegatedRunAdmission(
|
|
1013
|
+
context: IFlexDelegatedRunAdmissionContext<TScope>,
|
|
1014
|
+
): Promise<IFlexDelegatedRunAdmissionLease> | IFlexDelegatedRunAdmissionLease;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
967
1017
|
export interface IFlexExecutionContextProviderContext<TScope> {
|
|
968
1018
|
scopeId: string;
|
|
969
1019
|
scope: TScope;
|
|
@@ -1035,6 +1085,7 @@ export interface IFlexHarnessOptions<TScope> {
|
|
|
1035
1085
|
toolProvider?: IFlexToolProvider<TScope>;
|
|
1036
1086
|
resourceToolProviderResolver?: IFlexResourceToolProviderResolver<TScope>;
|
|
1037
1087
|
executionContextProvider?: IFlexExecutionContextProvider<TScope>;
|
|
1088
|
+
delegatedRunAdmissionProvider?: IFlexDelegatedRunAdmissionProvider<TScope>;
|
|
1038
1089
|
stores?: IFlexHarnessStores;
|
|
1039
1090
|
agentSessionPolicy?: IFlexAgentSessionPolicy<TScope>;
|
|
1040
1091
|
toolOutputLimits?: IFlexJsonLimits;
|
package/ts/utils.json.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
IFlexProjectionSnapshotV2,
|
|
16
16
|
IFlexProjectionSnapshotV3,
|
|
17
17
|
IFlexScopeSnapshot,
|
|
18
|
+
IFlexSubagentProvenance,
|
|
18
19
|
TFlexAgentModelMessage,
|
|
19
20
|
TJsonValue,
|
|
20
21
|
} from './interfaces.js';
|
|
@@ -546,6 +547,68 @@ function validateSession(value: unknown, path: string): IValidatedSessionIdentit
|
|
|
546
547
|
};
|
|
547
548
|
}
|
|
548
549
|
|
|
550
|
+
function validateSubagentProvenance(
|
|
551
|
+
value: unknown,
|
|
552
|
+
path: string,
|
|
553
|
+
): IFlexSubagentProvenance {
|
|
554
|
+
const provenance = requireRecord(value, path);
|
|
555
|
+
requireOnlyKeys(provenance, [
|
|
556
|
+
'parentSessionId',
|
|
557
|
+
'parentSessionGenerationId',
|
|
558
|
+
'parentSessionGenerationSequence',
|
|
559
|
+
'originParentRunId',
|
|
560
|
+
'originParentToolCallId',
|
|
561
|
+
'agent',
|
|
562
|
+
'depth',
|
|
563
|
+
], path);
|
|
564
|
+
const parentSessionId = requireString(
|
|
565
|
+
provenance.parentSessionId,
|
|
566
|
+
`${path}.parentSessionId`,
|
|
567
|
+
);
|
|
568
|
+
const parentSessionGenerationId = requireString(
|
|
569
|
+
provenance.parentSessionGenerationId,
|
|
570
|
+
`${path}.parentSessionGenerationId`,
|
|
571
|
+
);
|
|
572
|
+
if (
|
|
573
|
+
Buffer.byteLength(parentSessionGenerationId, 'utf8')
|
|
574
|
+
> FLEX_SESSION_GENERATION_ID_MAX_BYTES
|
|
575
|
+
) {
|
|
576
|
+
throw new FlexHarnessStoreFormatError(
|
|
577
|
+
`${path}.parentSessionGenerationId exceeds ${FLEX_SESSION_GENERATION_ID_MAX_BYTES} UTF-8 bytes.`,
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
if (
|
|
581
|
+
!Number.isSafeInteger(provenance.parentSessionGenerationSequence)
|
|
582
|
+
|| Number(provenance.parentSessionGenerationSequence) < 1
|
|
583
|
+
) {
|
|
584
|
+
throw new FlexHarnessStoreFormatError(
|
|
585
|
+
`${path}.parentSessionGenerationSequence must be a positive integer.`,
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
const originParentRunId = requireString(
|
|
589
|
+
provenance.originParentRunId,
|
|
590
|
+
`${path}.originParentRunId`,
|
|
591
|
+
);
|
|
592
|
+
const originParentToolCallId = requireString(
|
|
593
|
+
provenance.originParentToolCallId,
|
|
594
|
+
`${path}.originParentToolCallId`,
|
|
595
|
+
);
|
|
596
|
+
const agent = requireString(provenance.agent, `${path}.agent`);
|
|
597
|
+
requireNonNegativeInteger(provenance.depth, `${path}.depth`);
|
|
598
|
+
if (provenance.depth === 0) {
|
|
599
|
+
throw new FlexHarnessStoreFormatError(`${path}.depth must be positive.`);
|
|
600
|
+
}
|
|
601
|
+
return {
|
|
602
|
+
parentSessionId,
|
|
603
|
+
parentSessionGenerationId,
|
|
604
|
+
parentSessionGenerationSequence: provenance.parentSessionGenerationSequence as number,
|
|
605
|
+
originParentRunId,
|
|
606
|
+
originParentToolCallId,
|
|
607
|
+
agent,
|
|
608
|
+
depth: provenance.depth as number,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
549
612
|
function validateModel(value: unknown, path: string): void {
|
|
550
613
|
const model = requireRecord(value, path);
|
|
551
614
|
requireOnlyKeys(model, ['provider', 'model', 'displayName', 'variant'], path);
|
|
@@ -811,9 +874,12 @@ export function assertFlexScopeSnapshot(value: unknown): asserts value is IFlexS
|
|
|
811
874
|
const tombstoneIds = new Set<string>();
|
|
812
875
|
const tombstones = new Map<string, {
|
|
813
876
|
sessionId: string;
|
|
877
|
+
sessionGenerationId?: string;
|
|
878
|
+
sessionGenerationSequence?: number;
|
|
814
879
|
rootSessionId?: string;
|
|
815
880
|
depth?: number;
|
|
816
881
|
parentSessionId?: string;
|
|
882
|
+
subagent?: IFlexSubagentProvenance;
|
|
817
883
|
}>();
|
|
818
884
|
for (let index = 0; index < snapshot.tombstones.length; index++) {
|
|
819
885
|
const path = `$snapshot.tombstones[${index}]`;
|
|
@@ -828,11 +894,12 @@ export function assertFlexScopeSnapshot(value: unknown): asserts value is IFlexS
|
|
|
828
894
|
'rootSessionId',
|
|
829
895
|
'depth',
|
|
830
896
|
'parentSessionId',
|
|
897
|
+
'subagent',
|
|
831
898
|
],
|
|
832
899
|
path,
|
|
833
900
|
);
|
|
834
901
|
const sessionId = requireString(tombstone.sessionId, `${path}.sessionId`);
|
|
835
|
-
validateOptionalSessionGeneration(tombstone, path);
|
|
902
|
+
const generation = validateOptionalSessionGeneration(tombstone, path);
|
|
836
903
|
requireString(tombstone.deletedAt, `${path}.deletedAt`);
|
|
837
904
|
const parentSessionId = tombstone.parentSessionId === undefined
|
|
838
905
|
? undefined
|
|
@@ -840,6 +907,19 @@ export function assertFlexScopeSnapshot(value: unknown): asserts value is IFlexS
|
|
|
840
907
|
if (parentSessionId === sessionId) {
|
|
841
908
|
throw new FlexHarnessStoreFormatError(`${path} cannot be its own parent.`);
|
|
842
909
|
}
|
|
910
|
+
const subagent = tombstone.subagent === undefined
|
|
911
|
+
? undefined
|
|
912
|
+
: validateSubagentProvenance(tombstone.subagent, `${path}.subagent`);
|
|
913
|
+
if (subagent && generation.sessionGenerationId === undefined) {
|
|
914
|
+
throw new FlexHarnessStoreFormatError(
|
|
915
|
+
`${path}.subagent requires exact session generation fields.`,
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
if (subagent && subagent.parentSessionId !== parentSessionId) {
|
|
919
|
+
throw new FlexHarnessStoreFormatError(
|
|
920
|
+
`${path}.subagent does not match its parentSessionId.`,
|
|
921
|
+
);
|
|
922
|
+
}
|
|
843
923
|
const hasRoot = tombstone.rootSessionId !== undefined;
|
|
844
924
|
const hasDepth = tombstone.depth !== undefined;
|
|
845
925
|
if (hasRoot !== hasDepth) {
|
|
@@ -864,11 +944,26 @@ export function assertFlexScopeSnapshot(value: unknown): asserts value is IFlexS
|
|
|
864
944
|
);
|
|
865
945
|
}
|
|
866
946
|
tombstoneIds.add(sessionId);
|
|
947
|
+
if (subagent) {
|
|
948
|
+
const origin = JSON.stringify([
|
|
949
|
+
subagent.parentSessionId,
|
|
950
|
+
subagent.originParentRunId,
|
|
951
|
+
subagent.originParentToolCallId,
|
|
952
|
+
]);
|
|
953
|
+
if (origins.has(origin)) {
|
|
954
|
+
throw new FlexHarnessStoreFormatError(
|
|
955
|
+
'Snapshot contains duplicate subagent invocation origins.',
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
origins.add(origin);
|
|
959
|
+
}
|
|
867
960
|
tombstones.set(sessionId, {
|
|
868
961
|
sessionId,
|
|
962
|
+
...generation,
|
|
869
963
|
...(hasRoot ? { rootSessionId: tombstone.rootSessionId as string } : {}),
|
|
870
964
|
...(hasDepth ? { depth: tombstone.depth as number } : {}),
|
|
871
965
|
...(parentSessionId === undefined ? {} : { parentSessionId }),
|
|
966
|
+
...(subagent === undefined ? {} : { subagent }),
|
|
872
967
|
});
|
|
873
968
|
}
|
|
874
969
|
for (const [index, tombstone] of snapshot.tombstones.entries()) {
|
|
@@ -894,6 +989,26 @@ export function assertFlexScopeSnapshot(value: unknown): asserts value is IFlexS
|
|
|
894
989
|
`Snapshot tombstone "${tombstone.sessionId}" has no parent "${tombstone.parentSessionId}".`,
|
|
895
990
|
);
|
|
896
991
|
}
|
|
992
|
+
if (tombstone.subagent) {
|
|
993
|
+
const parent = parentSession ?? parentTombstone!;
|
|
994
|
+
if (
|
|
995
|
+
parent.sessionGenerationId !== tombstone.subagent.parentSessionGenerationId
|
|
996
|
+
|| parent.sessionGenerationSequence
|
|
997
|
+
!== tombstone.subagent.parentSessionGenerationSequence
|
|
998
|
+
) {
|
|
999
|
+
throw new FlexHarnessStoreFormatError(
|
|
1000
|
+
`Snapshot tombstone "${tombstone.sessionId}" has inconsistent parent generation provenance.`,
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
const parentDepth = parent.parentSessionId === undefined
|
|
1004
|
+
? 0
|
|
1005
|
+
: parentSession?.depth ?? parentTombstone?.subagent?.depth;
|
|
1006
|
+
if (parentDepth === undefined || tombstone.subagent.depth !== parentDepth + 1) {
|
|
1007
|
+
throw new FlexHarnessStoreFormatError(
|
|
1008
|
+
`Snapshot tombstone "${tombstone.sessionId}" has inconsistent session depth provenance.`,
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
897
1012
|
if (tombstone.depth !== undefined && tombstone.depth > 0) {
|
|
898
1013
|
if (
|
|
899
1014
|
!parentTombstone
|