@tangle-network/agent-eval 0.119.0 → 0.120.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 +25 -3
- package/README.md +0 -26
- package/dist/analyst/index.d.ts +30 -39
- package/dist/analyst/index.js +9 -5
- package/dist/analyst/index.js.map +1 -1
- package/dist/benchmarks/index.d.ts +11 -5
- package/dist/benchmarks/index.js +7 -7
- package/dist/campaign/index.d.ts +121 -66
- package/dist/campaign/index.js +26 -10
- package/dist/{chunk-RLCJQ6VZ.js → chunk-32BZXMSO.js} +385 -120
- package/dist/chunk-32BZXMSO.js.map +1 -0
- package/dist/{chunk-XJ7JVCHB.js → chunk-3A246TSA.js} +2 -2
- package/dist/{chunk-LMJ2TGWJ.js → chunk-JM2SKQMS.js} +2 -2
- package/dist/{chunk-F6YUH3L4.js → chunk-JN2FCO5W.js} +14 -168
- package/dist/chunk-JN2FCO5W.js.map +1 -0
- package/dist/{chunk-D7AEXSM5.js → chunk-PICTDURQ.js} +2 -2
- package/dist/{chunk-6QIM2EAP.js → chunk-PXD6ZFNY.js} +99 -1
- package/dist/chunk-PXD6ZFNY.js.map +1 -0
- package/dist/{chunk-GERDAIAL.js → chunk-QWMPPZ3X.js} +3 -3
- package/dist/{chunk-7A4LIMMY.js → chunk-S5TT5R3L.js} +229 -97
- package/dist/chunk-S5TT5R3L.js.map +1 -0
- package/dist/{chunk-ZYHJNKI3.js → chunk-ULOKLHIQ.js} +42 -5
- package/dist/chunk-ULOKLHIQ.js.map +1 -0
- package/dist/{chunk-FIUFRXP3.js → chunk-WW2A73HW.js} +49 -91
- package/dist/chunk-WW2A73HW.js.map +1 -0
- package/dist/{chunk-PAHNGS65.js → chunk-XDIRG3TO.js} +175 -11
- package/dist/chunk-XDIRG3TO.js.map +1 -0
- package/dist/contract/index.d.ts +76 -43
- package/dist/contract/index.js +596 -32
- package/dist/contract/index.js.map +1 -1
- package/dist/index.d.ts +92 -75
- package/dist/index.js +38 -35
- package/dist/index.js.map +1 -1
- package/dist/openapi.json +1 -1
- package/dist/rl.d.ts +11 -5
- package/dist/{run-campaign-OBXSN5WK.js → run-campaign-HNFPJET4.js} +2 -2
- package/dist/traces.d.ts +39 -25
- package/dist/traces.js +3 -5
- package/docs/trace-analysis.md +6 -2
- package/package.json +2 -7
- package/dist/chunk-6QIM2EAP.js.map +0 -1
- package/dist/chunk-7A4LIMMY.js.map +0 -1
- package/dist/chunk-F6YUH3L4.js.map +0 -1
- package/dist/chunk-FIUFRXP3.js.map +0 -1
- package/dist/chunk-PAHNGS65.js.map +0 -1
- package/dist/chunk-RLCJQ6VZ.js.map +0 -1
- package/dist/chunk-ZYHJNKI3.js.map +0 -1
- package/dist/primeintellect/index.d.ts +0 -311
- package/dist/primeintellect/index.js +0 -348
- package/dist/primeintellect/index.js.map +0 -1
- /package/dist/{chunk-XJ7JVCHB.js.map → chunk-3A246TSA.js.map} +0 -0
- /package/dist/{chunk-LMJ2TGWJ.js.map → chunk-JM2SKQMS.js.map} +0 -0
- /package/dist/{chunk-D7AEXSM5.js.map → chunk-PICTDURQ.js.map} +0 -0
- /package/dist/{chunk-GERDAIAL.js.map → chunk-QWMPPZ3X.js.map} +0 -0
- /package/dist/{run-campaign-OBXSN5WK.js.map → run-campaign-HNFPJET4.js.map} +0 -0
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
CostLedger
|
|
9
9
|
} from "./chunk-JHOJHHU7.js";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from "./chunk-
|
|
11
|
+
buildTraceAnalystTools,
|
|
12
|
+
runTraceAnalysisLoop
|
|
13
|
+
} from "./chunk-PXD6ZFNY.js";
|
|
14
14
|
import {
|
|
15
15
|
validateAgentProfileCell
|
|
16
16
|
} from "./chunk-XJYR7XFV.js";
|
|
@@ -27,12 +27,14 @@ var configuredModels = /* @__PURE__ */ new WeakMap();
|
|
|
27
27
|
function createAnalystAi(config) {
|
|
28
28
|
const model = config.model.trim();
|
|
29
29
|
if (!model) throw new TypeError("createAnalystAi: model must be a non-empty string");
|
|
30
|
-
const
|
|
30
|
+
const args = {
|
|
31
31
|
name: config.provider ?? "openai",
|
|
32
32
|
apiKey: config.apiKey,
|
|
33
|
-
apiURL: config.baseUrl,
|
|
33
|
+
...config.baseUrl ? { apiURL: config.baseUrl } : {},
|
|
34
|
+
...config.headers ? { headers: config.headers } : {},
|
|
34
35
|
config: { model }
|
|
35
|
-
}
|
|
36
|
+
};
|
|
37
|
+
const service = ai(args);
|
|
36
38
|
configuredModels.set(service, model);
|
|
37
39
|
return service;
|
|
38
40
|
}
|
|
@@ -589,9 +591,6 @@ That produced no valid findings. The report DOES describe issues \u2014 re-extra
|
|
|
589
591
|
return { findings: [], outcome: "extraction_failed" };
|
|
590
592
|
}
|
|
591
593
|
|
|
592
|
-
// src/analyst/kind-factory.ts
|
|
593
|
-
import { AxJSRuntime, agent } from "@ax-llm/ax";
|
|
594
|
-
|
|
595
594
|
// src/analyst/ax-cost-service.ts
|
|
596
595
|
function meterAxChatService(ai2, options) {
|
|
597
596
|
assertPositiveInteger(options.maxOutputTokens, "maxOutputTokens");
|
|
@@ -699,10 +698,17 @@ function costReceiptFromAxResponse(response, fallbackModel) {
|
|
|
699
698
|
validUsage(tokens.reasoningTokens) ? tokens.reasoningTokens : 0,
|
|
700
699
|
thoughtsTokens
|
|
701
700
|
);
|
|
702
|
-
const
|
|
701
|
+
const separateReasoningTokens = reasoningTokens > tokens.completionTokens ? reasoningTokens : 0;
|
|
702
|
+
const additionalOutputTokens = Math.max(thoughtsTokens, separateReasoningTokens);
|
|
703
|
+
const outputTokens = tokens.completionTokens + additionalOutputTokens;
|
|
703
704
|
const directTotal = tokens.promptTokens + tokens.completionTokens;
|
|
704
|
-
const
|
|
705
|
-
|
|
705
|
+
const validTotals = /* @__PURE__ */ new Set([
|
|
706
|
+
directTotal,
|
|
707
|
+
directTotal + totalCacheTokens,
|
|
708
|
+
directTotal + additionalOutputTokens,
|
|
709
|
+
directTotal + totalCacheTokens + additionalOutputTokens
|
|
710
|
+
]);
|
|
711
|
+
if (!validTotals.has(tokens.totalTokens)) {
|
|
706
712
|
return {
|
|
707
713
|
model,
|
|
708
714
|
inputTokens: 0,
|
|
@@ -751,7 +757,8 @@ function containsUnboundedOrCacheableContent(request) {
|
|
|
751
757
|
if (request.functions?.some((fn) => fn.cache === true)) return true;
|
|
752
758
|
return request.chatPrompt.some((message) => {
|
|
753
759
|
if (message.cache === true) return true;
|
|
754
|
-
|
|
760
|
+
if (!("content" in message) || !Array.isArray(message.content)) return false;
|
|
761
|
+
return message.content.some((part) => part.cache === true || part.type !== "text");
|
|
755
762
|
});
|
|
756
763
|
}
|
|
757
764
|
function modelName(value) {
|
|
@@ -827,6 +834,7 @@ function validateUsageSettlementTimeout(timeoutMs) {
|
|
|
827
834
|
|
|
828
835
|
// src/analyst/kind-factory.ts
|
|
829
836
|
function createTraceAnalystKind(spec, opts) {
|
|
837
|
+
rejectRemovedKindOptions(spec);
|
|
830
838
|
const version = opts.versionSuffix ? `${spec.version}+${opts.versionSuffix}` : spec.version;
|
|
831
839
|
const model = resolveAnalystModel(opts.ai, opts.model);
|
|
832
840
|
const minimumEvidenceCitations = spec.minimumEvidenceCitations ?? 1;
|
|
@@ -838,7 +846,7 @@ function createTraceAnalystKind(spec, opts) {
|
|
|
838
846
|
id: spec.id,
|
|
839
847
|
description: spec.description,
|
|
840
848
|
inputKind: "trace-store",
|
|
841
|
-
cost: spec.cost,
|
|
849
|
+
cost: { ...spec.cost, settlement_timeout_ms: settlementTimeoutMs },
|
|
842
850
|
version,
|
|
843
851
|
async analyze(store, ctx) {
|
|
844
852
|
const maxOutputTokens = spec.maxOutputTokens ?? 4096;
|
|
@@ -859,62 +867,37 @@ function createTraceAnalystKind(spec, opts) {
|
|
|
859
867
|
});
|
|
860
868
|
try {
|
|
861
869
|
const tools = spec.buildTools(store);
|
|
862
|
-
const
|
|
863
|
-
const maxParallel = spec.
|
|
870
|
+
const maxSubqueries = spec.subqueries?.maxCalls ?? 0;
|
|
871
|
+
const maxParallel = spec.subqueries?.maxParallel ?? 2;
|
|
864
872
|
const priorContext = renderPriorFindings(ctx.priorFindings);
|
|
865
873
|
const upstreamContext = renderUpstreamFindings(ctx.upstreamFindings);
|
|
866
|
-
const functions = tools;
|
|
867
874
|
const actorDescription = spec.actorDescription.trim() + priorContext + upstreamContext + "\n\n" + RAW_FINDING_SCHEMA_PROMPT + (minimumEvidenceCitations > 1 ? `
|
|
868
875
|
|
|
869
|
-
This kind requires at least ${minimumEvidenceCitations} evidence citations per finding; rows with fewer are rejected.` : "") + "\n\nFirst write `report`: a concise free-form prose diagnosis of what the traces show \u2014 what succeeded, what was suboptimal or failed \u2014 with concrete trace ids and numbers. THEN return the structured `findings` array (it MAY be empty when there is nothing to report).
|
|
870
|
-
const ax = agent(
|
|
871
|
-
"question:string -> report:string, findings:json[]",
|
|
872
|
-
{
|
|
873
|
-
agentIdentity: {
|
|
874
|
-
name: spec.id,
|
|
875
|
-
description: spec.description
|
|
876
|
-
},
|
|
877
|
-
contextFields: ["question"],
|
|
878
|
-
runtime: new AxJSRuntime({
|
|
879
|
-
permissions: [],
|
|
880
|
-
blockDynamicImport: true,
|
|
881
|
-
allowedModules: [],
|
|
882
|
-
freezeIntrinsics: true,
|
|
883
|
-
blockShadowRealm: true,
|
|
884
|
-
preventGlobalThisExtensions: false
|
|
885
|
-
}),
|
|
886
|
-
mode: maxDepth > 0 ? "advanced" : "simple",
|
|
887
|
-
recursionOptions: maxDepth > 0 ? { maxDepth } : void 0,
|
|
888
|
-
maxTurns: spec.maxTurns ?? 12,
|
|
889
|
-
maxRuntimeChars: spec.maxRuntimeChars ?? 6e3,
|
|
890
|
-
maxBatchedLlmQueryConcurrency: maxParallel,
|
|
891
|
-
promptLevel: "detailed",
|
|
892
|
-
// Trace analysis depends on exact prior tool results and runtime variables.
|
|
893
|
-
contextPolicy: { preset: "full", budget: "balanced" },
|
|
894
|
-
functions,
|
|
895
|
-
actorOptions: {
|
|
896
|
-
description: actorDescription,
|
|
897
|
-
model,
|
|
898
|
-
showThoughts: false,
|
|
899
|
-
thinkingTokenBudget: "none"
|
|
900
|
-
},
|
|
901
|
-
responderOptions: {
|
|
902
|
-
description: spec.responderDescription ?? "Pass through the actor's `report` prose verbatim, and format the `findings` array exactly as the actor produced it. Do not add, drop, or summarize entries.",
|
|
903
|
-
model,
|
|
904
|
-
showThoughts: false
|
|
905
|
-
},
|
|
906
|
-
bubbleErrors: [TraceFileMissingError]
|
|
907
|
-
}
|
|
908
|
-
);
|
|
876
|
+
This kind requires at least ${minimumEvidenceCitations} evidence citations per finding; rows with fewer are rejected.` : "") + "\n\nFirst write `report`: a concise free-form prose diagnosis of what the traces show \u2014 what succeeded, what was suboptimal or failed \u2014 with concrete trace ids and numbers. THEN return the structured `findings` array (it MAY be empty when there is nothing to report).";
|
|
909
877
|
ctx.log?.(`analyst.kind ${spec.id} forward`, {
|
|
910
|
-
|
|
878
|
+
max_subqueries: maxSubqueries,
|
|
911
879
|
tool_count: tools.length,
|
|
912
880
|
tags: ctx.tags
|
|
913
881
|
});
|
|
914
|
-
const
|
|
882
|
+
const completed = await runTraceAnalysisLoop({
|
|
883
|
+
id: spec.id,
|
|
884
|
+
description: spec.description,
|
|
885
|
+
prompt: actorDescription,
|
|
886
|
+
question: deriveQuestion(ctx, spec),
|
|
887
|
+
ai: meteredAi,
|
|
888
|
+
model,
|
|
889
|
+
tools,
|
|
890
|
+
findingType: "object",
|
|
891
|
+
maxSubqueries,
|
|
892
|
+
maxParallelSubqueries: maxParallel,
|
|
893
|
+
maxTurns: spec.maxTurns ?? 12,
|
|
894
|
+
maxRuntimeChars: spec.maxRuntimeChars ?? 6e3,
|
|
895
|
+
...ctx.signal ? { signal: ctx.signal } : {}
|
|
896
|
+
});
|
|
897
|
+
const { report, findings: submittedFindings } = completed;
|
|
915
898
|
const expectedSubjects = KIND_EXPECTED_SUBJECTS[spec.id];
|
|
916
899
|
const out = [];
|
|
917
|
-
const rawRows =
|
|
900
|
+
const rawRows = submittedFindings;
|
|
918
901
|
let rejectedWrongKind = 0;
|
|
919
902
|
let rejectedInsufficientEvidence = 0;
|
|
920
903
|
const processRow = (parsed) => {
|
|
@@ -973,7 +956,6 @@ This kind requires at least ${minimumEvidenceCitations} evidence citations per f
|
|
|
973
956
|
rejected_wrong_subject: rejectedWrongKind,
|
|
974
957
|
rejected_insufficient_evidence: rejectedInsufficientEvidence
|
|
975
958
|
});
|
|
976
|
-
const report = typeof result.report === "string" ? result.report : "";
|
|
977
959
|
if (out.length === 0 && report.trim().length >= 200) {
|
|
978
960
|
if (opts.recovery) {
|
|
979
961
|
const wrongKindBefore = rejectedWrongKind;
|
|
@@ -1039,6 +1021,23 @@ This kind requires at least ${minimumEvidenceCitations} evidence citations per f
|
|
|
1039
1021
|
}
|
|
1040
1022
|
};
|
|
1041
1023
|
}
|
|
1024
|
+
function rejectRemovedKindOptions(spec) {
|
|
1025
|
+
const supplied = spec;
|
|
1026
|
+
const migrations = [
|
|
1027
|
+
["recursion", "subqueries"],
|
|
1028
|
+
["responderDescription", "actorDescription"],
|
|
1029
|
+
["maxDepth", "subqueries"],
|
|
1030
|
+
["maxParallelSubagents", "subqueries.maxParallel"],
|
|
1031
|
+
["subagentDescription", "actorDescription"]
|
|
1032
|
+
];
|
|
1033
|
+
for (const [removed, replacement] of migrations) {
|
|
1034
|
+
if (removed in supplied) {
|
|
1035
|
+
throw new TypeError(
|
|
1036
|
+
`createTraceAnalystKind: '${removed}' is unsupported; use '${replacement}'`
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1042
1041
|
function deriveQuestion(ctx, spec) {
|
|
1043
1042
|
const focus = ctx.tags?.focus?.trim();
|
|
1044
1043
|
const task = `Analyze this trace dataset with the available tools and report ${spec.area} findings. ${spec.description}`;
|
|
@@ -1146,11 +1145,7 @@ DISCOVERY \u2192 CLUSTER \u2192 CITE protocol:
|
|
|
1146
1145
|
|
|
1147
1146
|
If the dataset has no failures, return an empty findings array \u2014 do NOT pad with low-confidence speculation.
|
|
1148
1147
|
|
|
1149
|
-
**
|
|
1150
|
-
- After your first \`getDatasetOverview\` + \`queryTraces\` calls, you should have 3-6 candidate failure clusters in mind. Spawn one \`llmQuery\` per cluster in a single batch \u2014 they investigate in parallel.
|
|
1151
|
-
- A sub-investigator that finds its cluster is actually two distinct modes should split again at its own level. Recursion is meant to discover sub-modes, not to do trivial drilling that the parent could do in-line.
|
|
1152
|
-
- Pass narrow context to each subagent: { question: 'investigate the auth-revoked-mid-run cluster', context: { trace_ids: ['abc', 'def'], suspected_root_cause: 'token refresh skipped on idle sessions' } }. Subagents need enough context to skip re-discovery but not the whole conversation.
|
|
1153
|
-
- Each subagent returns candidate cluster evidence; the parent merges it into the final finding set.
|
|
1148
|
+
**Use subqueries over loaded evidence.** After the first scan, load representative span excerpts for each candidate cluster. Then send one bounded \`llmQuery\` per cluster in one batch, including the exact excerpts and asking it to classify the root cause. Subqueries cannot call trace tools. Merge or split clusters yourself from their classifications and the cited source evidence.
|
|
1154
1149
|
|
|
1155
1150
|
OBSERVABILITY rules:
|
|
1156
1151
|
- Each non-final turn must emit at least one \`console.log\` for evidence.
|
|
@@ -1159,17 +1154,17 @@ var FAILURE_MODE_KIND_SPEC = {
|
|
|
1159
1154
|
id: "failure-mode",
|
|
1160
1155
|
description: "Clusters trace-dataset failures into distinct failure modes with cited evidence and a short recommended action.",
|
|
1161
1156
|
area: "failure-mode",
|
|
1162
|
-
version: "1.
|
|
1157
|
+
version: "1.2.0",
|
|
1163
1158
|
actorDescription: ACTOR_PROMPT,
|
|
1164
1159
|
buildTools: (store) => buildTraceToolsForGroup("all", store),
|
|
1165
|
-
|
|
1160
|
+
subqueries: { maxCalls: 8, maxParallel: 4 },
|
|
1166
1161
|
maxTurns: 24,
|
|
1167
1162
|
cost: { kind: "llm" }
|
|
1168
1163
|
};
|
|
1169
1164
|
|
|
1170
1165
|
// src/analyst/kinds/improvement.ts
|
|
1171
1166
|
var subjectGrammar2 = findingSubjectGrammarPromptFor("improvement");
|
|
1172
|
-
var ACTOR_PROMPT2 = `You are a
|
|
1167
|
+
var ACTOR_PROMPT2 = `You are a self-improvement analyst. Your job is to propose **concrete, locus-named edits** the agent's runtime should adopt to fix the failure modes, knowledge gaps, and poisonings present in this dataset.
|
|
1173
1168
|
|
|
1174
1169
|
Upstream analysts have already classified the problems. Your job is to convert each problem into a *change to make* and grade its expected leverage. Each finding is one proposed edit.
|
|
1175
1170
|
|
|
@@ -1189,8 +1184,8 @@ DISCOVERY \u2192 CANDIDATE-FIXES \u2192 COMPETE \u2192 CITE protocol:
|
|
|
1189
1184
|
- **Skill / MCP / hook / subagent** \u2014 change the reusable profile component responsible for the behavior
|
|
1190
1185
|
- **Workflow / rollout policy** \u2014 change orchestration, budget, sampling, or stopping behavior
|
|
1191
1186
|
- **Code** \u2014 change an implementation path when profile edits cannot repair the behavior
|
|
1192
|
-
3. **
|
|
1193
|
-
4. After
|
|
1187
|
+
3. **Compare candidate fixes with bounded subqueries.** Load the representative failure excerpts, then send one \`llmQuery\` per candidate-fix axis the same evidence. Ask for likely effect, side effects, and implementation scope. Subqueries cannot call trace tools; trace ids alone are insufficient context.
|
|
1188
|
+
4. After the comparisons return, **pick the winning candidate per cluster** based on expected effect and risk, then emit ONE finding. Keep the alternatives and rejection reasons in the rationale so the recommendation is auditable.
|
|
1194
1189
|
5. **Cross-reference upstream findings.** Cite prior failure-mode or knowledge-gap findings as \`finding://<prior-finding-id>\`. This builds the dependency graph that lets the dashboard show "fix #X resolves failure modes A, B, C."
|
|
1195
1190
|
|
|
1196
1191
|
For each winning recommendation, emit ONE finding. Use one exact locus from the subject grammar and state the edit in one sentence. Match leverage to the source failure's severity; use medium for quality-of-life changes and info for cleanup with no behavioral effect. Cite the targeted \`finding://<id>\` when available and the most representative span when useful. Quote the problem being fixed. Use confidence 0.85+ for a mechanical fix to a well-evidenced failure, 0.6-0.8 when judgment is required, and <0.5 for speculation. Explain in at most two sentences why this candidate beat its alternatives. The recommended action must be the literal diff, quoted replacement, tool description, or setting change.
|
|
@@ -1205,10 +1200,10 @@ var IMPROVEMENT_KIND_SPEC = {
|
|
|
1205
1200
|
id: "improvement",
|
|
1206
1201
|
description: "Converts upstream failure / gap / poisoning findings into concrete locus-named edits (prompt, tool-doc, RAG, scaffolding) with leverage grades.",
|
|
1207
1202
|
area: "improvement",
|
|
1208
|
-
version: "1.
|
|
1203
|
+
version: "1.2.0",
|
|
1209
1204
|
actorDescription: ACTOR_PROMPT2,
|
|
1210
1205
|
buildTools: (store) => buildTraceToolsForGroup("all", store),
|
|
1211
|
-
|
|
1206
|
+
subqueries: { maxCalls: 8, maxParallel: 4 },
|
|
1212
1207
|
maxTurns: 30,
|
|
1213
1208
|
maxRuntimeChars: 12e3,
|
|
1214
1209
|
cost: { kind: "llm" }
|
|
@@ -1237,7 +1232,7 @@ DISCOVERY \u2192 ATTRIBUTE-TO-LAYER \u2192 CITE protocol:
|
|
|
1237
1232
|
3. For each gap, identify the **layer of the runtime that should have prevented it** and use its exact locus from the subject grammar above.
|
|
1238
1233
|
4. For each defensible gap, emit ONE finding. Use an exact locus from the subject grammar and name the missing or stale knowledge (for example, "wiki has no page on invoice line-item shape; agent re-derived it from raw spans"). Rate it high when it caused failure or a clarifying question, medium for unnecessary turns, and low for minor inefficiency. Cite the span where the question, correction, retrieval miss, or stale result surfaced and quote it exactly. Use confidence 0.85+ when the agent articulated the gap and 0.6-0.8 when inferred. Recommend a concrete wiki edit for an agent-knowledge locus or a prompt/tool-description edit otherwise.
|
|
1239
1234
|
|
|
1240
|
-
**
|
|
1235
|
+
**Compare layers over loaded evidence.** After the first scan, load the exact excerpts behind candidates across \`agent-knowledge:*\`, \`websearch:outdated\`, \`tool-doc:*\`, \`system-prompt:*\`, and \`memory:*\`. Use one bounded \`llmQuery\` per layer to classify those excerpts. Subqueries cannot call trace tools. Merge their classifications into the final finding set only when the source excerpts support them.
|
|
1241
1236
|
|
|
1242
1237
|
Do NOT report a gap that the agent later recovered from cleanly within the same turn \u2014 that's resilience, not a gap. Cite the *non-recovery* version when both exist.
|
|
1243
1238
|
|
|
@@ -1247,10 +1242,10 @@ var KNOWLEDGE_GAP_KIND_SPEC = {
|
|
|
1247
1242
|
id: "knowledge-gap",
|
|
1248
1243
|
description: "Identifies missing or stale pieces of knowledge \u2014 primarily against the agent-knowledge wiki \u2014 and attributes each to the runtime layer (wiki page, claim, raw source, websearch, tool-doc, system-prompt, memory) that should have held it.",
|
|
1249
1244
|
area: "knowledge-gap",
|
|
1250
|
-
version: "1.
|
|
1245
|
+
version: "1.2.0",
|
|
1251
1246
|
actorDescription: ACTOR_PROMPT3,
|
|
1252
1247
|
buildTools: (store) => buildTraceToolsForGroup("discoveryAndSearch", store),
|
|
1253
|
-
|
|
1248
|
+
subqueries: { maxCalls: 5, maxParallel: 4 },
|
|
1254
1249
|
maxTurns: 18,
|
|
1255
1250
|
cost: { kind: "llm" }
|
|
1256
1251
|
};
|
|
@@ -1277,7 +1272,7 @@ DISCOVERY \u2192 DUAL-VERIFY \u2192 CITE protocol:
|
|
|
1277
1272
|
- Confirm the belief is actually false in this trace's own evidence (cite the span that contradicts it)
|
|
1278
1273
|
Only emit a finding when both halves are nailed down. If you can only nail one, drop it \u2014 single-evidence poisoning findings are too speculative to be useful.
|
|
1279
1274
|
|
|
1280
|
-
**
|
|
1275
|
+
**Independently assess both halves.** Load the action excerpt and contradicting excerpt yourself, then send bounded \`llmQuery\` calls the exact evidence for "did the agent act?" and "does the trace contradict the belief?" Subqueries cannot call trace tools. Accept a poisoning only when both assessments and the source excerpts support it.
|
|
1281
1276
|
|
|
1282
1277
|
For each confirmed poisoning, emit ONE finding. Use the source of the false belief as the exact subject. State "agent believed X (from source S); trace evidence shows X is false." Rate it critical for a wrong user-visible action, high when caught internally after significant waste, and medium for inefficiency. Cite BOTH the action span and the contradicting span with exact quotes. Use confidence 0.85+ when both halves have exact quotes and 0.6-0.8 when one half is inferred. Recommend the literal source correction: update the wiki claim, invalidate and re-curate the raw source, or replace the stale prompt/tool instruction.
|
|
1283
1278
|
|
|
@@ -1289,10 +1284,10 @@ var KNOWLEDGE_POISONING_KIND_SPEC = {
|
|
|
1289
1284
|
id: "knowledge-poisoning",
|
|
1290
1285
|
description: "Identifies confident-but-wrong actions caused by stale memory, contradicting RAG, deprecated tool docs, or outdated system-prompt instructions.",
|
|
1291
1286
|
area: "knowledge-poisoning",
|
|
1292
|
-
version: "1.
|
|
1287
|
+
version: "1.2.0",
|
|
1293
1288
|
actorDescription: ACTOR_PROMPT4,
|
|
1294
1289
|
buildTools: (store) => buildTraceToolsForGroup("all", store),
|
|
1295
|
-
|
|
1290
|
+
subqueries: { maxCalls: 8, maxParallel: 4 },
|
|
1296
1291
|
maxTurns: 20,
|
|
1297
1292
|
minimumEvidenceCitations: 2,
|
|
1298
1293
|
cost: { kind: "llm" }
|
|
@@ -1322,6 +1317,14 @@ var AnalystRegistry = class {
|
|
|
1322
1317
|
if (!analyst.version) {
|
|
1323
1318
|
throw new Error(`AnalystRegistry.register: analyst "${analyst.id}" must declare a version`);
|
|
1324
1319
|
}
|
|
1320
|
+
if (analyst.cost.kind === "deterministic" && analyst.cost.settlement_timeout_ms !== void 0) {
|
|
1321
|
+
throw new TypeError(
|
|
1322
|
+
`AnalystRegistry.register: deterministic analyst "${analyst.id}" cannot declare settlement_timeout_ms`
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
if (analyst.cost.settlement_timeout_ms !== void 0) {
|
|
1326
|
+
validateUsageSettlementTimeout(analyst.cost.settlement_timeout_ms);
|
|
1327
|
+
}
|
|
1325
1328
|
this.analysts.set(analyst.id, analyst);
|
|
1326
1329
|
}
|
|
1327
1330
|
list() {
|
|
@@ -1356,7 +1359,10 @@ var AnalystRegistry = class {
|
|
|
1356
1359
|
const hooks = this.options.hooks ?? {};
|
|
1357
1360
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1358
1361
|
const started = Date.now();
|
|
1359
|
-
const
|
|
1362
|
+
const timeoutMs = validateTimeout(runOpts.timeoutMs);
|
|
1363
|
+
const deadlineMs = timeoutMs === void 0 ? void 0 : started + timeoutMs;
|
|
1364
|
+
const timeoutSignal = timeoutMs === void 0 ? void 0 : AbortSignal.timeout(timeoutMs);
|
|
1365
|
+
const runSignal = combineAbortSignals(runOpts.signal, timeoutSignal);
|
|
1360
1366
|
const selected = this.selectAnalysts(runOpts);
|
|
1361
1367
|
const budget = runOpts.budget ?? this.options.defaultBudget;
|
|
1362
1368
|
validateBudgetPolicy(budget);
|
|
@@ -1380,9 +1386,16 @@ var AnalystRegistry = class {
|
|
|
1380
1386
|
}
|
|
1381
1387
|
for (const analyst of selected) {
|
|
1382
1388
|
const t0 = Date.now();
|
|
1389
|
+
if (runSignal?.aborted) {
|
|
1390
|
+
const summary2 = abortedBeforeStartSummary(analyst, runSignal);
|
|
1391
|
+
summaries.push(summary2);
|
|
1392
|
+
log(`[analyst] skip ${analyst.id} \u2014 run aborted`, { runId, reason: summary2.reason });
|
|
1393
|
+
yield { type: "analyst-skipped", summary: summary2 };
|
|
1394
|
+
continue;
|
|
1395
|
+
}
|
|
1383
1396
|
const input = this.routeInput(analyst, inputs);
|
|
1384
1397
|
if (input.kind === "missing") {
|
|
1385
|
-
const
|
|
1398
|
+
const summary2 = {
|
|
1386
1399
|
analyst_id: analyst.id,
|
|
1387
1400
|
status: "skipped",
|
|
1388
1401
|
reason: `missing input of kind '${analyst.inputKind}'`,
|
|
@@ -1391,10 +1404,13 @@ var AnalystRegistry = class {
|
|
|
1391
1404
|
cost_usd: 0,
|
|
1392
1405
|
usage: zeroUsage()
|
|
1393
1406
|
};
|
|
1394
|
-
summaries.push(
|
|
1407
|
+
summaries.push(summary2);
|
|
1395
1408
|
log(`[analyst] skip ${analyst.id} \u2014 missing input`, { runId, kind: analyst.inputKind });
|
|
1396
|
-
await
|
|
1397
|
-
|
|
1409
|
+
await waitForHook(
|
|
1410
|
+
hooks.onAfterAnalyze ? () => hooks.onAfterAnalyze?.({ analyst, summary: summary2, findings: [], runId }) : void 0,
|
|
1411
|
+
runSignal
|
|
1412
|
+
);
|
|
1413
|
+
yield { type: "analyst-skipped", summary: summary2 };
|
|
1398
1414
|
continue;
|
|
1399
1415
|
}
|
|
1400
1416
|
const perBudget = allocateBudget(budget, {
|
|
@@ -1414,7 +1430,7 @@ var AnalystRegistry = class {
|
|
|
1414
1430
|
chat: this.options.chat,
|
|
1415
1431
|
tags: runOpts.tags,
|
|
1416
1432
|
log: (msg, fields) => log(`[${analyst.id}] ${msg}`, { runId, correlationId, ...fields }),
|
|
1417
|
-
signal:
|
|
1433
|
+
signal: runSignal,
|
|
1418
1434
|
priorFindings: selectPriorFindings(runOpts.priorFindings, analyst.id),
|
|
1419
1435
|
upstreamFindings: runOpts.chainFindings && allFindings.length > 0 ? [...allFindings] : void 0,
|
|
1420
1436
|
recordUsage: (receipt) => {
|
|
@@ -1422,15 +1438,32 @@ var AnalystRegistry = class {
|
|
|
1422
1438
|
usageReceipts.push(receipt);
|
|
1423
1439
|
}
|
|
1424
1440
|
};
|
|
1425
|
-
await
|
|
1441
|
+
await waitForHook(
|
|
1442
|
+
hooks.onBeforeAnalyze ? () => hooks.onBeforeAnalyze?.({ analyst, ctx, runId }) : void 0,
|
|
1443
|
+
runSignal
|
|
1444
|
+
);
|
|
1445
|
+
if (runSignal?.aborted) {
|
|
1446
|
+
const summary2 = abortedBeforeStartSummary(analyst, runSignal, Date.now() - t0);
|
|
1447
|
+
summaries.push(summary2);
|
|
1448
|
+
log(`[analyst] skip ${analyst.id} \u2014 run aborted`, { runId, reason: summary2.reason });
|
|
1449
|
+
yield { type: "analyst-skipped", summary: summary2 };
|
|
1450
|
+
continue;
|
|
1451
|
+
}
|
|
1426
1452
|
const effectiveBudget = validateEffectiveBudget(ctx.budgetUsd, remainingUsd, analyst.id);
|
|
1427
1453
|
yield {
|
|
1428
1454
|
type: "analyst-started",
|
|
1429
1455
|
analyst_id: analyst.id,
|
|
1430
1456
|
started_at: new Date(t0).toISOString()
|
|
1431
1457
|
};
|
|
1458
|
+
let findings;
|
|
1459
|
+
let summary;
|
|
1432
1460
|
try {
|
|
1433
|
-
|
|
1461
|
+
if (runSignal?.aborted) throw abortReason(runSignal);
|
|
1462
|
+
findings = await waitForOperation(
|
|
1463
|
+
analyst.analyze(input.value, ctx),
|
|
1464
|
+
runSignal,
|
|
1465
|
+
analystAbortGraceMs(analyst)
|
|
1466
|
+
);
|
|
1434
1467
|
const latency = Date.now() - t0;
|
|
1435
1468
|
const usage = resolveUsage(analyst, findings, usageReceipts);
|
|
1436
1469
|
const cost = knownCostUsd(usage);
|
|
@@ -1439,7 +1472,7 @@ var AnalystRegistry = class {
|
|
|
1439
1472
|
remainingUsd = Math.max(0, remainingUsd - budgetDebit(usage, effectiveBudget));
|
|
1440
1473
|
}
|
|
1441
1474
|
allFindings.push(...findings);
|
|
1442
|
-
|
|
1475
|
+
summary = {
|
|
1443
1476
|
analyst_id: analyst.id,
|
|
1444
1477
|
status: "ok",
|
|
1445
1478
|
findings_count: findings.length,
|
|
@@ -1464,12 +1497,10 @@ var AnalystRegistry = class {
|
|
|
1464
1497
|
cost_captured: false
|
|
1465
1498
|
});
|
|
1466
1499
|
}
|
|
1467
|
-
await hooks.onAfterAnalyze?.({ analyst, summary, findings, runId });
|
|
1468
|
-
yield { type: "analyst-completed", summary, findings };
|
|
1469
1500
|
} catch (err) {
|
|
1470
1501
|
const latency = Date.now() - t0;
|
|
1471
1502
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
1472
|
-
const hookFindings = await hooks.onError?.({ analyst, error: e, runId }) ?? [];
|
|
1503
|
+
const hookFindings = runSignal?.aborted ? [] : await hooks.onError?.({ analyst, error: e, runId }) ?? [];
|
|
1473
1504
|
if (hookFindings.length) allFindings.push(...hookFindings);
|
|
1474
1505
|
const usage = resolveUsage(analyst, hookFindings, usageReceipts);
|
|
1475
1506
|
const cost = knownCostUsd(usage);
|
|
@@ -1477,7 +1508,7 @@ var AnalystRegistry = class {
|
|
|
1477
1508
|
if (typeof remainingUsd === "number") {
|
|
1478
1509
|
remainingUsd = Math.max(0, remainingUsd - budgetDebit(usage, effectiveBudget));
|
|
1479
1510
|
}
|
|
1480
|
-
const
|
|
1511
|
+
const summary2 = {
|
|
1481
1512
|
analyst_id: analyst.id,
|
|
1482
1513
|
status: "failed",
|
|
1483
1514
|
findings_count: hookFindings.length,
|
|
@@ -1486,7 +1517,7 @@ var AnalystRegistry = class {
|
|
|
1486
1517
|
usage,
|
|
1487
1518
|
error: { class: e.constructor.name, message: e.message }
|
|
1488
1519
|
};
|
|
1489
|
-
summaries.push(
|
|
1520
|
+
summaries.push(summary2);
|
|
1490
1521
|
log(`[analyst] FAIL ${analyst.id}`, {
|
|
1491
1522
|
runId,
|
|
1492
1523
|
error_class: e.constructor.name,
|
|
@@ -1501,9 +1532,18 @@ var AnalystRegistry = class {
|
|
|
1501
1532
|
cost_captured: false
|
|
1502
1533
|
});
|
|
1503
1534
|
}
|
|
1504
|
-
await
|
|
1505
|
-
|
|
1535
|
+
await waitForHook(
|
|
1536
|
+
hooks.onAfterAnalyze ? () => hooks.onAfterAnalyze?.({ analyst, summary: summary2, findings: hookFindings, runId }) : void 0,
|
|
1537
|
+
runSignal
|
|
1538
|
+
);
|
|
1539
|
+
yield { type: "analyst-completed", summary: summary2, findings: hookFindings };
|
|
1540
|
+
continue;
|
|
1506
1541
|
}
|
|
1542
|
+
await waitForHook(
|
|
1543
|
+
hooks.onAfterAnalyze ? () => hooks.onAfterAnalyze?.({ analyst, summary, findings, runId }) : void 0,
|
|
1544
|
+
runSignal
|
|
1545
|
+
);
|
|
1546
|
+
yield { type: "analyst-completed", summary, findings };
|
|
1507
1547
|
}
|
|
1508
1548
|
const result = {
|
|
1509
1549
|
run_id: runId,
|
|
@@ -1517,7 +1557,10 @@ var AnalystRegistry = class {
|
|
|
1517
1557
|
summaries.map((summary) => summary.usage?.cost ?? { kind: "uncaptured", usd: null })
|
|
1518
1558
|
)
|
|
1519
1559
|
};
|
|
1520
|
-
await
|
|
1560
|
+
await waitForHook(
|
|
1561
|
+
hooks.onComplete ? () => hooks.onComplete?.({ result }) : void 0,
|
|
1562
|
+
runSignal
|
|
1563
|
+
);
|
|
1521
1564
|
yield { type: "run-completed", result };
|
|
1522
1565
|
}
|
|
1523
1566
|
selectAnalysts(opts) {
|
|
@@ -1549,6 +1592,95 @@ var AnalystRegistry = class {
|
|
|
1549
1592
|
}
|
|
1550
1593
|
}
|
|
1551
1594
|
};
|
|
1595
|
+
function validateTimeout(timeoutMs) {
|
|
1596
|
+
if (timeoutMs === void 0) return void 0;
|
|
1597
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) {
|
|
1598
|
+
throw new TypeError(
|
|
1599
|
+
"RegistryRunOpts.timeoutMs must be a positive safe integer no greater than 2147483647"
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
return timeoutMs;
|
|
1603
|
+
}
|
|
1604
|
+
function combineAbortSignals(caller, timeout) {
|
|
1605
|
+
if (!caller) return timeout;
|
|
1606
|
+
if (!timeout) return caller;
|
|
1607
|
+
return AbortSignal.any([caller, timeout]);
|
|
1608
|
+
}
|
|
1609
|
+
async function waitForOperation(operation, signal, abortGraceMs) {
|
|
1610
|
+
if (!signal) return operation;
|
|
1611
|
+
if (signal.aborted) {
|
|
1612
|
+
void operation.catch(() => {
|
|
1613
|
+
});
|
|
1614
|
+
throw abortReason(signal);
|
|
1615
|
+
}
|
|
1616
|
+
return new Promise((resolve, reject) => {
|
|
1617
|
+
let settlementTimer;
|
|
1618
|
+
const cleanup = () => {
|
|
1619
|
+
signal.removeEventListener("abort", onAbort);
|
|
1620
|
+
if (settlementTimer) clearTimeout(settlementTimer);
|
|
1621
|
+
};
|
|
1622
|
+
const onAbort = () => {
|
|
1623
|
+
if (abortGraceMs === 0) {
|
|
1624
|
+
cleanup();
|
|
1625
|
+
reject(abortReason(signal));
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
settlementTimer = setTimeout(() => {
|
|
1629
|
+
cleanup();
|
|
1630
|
+
reject(abortReason(signal));
|
|
1631
|
+
}, abortGraceMs);
|
|
1632
|
+
};
|
|
1633
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1634
|
+
operation.then(
|
|
1635
|
+
(value) => {
|
|
1636
|
+
cleanup();
|
|
1637
|
+
if (signal.aborted) reject(abortReason(signal));
|
|
1638
|
+
else resolve(value);
|
|
1639
|
+
},
|
|
1640
|
+
(error) => {
|
|
1641
|
+
cleanup();
|
|
1642
|
+
reject(signal.aborted ? abortReason(signal) : error);
|
|
1643
|
+
}
|
|
1644
|
+
);
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
async function waitForHook(operation, signal) {
|
|
1648
|
+
if (operation === void 0 || signal?.aborted) return void 0;
|
|
1649
|
+
try {
|
|
1650
|
+
return await waitForOperation(
|
|
1651
|
+
Promise.resolve().then(() => {
|
|
1652
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
1653
|
+
return operation();
|
|
1654
|
+
}),
|
|
1655
|
+
signal,
|
|
1656
|
+
0
|
|
1657
|
+
);
|
|
1658
|
+
} catch (error) {
|
|
1659
|
+
if (signal?.aborted) return void 0;
|
|
1660
|
+
throw error;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
function analystAbortGraceMs(analyst) {
|
|
1664
|
+
if (analyst.cost.kind === "deterministic") return 0;
|
|
1665
|
+
const settlementMs = validateUsageSettlementTimeout(analyst.cost.settlement_timeout_ms);
|
|
1666
|
+
if (settlementMs === 0) return 0;
|
|
1667
|
+
return Math.min(settlementMs + 100, 2147483647);
|
|
1668
|
+
}
|
|
1669
|
+
function abortedBeforeStartSummary(analyst, signal, latencyMs = 0) {
|
|
1670
|
+
const reason = abortReason(signal);
|
|
1671
|
+
return {
|
|
1672
|
+
analyst_id: analyst.id,
|
|
1673
|
+
status: "skipped",
|
|
1674
|
+
reason: `${reason.name}: ${reason.message}`,
|
|
1675
|
+
findings_count: 0,
|
|
1676
|
+
latency_ms: latencyMs,
|
|
1677
|
+
cost_usd: 0,
|
|
1678
|
+
usage: zeroUsage()
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
function abortReason(signal) {
|
|
1682
|
+
return signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted", "AbortError");
|
|
1683
|
+
}
|
|
1552
1684
|
function allocateBudget(policy, args) {
|
|
1553
1685
|
if (!policy) return void 0;
|
|
1554
1686
|
if (policy.allocate) {
|
|
@@ -2533,4 +2665,4 @@ export {
|
|
|
2533
2665
|
aggregateRunScore,
|
|
2534
2666
|
clamp012 as clamp01
|
|
2535
2667
|
};
|
|
2536
|
-
//# sourceMappingURL=chunk-
|
|
2668
|
+
//# sourceMappingURL=chunk-S5TT5R3L.js.map
|