chainlesschain 0.166.136 → 0.166.138
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/README.md +27 -1
- package/package.json +1 -1
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/agent.js +12 -0
- package/src/data/changelog.json +26 -0
- package/src/harness/prompt-compressor.js +57 -0
- package/src/harness/provider-backed-compaction.js +6 -0
- package/src/lib/coding-task-guidance.js +2 -0
- package/src/lib/context-memory-kernel/jsonl-session-context-port.js +19 -10
- package/src/lib/context-memory-kernel/live-compaction.js +114 -6
- package/src/lib/context-memory-kernel/message-adapter.js +17 -13
- package/src/lib/context-memory-kernel/task-binding.js +41 -0
- package/src/lib/context-memory-kernel/task-worklog-port.js +26 -3
- package/src/lib/evolution/evidence-json-text.js +324 -14
- package/src/lib/evolution/evolution-evidence-projector.js +67 -1
- package/src/lib/read-file-loop-guard.js +134 -4
- package/src/lib/tool-result-preview.js +11 -2
- package/src/repl/agent-repl.js +2 -0
- package/src/runtime/agent-core.js +40 -6
- package/src/runtime/headless-runner.js +1 -0
- package/src/runtime/headless-stream.js +82 -10
- package/src/runtime/message-roles.js +28 -0
- package/src/runtime/policies/agent-policy.js +1 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { parse as parseSource, parseExpressionAt, tokenizer } from "acorn";
|
|
2
|
+
|
|
1
3
|
// Decode data only: no evaluation, revivers, prototype setters or permissive
|
|
2
4
|
// JSON repair. Budgets belong to the entire projection, including nested text.
|
|
3
5
|
export const EVIDENCE_JSON_POLICY = Object.freeze({
|
|
@@ -177,6 +179,7 @@ export function createEvidenceJsonTextBoundary(
|
|
|
177
179
|
policy = EVIDENCE_JSON_POLICY,
|
|
178
180
|
) {
|
|
179
181
|
let nodes = 0;
|
|
182
|
+
let sourceTokens = 0;
|
|
180
183
|
let bytes = 0;
|
|
181
184
|
const PARSE_FAILURE = Symbol("evidence-json-parse-failure");
|
|
182
185
|
const reject = (
|
|
@@ -303,7 +306,11 @@ export function createEvidenceJsonTextBoundary(
|
|
|
303
306
|
const parser = (
|
|
304
307
|
text,
|
|
305
308
|
start,
|
|
306
|
-
{
|
|
309
|
+
{
|
|
310
|
+
allowSingleQuotedStrings = false,
|
|
311
|
+
allowSourceExpressions = false,
|
|
312
|
+
allowSourceLiteralSyntax = false,
|
|
313
|
+
} = {},
|
|
307
314
|
) => {
|
|
308
315
|
let index = start;
|
|
309
316
|
const whitespace = () => {
|
|
@@ -405,9 +412,36 @@ export function createEvidenceJsonTextBoundary(
|
|
|
405
412
|
}
|
|
406
413
|
index++;
|
|
407
414
|
whitespace();
|
|
415
|
+
if (text[index] === end) {
|
|
416
|
+
if (!allowSourceLiteralSyntax) parserReject("trailing-comma");
|
|
417
|
+
index++;
|
|
418
|
+
return output;
|
|
419
|
+
}
|
|
408
420
|
}
|
|
409
421
|
return parserReject("syntax");
|
|
410
422
|
}
|
|
423
|
+
// JavaScript numeric separators/radices are never JSON syntax. Admit
|
|
424
|
+
// them only in a container independently proven by the source parser.
|
|
425
|
+
const sourceNumber =
|
|
426
|
+
/^[+-]?(?:0[xX][0-9a-fA-F_]+|0[bB][01_]+|0[oO][0-7_]+|[0-9][0-9_]*(?:\.[0-9_]*)?(?:[eE][+-]?[0-9_]+)?)/u.exec(
|
|
427
|
+
text.slice(index),
|
|
428
|
+
)?.[0];
|
|
429
|
+
if (
|
|
430
|
+
sourceNumber &&
|
|
431
|
+
(sourceNumber.includes("_") || /^[+-]?0[xXbBoO]/u.test(sourceNumber))
|
|
432
|
+
) {
|
|
433
|
+
if (!allowSourceLiteralSyntax) parserReject("source-number");
|
|
434
|
+
const unsigned = sourceNumber.replace(/^[+-]/u, "").replaceAll("_", "");
|
|
435
|
+
const number =
|
|
436
|
+
(sourceNumber.startsWith("-") ? -1 : 1) * Number(unsigned);
|
|
437
|
+
if (
|
|
438
|
+
!Number.isFinite(number) ||
|
|
439
|
+
(Number.isInteger(number) && !Number.isSafeInteger(number))
|
|
440
|
+
)
|
|
441
|
+
parserReject("budget");
|
|
442
|
+
index += sourceNumber.length;
|
|
443
|
+
return number;
|
|
444
|
+
}
|
|
411
445
|
const token =
|
|
412
446
|
/^(?:true|false|null|-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)/u.exec(
|
|
413
447
|
text.slice(index),
|
|
@@ -460,6 +494,7 @@ export function createEvidenceJsonTextBoundary(
|
|
|
460
494
|
layer = 0,
|
|
461
495
|
objectOnly = false,
|
|
462
496
|
codeText = false,
|
|
497
|
+
sourceText = false,
|
|
463
498
|
pythonFString = false,
|
|
464
499
|
fencedCodeLanguages = null,
|
|
465
500
|
visit,
|
|
@@ -478,6 +513,7 @@ export function createEvidenceJsonTextBoundary(
|
|
|
478
513
|
output += scanText(text.slice(start, end), {
|
|
479
514
|
...options,
|
|
480
515
|
codeText: true,
|
|
516
|
+
sourceText: layer === 0,
|
|
481
517
|
});
|
|
482
518
|
offset = end;
|
|
483
519
|
}
|
|
@@ -516,6 +552,214 @@ export function createEvidenceJsonTextBoundary(
|
|
|
516
552
|
"Agent model code expression under an opaque source context cannot be safely projected",
|
|
517
553
|
),
|
|
518
554
|
);
|
|
555
|
+
// Source syntax is a separate, explicit boundary. A paired tool's decoded
|
|
556
|
+
// string values do not inherit it. JSON-shaped standalone expressions still
|
|
557
|
+
// require strict JSON, even when they happen to parse as JavaScript arrays.
|
|
558
|
+
let sourceRanges;
|
|
559
|
+
let sourceBindings;
|
|
560
|
+
let tokenStarts;
|
|
561
|
+
let containerEnds;
|
|
562
|
+
let interpolationStarts;
|
|
563
|
+
const sourceOptions = {
|
|
564
|
+
ecmaVersion: "latest",
|
|
565
|
+
sourceType: "module",
|
|
566
|
+
allowReturnOutsideFunction: true,
|
|
567
|
+
};
|
|
568
|
+
const sourceContainer = (start) => {
|
|
569
|
+
if (
|
|
570
|
+
!sourceText ||
|
|
571
|
+
!codeText ||
|
|
572
|
+
policy.sourceLiteralPolicy !== "explicit-source-containers-v1"
|
|
573
|
+
)
|
|
574
|
+
return null;
|
|
575
|
+
const explicit = new Set([
|
|
576
|
+
"VariableDeclarator",
|
|
577
|
+
"FunctionDeclaration",
|
|
578
|
+
"FunctionExpression",
|
|
579
|
+
"ArrowFunctionExpression",
|
|
580
|
+
"CallExpression",
|
|
581
|
+
"NewExpression",
|
|
582
|
+
"MemberExpression",
|
|
583
|
+
"AssignmentExpression",
|
|
584
|
+
"ReturnStatement",
|
|
585
|
+
]);
|
|
586
|
+
const bindingNames = (root) => {
|
|
587
|
+
const names = [];
|
|
588
|
+
const pending = [root];
|
|
589
|
+
while (pending.length) {
|
|
590
|
+
const node = pending.pop();
|
|
591
|
+
if (!node || typeof node.type !== "string") continue;
|
|
592
|
+
if (
|
|
593
|
+
(node.type === "MemberExpression" || node.type === "Property") &&
|
|
594
|
+
node.computed
|
|
595
|
+
)
|
|
596
|
+
return null;
|
|
597
|
+
if (["Identifier", "PrivateIdentifier"].includes(node.type))
|
|
598
|
+
names.push(node.name);
|
|
599
|
+
if (node.type === "Literal" && typeof node.value === "string")
|
|
600
|
+
names.push(node.value);
|
|
601
|
+
for (const child of Object.values(node)) {
|
|
602
|
+
if (Array.isArray(child)) pending.push(...child);
|
|
603
|
+
else if (child && typeof child === "object") pending.push(child);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return names;
|
|
607
|
+
};
|
|
608
|
+
const collect = (root, admitted = false, bindings = null) => {
|
|
609
|
+
const provenAncestry = bindings !== null;
|
|
610
|
+
const stack = [[root, admitted, bindings]];
|
|
611
|
+
while (stack.length) {
|
|
612
|
+
const [node, parentSource, parentBindings] = stack.pop();
|
|
613
|
+
if (!node || typeof node.type !== "string") continue;
|
|
614
|
+
const isSource = parentSource || explicit.has(node.type);
|
|
615
|
+
let ownBindings = [];
|
|
616
|
+
if (node.type === "VariableDeclarator")
|
|
617
|
+
ownBindings = bindingNames(node.id);
|
|
618
|
+
if (["AssignmentExpression", "AssignmentPattern"].includes(node.type))
|
|
619
|
+
ownBindings = bindingNames(node.left);
|
|
620
|
+
if (
|
|
621
|
+
["Property", "PropertyDefinition", "MethodDefinition"].includes(
|
|
622
|
+
node.type,
|
|
623
|
+
)
|
|
624
|
+
)
|
|
625
|
+
ownBindings = node.computed ? null : bindingNames(node.key);
|
|
626
|
+
const activeBindings =
|
|
627
|
+
parentBindings && ownBindings
|
|
628
|
+
? [...parentBindings, ...ownBindings]
|
|
629
|
+
: null;
|
|
630
|
+
if (
|
|
631
|
+
isSource &&
|
|
632
|
+
["ArrayExpression", "ObjectExpression"].includes(node.type)
|
|
633
|
+
) {
|
|
634
|
+
sourceRanges.set(node.start, node.end);
|
|
635
|
+
if (provenAncestry) sourceBindings.set(node.start, activeBindings);
|
|
636
|
+
}
|
|
637
|
+
if (node.type === "TemplateLiteral" && provenAncestry) {
|
|
638
|
+
for (const quasi of node.quasis.slice(0, -1))
|
|
639
|
+
sourceBindings.set(quasi.end + 1, activeBindings);
|
|
640
|
+
}
|
|
641
|
+
for (const value of Object.values(node)) {
|
|
642
|
+
if (Array.isArray(value))
|
|
643
|
+
for (const child of value)
|
|
644
|
+
stack.push([child, isSource, activeBindings]);
|
|
645
|
+
else if (value && typeof value === "object")
|
|
646
|
+
stack.push([value, isSource, activeBindings]);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
if (!sourceRanges) {
|
|
651
|
+
sourceRanges = new Map();
|
|
652
|
+
sourceBindings = new Map();
|
|
653
|
+
tokenStarts = new Set();
|
|
654
|
+
containerEnds = new Map();
|
|
655
|
+
interpolationStarts = new Set();
|
|
656
|
+
const templates = [];
|
|
657
|
+
const delimiters = [];
|
|
658
|
+
let completePrefixEnd = 0;
|
|
659
|
+
try {
|
|
660
|
+
for (const token of tokenizer(text, sourceOptions)) {
|
|
661
|
+
if (++sourceTokens > (policy.maxSourceTokens ?? policy.maxNodes))
|
|
662
|
+
parserReject("budget");
|
|
663
|
+
tokenStarts.add(token.start);
|
|
664
|
+
const label = token.type.label;
|
|
665
|
+
if (label === "`") {
|
|
666
|
+
if (templates.at(-1)?.inText) templates.pop();
|
|
667
|
+
else templates.push({ start: token.start, inText: true });
|
|
668
|
+
}
|
|
669
|
+
if (label === "${" && templates.length) {
|
|
670
|
+
templates.at(-1).inText = false;
|
|
671
|
+
}
|
|
672
|
+
if (["[", "{", "${", "("].includes(label)) {
|
|
673
|
+
delimiters.push(token);
|
|
674
|
+
if (label === "${") interpolationStarts.add(token.start);
|
|
675
|
+
} else if (["]", "}", ")"].includes(label)) {
|
|
676
|
+
const opening = delimiters.pop();
|
|
677
|
+
if (
|
|
678
|
+
opening?.type.label === "${" &&
|
|
679
|
+
label === "}" &&
|
|
680
|
+
templates.length
|
|
681
|
+
)
|
|
682
|
+
templates.at(-1).inText = true;
|
|
683
|
+
if (
|
|
684
|
+
opening &&
|
|
685
|
+
((opening.type.label === "[" && label === "]") ||
|
|
686
|
+
(opening.type.label === "(" && label === ")") ||
|
|
687
|
+
(["{", "${"].includes(opening.type.label) && label === "}"))
|
|
688
|
+
)
|
|
689
|
+
containerEnds.set(opening.start, token.end);
|
|
690
|
+
}
|
|
691
|
+
if (
|
|
692
|
+
delimiters.length === 0 &&
|
|
693
|
+
templates.length === 0 &&
|
|
694
|
+
[";", "}"].includes(label)
|
|
695
|
+
)
|
|
696
|
+
completePrefixEnd = token.end;
|
|
697
|
+
}
|
|
698
|
+
} catch (error) {
|
|
699
|
+
if (error?.[PARSE_FAILURE]) throw error;
|
|
700
|
+
tokenStarts.clear();
|
|
701
|
+
interpolationStarts.clear();
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
try {
|
|
705
|
+
collect(parseSource(text, sourceOptions), false, []);
|
|
706
|
+
} catch {
|
|
707
|
+
// A page may end inside a later statement. A fully parsed top-level
|
|
708
|
+
// prefix still proves the ancestors of earlier complete constructs.
|
|
709
|
+
// Try only one tokenizer-bounded prefix; partial/unproven constructs
|
|
710
|
+
// retain the conservative source-prefix checks.
|
|
711
|
+
if (completePrefixEnd > 0 && completePrefixEnd < text.length) {
|
|
712
|
+
try {
|
|
713
|
+
collect(
|
|
714
|
+
parseSource(text.slice(0, completePrefixEnd), sourceOptions),
|
|
715
|
+
false,
|
|
716
|
+
[],
|
|
717
|
+
);
|
|
718
|
+
} catch {
|
|
719
|
+
/* No complete program prefix was proven. */
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (!sourceRanges.has(start) && tokenStarts.has(start)) {
|
|
725
|
+
try {
|
|
726
|
+
const end = containerEnds.get(start);
|
|
727
|
+
if (!end) return null;
|
|
728
|
+
const expression = parseExpressionAt(
|
|
729
|
+
text.slice(0, end),
|
|
730
|
+
start,
|
|
731
|
+
sourceOptions,
|
|
732
|
+
);
|
|
733
|
+
// A complete member/call expression proves source syntax itself.
|
|
734
|
+
// A declaration prefix can also prove a complete literal on a page
|
|
735
|
+
// whose later statements are incomplete. Comments/strings cannot
|
|
736
|
+
// nominate starts: only tokenizer-confirmed source tokens qualify.
|
|
737
|
+
const declared =
|
|
738
|
+
/(?:^|[;\n])\s*(?:const|let|var)\s+[$_A-Za-z][$_A-Za-z0-9]*\s*=\s*$/u.test(
|
|
739
|
+
text.slice(0, start),
|
|
740
|
+
);
|
|
741
|
+
const iteration =
|
|
742
|
+
/(?:^|[;\n])\s*for\s*\(\s*(?:const|let|var)\s+[$_A-Za-z][$_A-Za-z0-9]*\s+of\s*$/u.test(
|
|
743
|
+
text.slice(0, start),
|
|
744
|
+
);
|
|
745
|
+
const member = /^\s*\.\s*[$_A-Za-z][$_A-Za-z0-9]*/u.exec(
|
|
746
|
+
text.slice(end),
|
|
747
|
+
);
|
|
748
|
+
if (member) {
|
|
749
|
+
const memberExpression = parseExpressionAt(
|
|
750
|
+
text.slice(0, end + member[0].length),
|
|
751
|
+
start,
|
|
752
|
+
sourceOptions,
|
|
753
|
+
);
|
|
754
|
+
collect(memberExpression);
|
|
755
|
+
}
|
|
756
|
+
collect(expression, declared || iteration);
|
|
757
|
+
} catch {
|
|
758
|
+
/* No complete, explicit source expression. */
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return sourceRanges.get(start) ?? null;
|
|
762
|
+
};
|
|
519
763
|
if (objectOnly) {
|
|
520
764
|
const reader = parser(text, 0);
|
|
521
765
|
let parsed;
|
|
@@ -543,6 +787,43 @@ export function createEvidenceJsonTextBoundary(
|
|
|
543
787
|
let plainStart = 0;
|
|
544
788
|
for (let index = 0; index < text.length; ) {
|
|
545
789
|
const char = text[index];
|
|
790
|
+
// A tokenizer-proven template interpolation is source punctuation,
|
|
791
|
+
// not a JSON object. Its complete expression must parse independently;
|
|
792
|
+
// the normal scanner still visits all literals inside it.
|
|
793
|
+
if (
|
|
794
|
+
sourceText &&
|
|
795
|
+
codeText &&
|
|
796
|
+
char === "{" &&
|
|
797
|
+
text[index - 1] === "$" &&
|
|
798
|
+
['"', "'"].includes(text.slice(index + 1).trimStart()[0]) &&
|
|
799
|
+
policy.sourceLiteralPolicy === "explicit-source-containers-v1"
|
|
800
|
+
) {
|
|
801
|
+
sourceContainer(index);
|
|
802
|
+
const end = containerEnds?.get(index - 1);
|
|
803
|
+
if (interpolationStarts?.has(index - 1) && end) {
|
|
804
|
+
try {
|
|
805
|
+
const expression = parseExpressionAt(
|
|
806
|
+
text.slice(0, end - 1),
|
|
807
|
+
index + 1,
|
|
808
|
+
sourceOptions,
|
|
809
|
+
);
|
|
810
|
+
if (text.slice(expression.end, end - 1).trim() === "") {
|
|
811
|
+
const bindings = sourceBindings.get(index);
|
|
812
|
+
if (!bindings)
|
|
813
|
+
reject(
|
|
814
|
+
"Agent model code expression under an opaque source context cannot be safely projected",
|
|
815
|
+
);
|
|
816
|
+
bindings.forEach(requireSafeSourceKey);
|
|
817
|
+
index++;
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
} catch (error) {
|
|
821
|
+
// Projection failures must propagate; only Acorn syntax failures
|
|
822
|
+
// may fall back to the strict scanner.
|
|
823
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
546
827
|
const next =
|
|
547
828
|
char === "{" || char === "["
|
|
548
829
|
? text.slice(index + 1).match(/^\s*(.)/u)?.[1]
|
|
@@ -652,7 +933,7 @@ export function createEvidenceJsonTextBoundary(
|
|
|
652
933
|
index++;
|
|
653
934
|
continue;
|
|
654
935
|
}
|
|
655
|
-
|
|
936
|
+
let reader = parser(text, index, {
|
|
656
937
|
// A recognized container is always tried as strict JSON first, even
|
|
657
938
|
// inside code. Only an explicit non-JSON token allows literal scanning.
|
|
658
939
|
allowSingleQuotedStrings: codeText && !container,
|
|
@@ -667,21 +948,50 @@ export function createEvidenceJsonTextBoundary(
|
|
|
667
948
|
valueEnd = reader.end();
|
|
668
949
|
} catch (error) {
|
|
669
950
|
const kind = error?.[PARSE_FAILURE];
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
951
|
+
let sourceEnd = null;
|
|
952
|
+
if (["trailing-comma", "source-number"].includes(kind)) {
|
|
953
|
+
try {
|
|
954
|
+
sourceEnd = sourceContainer(index);
|
|
955
|
+
} catch (sourceError) {
|
|
956
|
+
rejectParseFailure(sourceError);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
if (sourceEnd !== null) {
|
|
960
|
+
const bindings = sourceBindings.get(index);
|
|
961
|
+
if (sourceBindings.has(index)) {
|
|
962
|
+
if (!bindings)
|
|
963
|
+
reject(
|
|
964
|
+
"Agent model code expression under an opaque source context cannot be safely projected",
|
|
965
|
+
);
|
|
966
|
+
bindings.forEach(requireSafeSourceKey);
|
|
967
|
+
} else requireSafeSourceAssignments(index);
|
|
677
968
|
const key = assignmentKeyBefore(text, plainStart, index);
|
|
678
969
|
if (key) requireSafeSourceKey(key);
|
|
970
|
+
reader = parser(text, index, { allowSourceLiteralSyntax: true });
|
|
971
|
+
try {
|
|
972
|
+
parsed = reader.value();
|
|
973
|
+
valueEnd = reader.end();
|
|
974
|
+
} catch (sourceError) {
|
|
975
|
+
rejectParseFailure(sourceError);
|
|
976
|
+
}
|
|
977
|
+
if (valueEnd !== sourceEnd) reject();
|
|
978
|
+
} else {
|
|
979
|
+
if (
|
|
980
|
+
!codeText ||
|
|
981
|
+
!["code-expression", "source-expression"].includes(kind)
|
|
982
|
+
)
|
|
983
|
+
rejectParseFailure(error);
|
|
984
|
+
if (kind === "source-expression") {
|
|
985
|
+
requireSafeSourceAssignments(index);
|
|
986
|
+
const key = assignmentKeyBefore(text, plainStart, index);
|
|
987
|
+
if (key) requireSafeSourceKey(key);
|
|
988
|
+
}
|
|
989
|
+
// Python/JavaScript containers may contain single quotes or expressions.
|
|
990
|
+
// Scan their literals individually, preserving nested strict JSON scans.
|
|
991
|
+
// Keep consumed nodes charged so repeated fallback cannot reset budgets.
|
|
992
|
+
index++;
|
|
993
|
+
continue;
|
|
679
994
|
}
|
|
680
|
-
// Python/JavaScript containers may contain single quotes or expressions.
|
|
681
|
-
// Scan their literals individually, preserving nested strict JSON scans.
|
|
682
|
-
// Keep consumed nodes charged so repeated fallback cannot reset budgets.
|
|
683
|
-
index++;
|
|
684
|
-
continue;
|
|
685
995
|
}
|
|
686
996
|
reader.whitespace();
|
|
687
997
|
let pair = quoted && text[reader.end()] === ":";
|
|
@@ -614,7 +614,7 @@ export const EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_V7_DIGEST = `sha256:${crea
|
|
|
614
614
|
"utf8",
|
|
615
615
|
)
|
|
616
616
|
.digest("hex")}`;
|
|
617
|
-
const
|
|
617
|
+
const AGENT_MODEL_RULESET_V8 = Object.freeze({
|
|
618
618
|
...AGENT_MODEL_RULESET_V7,
|
|
619
619
|
schema: "chainlesschain.evolution-agent-model-projection-rules/v8",
|
|
620
620
|
version: 8,
|
|
@@ -624,6 +624,25 @@ const AGENT_MODEL_RULESET = Object.freeze({
|
|
|
624
624
|
sourceExpressionPolicy: "bounded-source-tokens-v1",
|
|
625
625
|
}),
|
|
626
626
|
});
|
|
627
|
+
export const EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_V8_DIGEST = `sha256:${createHash(
|
|
628
|
+
"sha256",
|
|
629
|
+
)
|
|
630
|
+
.update(
|
|
631
|
+
`${AGENT_MODEL_RULESET_V8.schema}\0${JSON.stringify(AGENT_MODEL_RULESET_V8)}`,
|
|
632
|
+
"utf8",
|
|
633
|
+
)
|
|
634
|
+
.digest("hex")}`;
|
|
635
|
+
const AGENT_MODEL_RULESET = Object.freeze({
|
|
636
|
+
...AGENT_MODEL_RULESET_V8,
|
|
637
|
+
schema: "chainlesschain.evolution-agent-model-projection-rules/v9",
|
|
638
|
+
version: 9,
|
|
639
|
+
jsonTextPolicy: Object.freeze({
|
|
640
|
+
...AGENT_MODEL_RULESET_V8.jsonTextPolicy,
|
|
641
|
+
version: 6,
|
|
642
|
+
sourceLiteralPolicy: "explicit-source-containers-v1",
|
|
643
|
+
maxSourceTokens: 32768,
|
|
644
|
+
}),
|
|
645
|
+
});
|
|
627
646
|
export const EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_DIGEST = `sha256:${createHash(
|
|
628
647
|
"sha256",
|
|
629
648
|
)
|
|
@@ -659,6 +678,7 @@ const SUPPORTED_RULESETS = new Map([
|
|
|
659
678
|
[EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_V5_DIGEST, AGENT_MODEL_RULESET_V5],
|
|
660
679
|
[EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_V6_DIGEST, AGENT_MODEL_RULESET_V6],
|
|
661
680
|
[EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_V7_DIGEST, AGENT_MODEL_RULESET_V7],
|
|
681
|
+
[EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_V8_DIGEST, AGENT_MODEL_RULESET_V8],
|
|
662
682
|
[EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_DIGEST, AGENT_MODEL_RULESET],
|
|
663
683
|
]);
|
|
664
684
|
|
|
@@ -734,6 +754,7 @@ const AGENT_VALUE_PATTERNS = Object.freeze([
|
|
|
734
754
|
function valuePatterns(ruleset) {
|
|
735
755
|
return ruleset === SKILL_DECISION_RULESET ||
|
|
736
756
|
ruleset === AGENT_MODEL_RULESET ||
|
|
757
|
+
ruleset === AGENT_MODEL_RULESET_V8 ||
|
|
737
758
|
ruleset === AGENT_MODEL_RULESET_V7 ||
|
|
738
759
|
ruleset === AGENT_MODEL_RULESET_V6 ||
|
|
739
760
|
ruleset === AGENT_MODEL_RULESET_V5 ||
|
|
@@ -1459,6 +1480,43 @@ function isAgentCodeTextPath(payload, path, resultToolNames) {
|
|
|
1459
1480
|
return false;
|
|
1460
1481
|
}
|
|
1461
1482
|
|
|
1483
|
+
// Unlike historical codeText literal scanning, source grammar is confined to
|
|
1484
|
+
// exact paired-tool source fields. A second $json descent is ordinary data.
|
|
1485
|
+
function isAgentSourceTextPath(payload, path, resultToolNames) {
|
|
1486
|
+
if (!isAgentCodeTextPath(payload, path, resultToolNames)) return false;
|
|
1487
|
+
const message = payload.messages[Number(path[1])];
|
|
1488
|
+
if (path[2] === "tool_calls") {
|
|
1489
|
+
const fields = path.slice(6);
|
|
1490
|
+
if (fields[0] === "$json") fields.shift();
|
|
1491
|
+
return fields.length === 1 && fields[0] === "code";
|
|
1492
|
+
}
|
|
1493
|
+
const calls = payload.messages.flatMap((candidate) =>
|
|
1494
|
+
candidate?.role === "assistant" && Array.isArray(candidate.tool_calls)
|
|
1495
|
+
? candidate.tool_calls.filter((call) => call?.id === message.tool_call_id)
|
|
1496
|
+
: [],
|
|
1497
|
+
);
|
|
1498
|
+
if (calls.length !== 1) return false;
|
|
1499
|
+
const tool = calls[0].function?.name;
|
|
1500
|
+
const fields = path.slice(3);
|
|
1501
|
+
if (fields[0] === "$json") fields.shift();
|
|
1502
|
+
if (fields.includes("$json")) return false;
|
|
1503
|
+
if (tool === "search_files")
|
|
1504
|
+
return (
|
|
1505
|
+
fields.length === 3 &&
|
|
1506
|
+
fields[0] === "matches" &&
|
|
1507
|
+
/^\d+$/u.test(fields[1]) &&
|
|
1508
|
+
["context", "text"].includes(fields[2])
|
|
1509
|
+
);
|
|
1510
|
+
const sourceFields = {
|
|
1511
|
+
read_file: ["content"],
|
|
1512
|
+
run_code: ["output"],
|
|
1513
|
+
run_shell: ["stdout", "stderr"],
|
|
1514
|
+
check_shell: ["stdout", "stderr"],
|
|
1515
|
+
git: ["stdout", "stderr"],
|
|
1516
|
+
};
|
|
1517
|
+
return fields.length === 1 && (sourceFields[tool] || []).includes(fields[0]);
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1462
1520
|
function sanitizePayload(
|
|
1463
1521
|
payload,
|
|
1464
1522
|
ruleset = RULESET,
|
|
@@ -1489,6 +1547,7 @@ function sanitizePayload(
|
|
|
1489
1547
|
const toolArguments = jsonText && isAgentToolArgumentsPath(path);
|
|
1490
1548
|
const opaqueTransportBlock =
|
|
1491
1549
|
ruleset === AGENT_MODEL_RULESET ||
|
|
1550
|
+
ruleset === AGENT_MODEL_RULESET_V8 ||
|
|
1492
1551
|
ruleset === AGENT_MODEL_RULESET_V7 ||
|
|
1493
1552
|
ruleset === AGENT_MODEL_RULESET_V6 ||
|
|
1494
1553
|
ruleset === AGENT_MODEL_RULESET_V5 ||
|
|
@@ -1515,6 +1574,11 @@ function sanitizePayload(
|
|
|
1515
1574
|
layer,
|
|
1516
1575
|
objectOnly: toolArguments,
|
|
1517
1576
|
codeText: codeText || textContext?.pythonFString === true,
|
|
1577
|
+
sourceText: isAgentSourceTextPath(
|
|
1578
|
+
payload,
|
|
1579
|
+
path,
|
|
1580
|
+
ruleset.codeTextResultTools,
|
|
1581
|
+
),
|
|
1518
1582
|
pythonFString: textContext?.pythonFString === true,
|
|
1519
1583
|
// Only direct message text can contain Markdown code examples. Tool
|
|
1520
1584
|
// arguments and recursively decoded strings keep their existing rules.
|
|
@@ -3385,6 +3449,7 @@ function validateBundleIntegrity(bundle) {
|
|
|
3385
3449
|
}
|
|
3386
3450
|
if (
|
|
3387
3451
|
ruleset === AGENT_MODEL_RULESET ||
|
|
3452
|
+
ruleset === AGENT_MODEL_RULESET_V8 ||
|
|
3388
3453
|
ruleset === AGENT_MODEL_RULESET_V7 ||
|
|
3389
3454
|
ruleset === AGENT_MODEL_RULESET_V6 ||
|
|
3390
3455
|
ruleset === AGENT_MODEL_RULESET_V5 ||
|
|
@@ -3397,6 +3462,7 @@ function validateBundleIntegrity(bundle) {
|
|
|
3397
3462
|
allowStructuredArgumentText: ruleset !== AGENT_MODEL_RULESET_V1,
|
|
3398
3463
|
allowOpaqueTransportBlocks:
|
|
3399
3464
|
ruleset === AGENT_MODEL_RULESET ||
|
|
3465
|
+
ruleset === AGENT_MODEL_RULESET_V8 ||
|
|
3400
3466
|
ruleset === AGENT_MODEL_RULESET_V7 ||
|
|
3401
3467
|
ruleset === AGENT_MODEL_RULESET_V6 ||
|
|
3402
3468
|
ruleset === AGENT_MODEL_RULESET_V5 ||
|