@granular-software/sdk 0.4.31 → 0.4.33
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 +21 -0
- package/dist/agent-evals.d.mts +1 -1
- package/dist/agent-evals.d.ts +1 -1
- package/dist/agent-evals.js +812 -313
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +812 -313
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/cli/index.js +813 -59
- package/dist/{client-CTH1hfwe.d.mts → client-DTvI5MUG.d.mts} +112 -19
- package/dist/{client-CTH1hfwe.d.ts → client-DTvI5MUG.d.ts} +112 -19
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7864 -7665
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7864 -7665
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/agent-evals.mjs
CHANGED
|
@@ -3985,7 +3985,11 @@ var WSClient = class {
|
|
|
3985
3985
|
return null;
|
|
3986
3986
|
}
|
|
3987
3987
|
try {
|
|
3988
|
-
const
|
|
3988
|
+
const payloadSegment = parts[1];
|
|
3989
|
+
if (!payloadSegment) {
|
|
3990
|
+
return null;
|
|
3991
|
+
}
|
|
3992
|
+
const payloadRaw = this.decodeBase64Url(payloadSegment);
|
|
3989
3993
|
const payload = JSON.parse(payloadRaw);
|
|
3990
3994
|
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) {
|
|
3991
3995
|
return null;
|
|
@@ -5062,6 +5066,7 @@ import { ${allImports} } from "./sandbox-tools";
|
|
|
5062
5066
|
this.eventListeners.set(event, []);
|
|
5063
5067
|
}
|
|
5064
5068
|
this.eventListeners.get(event).push(handler);
|
|
5069
|
+
return () => this.off(event, handler);
|
|
5065
5070
|
}
|
|
5066
5071
|
/**
|
|
5067
5072
|
* Unsubscribe from session events
|
|
@@ -5518,6 +5523,16 @@ var JobImplementation = class {
|
|
|
5518
5523
|
handler(message);
|
|
5519
5524
|
}
|
|
5520
5525
|
}
|
|
5526
|
+
return () => {
|
|
5527
|
+
const handlers = this.eventListeners.get(event);
|
|
5528
|
+
if (!handlers) {
|
|
5529
|
+
return;
|
|
5530
|
+
}
|
|
5531
|
+
this.eventListeners.set(
|
|
5532
|
+
event,
|
|
5533
|
+
handlers.filter((current) => current !== handler)
|
|
5534
|
+
);
|
|
5535
|
+
};
|
|
5521
5536
|
}
|
|
5522
5537
|
replayAgentMessage(message) {
|
|
5523
5538
|
this.captureAgentMessage(message);
|
|
@@ -5604,6 +5619,540 @@ var JobImplementation = class {
|
|
|
5604
5619
|
}
|
|
5605
5620
|
};
|
|
5606
5621
|
|
|
5622
|
+
// src/job-presentation.ts
|
|
5623
|
+
var RESPONSE_KEYS = [
|
|
5624
|
+
"reply",
|
|
5625
|
+
"response",
|
|
5626
|
+
"text",
|
|
5627
|
+
"message",
|
|
5628
|
+
"summary",
|
|
5629
|
+
"answer"
|
|
5630
|
+
];
|
|
5631
|
+
var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
|
|
5632
|
+
var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
|
|
5633
|
+
var LIST_KEY_CANDIDATES = ["listName"];
|
|
5634
|
+
var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
|
|
5635
|
+
var VARIABLE_KEY_CANDIDATES = ["variableName"];
|
|
5636
|
+
var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
|
|
5637
|
+
var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
|
|
5638
|
+
function asRecord2(value) {
|
|
5639
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
5640
|
+
return value;
|
|
5641
|
+
}
|
|
5642
|
+
function normalizeText(value) {
|
|
5643
|
+
if (typeof value !== "string") return null;
|
|
5644
|
+
const trimmed = value.trim();
|
|
5645
|
+
if (!trimmed) return null;
|
|
5646
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
5647
|
+
return null;
|
|
5648
|
+
}
|
|
5649
|
+
return trimmed;
|
|
5650
|
+
}
|
|
5651
|
+
function humanTextFromStdout(stdout) {
|
|
5652
|
+
for (const line of [...stdout].reverse()) {
|
|
5653
|
+
const normalized = normalizeText(line);
|
|
5654
|
+
if (!normalized) continue;
|
|
5655
|
+
if (/^[A-Z_]+:/.test(normalized)) continue;
|
|
5656
|
+
return normalized;
|
|
5657
|
+
}
|
|
5658
|
+
return null;
|
|
5659
|
+
}
|
|
5660
|
+
function pushString(target, value) {
|
|
5661
|
+
if (typeof value === "string" && value.trim()) {
|
|
5662
|
+
target.add(value.trim());
|
|
5663
|
+
}
|
|
5664
|
+
}
|
|
5665
|
+
function pushStringArray(target, value) {
|
|
5666
|
+
if (!Array.isArray(value)) return;
|
|
5667
|
+
for (const item of value) {
|
|
5668
|
+
pushString(target, item);
|
|
5669
|
+
}
|
|
5670
|
+
}
|
|
5671
|
+
function collectReferencesFromRecord(record, refs) {
|
|
5672
|
+
for (const key of ENTRY_KEY_CANDIDATES)
|
|
5673
|
+
pushString(refs.entryPaths, record[key]);
|
|
5674
|
+
for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
|
|
5675
|
+
pushStringArray(refs.entryPaths, record[key]);
|
|
5676
|
+
for (const key of LIST_KEY_CANDIDATES)
|
|
5677
|
+
pushString(refs.listNames, record[key]);
|
|
5678
|
+
for (const key of LIST_ARRAY_KEY_CANDIDATES)
|
|
5679
|
+
pushStringArray(refs.listNames, record[key]);
|
|
5680
|
+
for (const key of VARIABLE_KEY_CANDIDATES)
|
|
5681
|
+
pushString(refs.variableNames, record[key]);
|
|
5682
|
+
for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
|
|
5683
|
+
pushStringArray(refs.variableNames, record[key]);
|
|
5684
|
+
}
|
|
5685
|
+
function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
5686
|
+
if (value === null || value === void 0 || depth > 4 || seen.has(value))
|
|
5687
|
+
return;
|
|
5688
|
+
if (typeof value === "string") {
|
|
5689
|
+
const trimmed = value.trim();
|
|
5690
|
+
if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
|
|
5691
|
+
if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
|
|
5692
|
+
if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
|
|
5693
|
+
return;
|
|
5694
|
+
}
|
|
5695
|
+
if (Array.isArray(value)) {
|
|
5696
|
+
seen.add(value);
|
|
5697
|
+
for (const item of value.slice(0, 24)) {
|
|
5698
|
+
scanForHeapReferences(item, heap, refs, depth + 1, seen);
|
|
5699
|
+
}
|
|
5700
|
+
return;
|
|
5701
|
+
}
|
|
5702
|
+
const record = asRecord2(value);
|
|
5703
|
+
if (!record) return;
|
|
5704
|
+
seen.add(value);
|
|
5705
|
+
collectReferencesFromRecord(record, refs);
|
|
5706
|
+
for (const key of UI_CONTAINER_KEYS) {
|
|
5707
|
+
const nested = asRecord2(record[key]);
|
|
5708
|
+
if (nested) collectReferencesFromRecord(nested, refs);
|
|
5709
|
+
}
|
|
5710
|
+
for (const nested of Object.values(record).slice(0, 24)) {
|
|
5711
|
+
scanForHeapReferences(nested, heap, refs, depth + 1, seen);
|
|
5712
|
+
}
|
|
5713
|
+
}
|
|
5714
|
+
function resolveVariablesToReferences(variableNames, heap, refs) {
|
|
5715
|
+
for (const variableName of variableNames) {
|
|
5716
|
+
const variable = heap.variablesByName?.[variableName];
|
|
5717
|
+
if (!variable) continue;
|
|
5718
|
+
if (variable.kind === "entry" && variable.entryPath) {
|
|
5719
|
+
refs.entryPaths.add(variable.entryPath);
|
|
5720
|
+
}
|
|
5721
|
+
if (variable.kind === "list" && variable.listName) {
|
|
5722
|
+
refs.listNames.add(variable.listName);
|
|
5723
|
+
}
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
function sortEntries(entries) {
|
|
5727
|
+
return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
5728
|
+
}
|
|
5729
|
+
function sortLists(lists) {
|
|
5730
|
+
return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
5731
|
+
}
|
|
5732
|
+
function dedupeEntries(entries) {
|
|
5733
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5734
|
+
const result = [];
|
|
5735
|
+
for (const entry of entries) {
|
|
5736
|
+
if (!entry?.path || seen.has(entry.path)) continue;
|
|
5737
|
+
seen.add(entry.path);
|
|
5738
|
+
result.push(entry);
|
|
5739
|
+
}
|
|
5740
|
+
return result;
|
|
5741
|
+
}
|
|
5742
|
+
function dedupeLists(lists) {
|
|
5743
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5744
|
+
const result = [];
|
|
5745
|
+
for (const list of lists) {
|
|
5746
|
+
if (!list?.name || seen.has(list.name)) continue;
|
|
5747
|
+
seen.add(list.name);
|
|
5748
|
+
result.push(list);
|
|
5749
|
+
}
|
|
5750
|
+
return result;
|
|
5751
|
+
}
|
|
5752
|
+
function extractResponseText(result, stdout) {
|
|
5753
|
+
const directText = normalizeText(result);
|
|
5754
|
+
if (directText) return directText;
|
|
5755
|
+
const record = asRecord2(result);
|
|
5756
|
+
if (record) {
|
|
5757
|
+
for (const key of RESPONSE_KEYS) {
|
|
5758
|
+
const normalized = normalizeText(record[key]);
|
|
5759
|
+
if (normalized) return normalized;
|
|
5760
|
+
}
|
|
5761
|
+
for (const containerKey of UI_CONTAINER_KEYS) {
|
|
5762
|
+
const nested = asRecord2(record[containerKey]);
|
|
5763
|
+
if (!nested) continue;
|
|
5764
|
+
for (const key of RESPONSE_KEYS) {
|
|
5765
|
+
const normalized = normalizeText(nested[key]);
|
|
5766
|
+
if (normalized) return normalized;
|
|
5767
|
+
}
|
|
5768
|
+
}
|
|
5769
|
+
}
|
|
5770
|
+
return humanTextFromStdout(stdout);
|
|
5771
|
+
}
|
|
5772
|
+
function fallbackResponseText(entries, lists) {
|
|
5773
|
+
if (entries.length > 0) {
|
|
5774
|
+
return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
|
|
5775
|
+
}
|
|
5776
|
+
if (lists.length > 0) {
|
|
5777
|
+
const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
|
|
5778
|
+
if (emptyOnly) {
|
|
5779
|
+
return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
|
|
5780
|
+
}
|
|
5781
|
+
return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
|
|
5782
|
+
}
|
|
5783
|
+
return null;
|
|
5784
|
+
}
|
|
5785
|
+
function getJobRelatedEntries(heap, jobId) {
|
|
5786
|
+
return sortEntries(
|
|
5787
|
+
Object.values(heap.entriesByPath || {}).filter(
|
|
5788
|
+
(entry) => entry.relatedJobIds?.includes(jobId)
|
|
5789
|
+
)
|
|
5790
|
+
);
|
|
5791
|
+
}
|
|
5792
|
+
function getJobRelatedLists(heap, jobId) {
|
|
5793
|
+
return sortLists(
|
|
5794
|
+
Object.values(heap.listsByName || {}).filter(
|
|
5795
|
+
(list) => list.relatedJobIds?.includes(jobId)
|
|
5796
|
+
)
|
|
5797
|
+
);
|
|
5798
|
+
}
|
|
5799
|
+
function entriesFromLists(lists, heap) {
|
|
5800
|
+
const entries = [];
|
|
5801
|
+
for (const list of lists) {
|
|
5802
|
+
for (const path2 of list.paths || []) {
|
|
5803
|
+
const entry = heap.entriesByPath?.[path2];
|
|
5804
|
+
if (entry) entries.push(entry);
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
return entries;
|
|
5808
|
+
}
|
|
5809
|
+
function resolveJobPresentation({
|
|
5810
|
+
jobId,
|
|
5811
|
+
result,
|
|
5812
|
+
stdout = [],
|
|
5813
|
+
sessionHeap,
|
|
5814
|
+
allowExplicitArtifacts = true
|
|
5815
|
+
}) {
|
|
5816
|
+
const refs = {
|
|
5817
|
+
entryPaths: /* @__PURE__ */ new Set(),
|
|
5818
|
+
listNames: /* @__PURE__ */ new Set(),
|
|
5819
|
+
variableNames: /* @__PURE__ */ new Set()
|
|
5820
|
+
};
|
|
5821
|
+
if (allowExplicitArtifacts) {
|
|
5822
|
+
scanForHeapReferences(result, sessionHeap, refs);
|
|
5823
|
+
resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
|
|
5824
|
+
}
|
|
5825
|
+
const referencedLists = sortLists(
|
|
5826
|
+
[...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
|
|
5827
|
+
);
|
|
5828
|
+
const referencedEntries = sortEntries(
|
|
5829
|
+
[...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
|
|
5830
|
+
);
|
|
5831
|
+
const jobLists = getJobRelatedLists(sessionHeap, jobId);
|
|
5832
|
+
const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
|
|
5833
|
+
const changedEntries = dedupeEntries([
|
|
5834
|
+
...jobEntries,
|
|
5835
|
+
...entriesFromLists(jobLists, sessionHeap)
|
|
5836
|
+
]);
|
|
5837
|
+
const explicitLists = dedupeLists(referencedLists);
|
|
5838
|
+
const explicitEntries = dedupeEntries([
|
|
5839
|
+
...referencedEntries,
|
|
5840
|
+
...entriesFromLists(referencedLists, sessionHeap)
|
|
5841
|
+
]);
|
|
5842
|
+
const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
|
|
5843
|
+
const lists = hasExplicitArtifacts ? explicitLists : jobLists;
|
|
5844
|
+
const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
|
|
5845
|
+
const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
|
|
5846
|
+
return {
|
|
5847
|
+
responseText,
|
|
5848
|
+
entries,
|
|
5849
|
+
lists,
|
|
5850
|
+
changedEntries,
|
|
5851
|
+
changedLists: jobLists,
|
|
5852
|
+
hasExplicitArtifacts
|
|
5853
|
+
};
|
|
5854
|
+
}
|
|
5855
|
+
|
|
5856
|
+
// src/session-transcript.ts
|
|
5857
|
+
var EMPTY_HEAP = {
|
|
5858
|
+
entriesByPath: {},
|
|
5859
|
+
listsByName: {},
|
|
5860
|
+
variablesByName: {}};
|
|
5861
|
+
function asRecord3(value) {
|
|
5862
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
5863
|
+
return value;
|
|
5864
|
+
}
|
|
5865
|
+
function asArray(value) {
|
|
5866
|
+
return Array.isArray(value) ? value : [];
|
|
5867
|
+
}
|
|
5868
|
+
function asNumber(value) {
|
|
5869
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
5870
|
+
}
|
|
5871
|
+
function asString(value) {
|
|
5872
|
+
return typeof value === "string" ? value : void 0;
|
|
5873
|
+
}
|
|
5874
|
+
function trimString(value) {
|
|
5875
|
+
return typeof value === "string" ? value.trim() : "";
|
|
5876
|
+
}
|
|
5877
|
+
function normalizeShowRefs(value) {
|
|
5878
|
+
const record = asRecord3(value);
|
|
5879
|
+
if (!record) return void 0;
|
|
5880
|
+
const normalizeRefs = (input) => {
|
|
5881
|
+
if (!Array.isArray(input)) return void 0;
|
|
5882
|
+
const refs = Array.from(
|
|
5883
|
+
new Set(
|
|
5884
|
+
input.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean)
|
|
5885
|
+
)
|
|
5886
|
+
);
|
|
5887
|
+
return refs.length > 0 ? refs : void 0;
|
|
5888
|
+
};
|
|
5889
|
+
const show = {
|
|
5890
|
+
entryPaths: normalizeRefs(record.entryPaths),
|
|
5891
|
+
listNames: normalizeRefs(record.listNames),
|
|
5892
|
+
variableNames: normalizeRefs(record.variableNames)
|
|
5893
|
+
};
|
|
5894
|
+
return show.entryPaths || show.listNames || show.variableNames ? show : void 0;
|
|
5895
|
+
}
|
|
5896
|
+
function stringifyTranscriptValue(value, fallback = "") {
|
|
5897
|
+
if (typeof value === "string") {
|
|
5898
|
+
return value.trim() || fallback;
|
|
5899
|
+
}
|
|
5900
|
+
if (typeof value === "boolean") {
|
|
5901
|
+
return value ? "Confirmed" : "Canceled";
|
|
5902
|
+
}
|
|
5903
|
+
if (value === void 0) {
|
|
5904
|
+
return fallback;
|
|
5905
|
+
}
|
|
5906
|
+
try {
|
|
5907
|
+
const json = JSON.stringify(value, null, 2);
|
|
5908
|
+
if (!json || json === "undefined") return fallback;
|
|
5909
|
+
return json.length > 2e3 ? `${json.slice(0, 2e3)}...` : json;
|
|
5910
|
+
} catch {
|
|
5911
|
+
return String(value);
|
|
5912
|
+
}
|
|
5913
|
+
}
|
|
5914
|
+
function buildArtifactHistory(show) {
|
|
5915
|
+
if (!show) return void 0;
|
|
5916
|
+
return `[Agent message]
|
|
5917
|
+
${stringifyTranscriptValue({ show }, "")}`;
|
|
5918
|
+
}
|
|
5919
|
+
function normalizeConversationMessage(raw) {
|
|
5920
|
+
const record = asRecord3(raw);
|
|
5921
|
+
if (!record) return null;
|
|
5922
|
+
const role = record.role === "user" ? "user" : record.role === "assistant" ? "assistant" : null;
|
|
5923
|
+
if (!role) return null;
|
|
5924
|
+
const content = trimString(
|
|
5925
|
+
record.content ?? record.reply ?? record.message ?? record.text
|
|
5926
|
+
);
|
|
5927
|
+
const show = normalizeShowRefs(record.show);
|
|
5928
|
+
const id = asString(record.id) || crypto.randomUUID();
|
|
5929
|
+
const timestamp = asNumber(record.timestamp) || asNumber(record.ts) || 0;
|
|
5930
|
+
if (!content && !show) return null;
|
|
5931
|
+
return {
|
|
5932
|
+
id,
|
|
5933
|
+
role,
|
|
5934
|
+
content,
|
|
5935
|
+
timestamp,
|
|
5936
|
+
jobId: asString(record.jobId),
|
|
5937
|
+
promptId: asString(record.promptId),
|
|
5938
|
+
show,
|
|
5939
|
+
historyContent: role === "assistant" ? content ? `[Assistant reply]
|
|
5940
|
+
${content}` : buildArtifactHistory(show) : void 0,
|
|
5941
|
+
source: "conversation"
|
|
5942
|
+
};
|
|
5943
|
+
}
|
|
5944
|
+
function normalizePromptEntries(jobId, rawPrompts, conversationPromptIds) {
|
|
5945
|
+
const promptsById = asRecord3(rawPrompts) || {};
|
|
5946
|
+
return Object.values(promptsById).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
5947
|
+
(left, right) => (asNumber(left.openedAt) || asNumber(left.answeredAt) || 0) - (asNumber(right.openedAt) || asNumber(right.answeredAt) || 0)
|
|
5948
|
+
).flatMap((prompt) => {
|
|
5949
|
+
const promptId = asString(prompt.promptId);
|
|
5950
|
+
if (!promptId || conversationPromptIds.has(promptId)) return [];
|
|
5951
|
+
const title = trimString(prompt.title);
|
|
5952
|
+
const message = trimString(prompt.message);
|
|
5953
|
+
const assistantContent = message || title || "Input required";
|
|
5954
|
+
const openedAt = asNumber(prompt.openedAt) || 0;
|
|
5955
|
+
const answeredAt = asNumber(prompt.answeredAt) || openedAt;
|
|
5956
|
+
const entries = [
|
|
5957
|
+
{
|
|
5958
|
+
id: `prompt:${promptId}:assistant`,
|
|
5959
|
+
role: "assistant",
|
|
5960
|
+
content: assistantContent,
|
|
5961
|
+
timestamp: openedAt,
|
|
5962
|
+
jobId,
|
|
5963
|
+
promptId,
|
|
5964
|
+
historyContent: `[Assistant reply]
|
|
5965
|
+
${assistantContent}`,
|
|
5966
|
+
source: "job_prompt"
|
|
5967
|
+
}
|
|
5968
|
+
];
|
|
5969
|
+
if (Object.prototype.hasOwnProperty.call(prompt, "answer")) {
|
|
5970
|
+
entries.push({
|
|
5971
|
+
id: `prompt:${promptId}:user`,
|
|
5972
|
+
role: "user",
|
|
5973
|
+
content: stringifyTranscriptValue(prompt.answer, ""),
|
|
5974
|
+
timestamp: answeredAt,
|
|
5975
|
+
jobId,
|
|
5976
|
+
promptId,
|
|
5977
|
+
source: "job_prompt"
|
|
5978
|
+
});
|
|
5979
|
+
}
|
|
5980
|
+
return entries;
|
|
5981
|
+
});
|
|
5982
|
+
}
|
|
5983
|
+
function normalizeAgentMessageEntries(jobId, rawMessages) {
|
|
5984
|
+
return asArray(rawMessages).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
5985
|
+
(left, right) => (asNumber(left.timestamp) || asNumber(left.ts) || 0) - (asNumber(right.timestamp) || asNumber(right.ts) || 0)
|
|
5986
|
+
).flatMap((message) => {
|
|
5987
|
+
const messageId = asString(message.messageId) || asString(message.id) || crypto.randomUUID();
|
|
5988
|
+
const timestamp = asNumber(message.timestamp) || asNumber(message.ts) || 0;
|
|
5989
|
+
const reply = trimString(
|
|
5990
|
+
message.reply ?? message.message ?? message.text ?? message.content
|
|
5991
|
+
);
|
|
5992
|
+
const show = normalizeShowRefs(message.show);
|
|
5993
|
+
const entries = [];
|
|
5994
|
+
if (reply) {
|
|
5995
|
+
entries.push({
|
|
5996
|
+
id: `agent:${messageId}:text`,
|
|
5997
|
+
role: "assistant",
|
|
5998
|
+
content: reply,
|
|
5999
|
+
timestamp,
|
|
6000
|
+
jobId,
|
|
6001
|
+
historyContent: `[Assistant reply]
|
|
6002
|
+
${reply}`,
|
|
6003
|
+
source: "job_agent_message"
|
|
6004
|
+
});
|
|
6005
|
+
}
|
|
6006
|
+
if (show) {
|
|
6007
|
+
entries.push({
|
|
6008
|
+
id: `agent:${messageId}:artifacts`,
|
|
6009
|
+
role: "assistant",
|
|
6010
|
+
content: "",
|
|
6011
|
+
timestamp,
|
|
6012
|
+
jobId,
|
|
6013
|
+
show,
|
|
6014
|
+
historyContent: buildArtifactHistory(show),
|
|
6015
|
+
source: "job_agent_message"
|
|
6016
|
+
});
|
|
6017
|
+
}
|
|
6018
|
+
return entries;
|
|
6019
|
+
});
|
|
6020
|
+
}
|
|
6021
|
+
function buildJobFallbackEntries(jobId, job, sessionHeap) {
|
|
6022
|
+
const timestamp = asNumber(job.finishedAt) || asNumber(job.startedAt) || asNumber(job.submittedAt) || 0;
|
|
6023
|
+
const resultPreview = stringifyTranscriptValue(
|
|
6024
|
+
job.result,
|
|
6025
|
+
"No job result recorded."
|
|
6026
|
+
);
|
|
6027
|
+
const presentation = resolveJobPresentation({
|
|
6028
|
+
jobId,
|
|
6029
|
+
result: job.result,
|
|
6030
|
+
stdout: [],
|
|
6031
|
+
sessionHeap
|
|
6032
|
+
});
|
|
6033
|
+
const entries = [];
|
|
6034
|
+
const responseText = presentation.responseText || "";
|
|
6035
|
+
if (responseText) {
|
|
6036
|
+
entries.push({
|
|
6037
|
+
id: `job:${jobId}:result-text`,
|
|
6038
|
+
role: "assistant",
|
|
6039
|
+
content: responseText,
|
|
6040
|
+
timestamp,
|
|
6041
|
+
jobId,
|
|
6042
|
+
historyContent: `[Assistant reply]
|
|
6043
|
+
${responseText}`,
|
|
6044
|
+
source: "job_result"
|
|
6045
|
+
});
|
|
6046
|
+
}
|
|
6047
|
+
const show = {
|
|
6048
|
+
entryPaths: presentation.entries.map((entry) => entry.path),
|
|
6049
|
+
listNames: presentation.lists.map((list) => list.name)
|
|
6050
|
+
};
|
|
6051
|
+
if (show.entryPaths && show.entryPaths.length > 0 || show.listNames && show.listNames.length > 0) {
|
|
6052
|
+
entries.push({
|
|
6053
|
+
id: `job:${jobId}:result-artifacts`,
|
|
6054
|
+
role: "assistant",
|
|
6055
|
+
content: "",
|
|
6056
|
+
timestamp,
|
|
6057
|
+
jobId,
|
|
6058
|
+
show,
|
|
6059
|
+
historyContent: buildArtifactHistory(show),
|
|
6060
|
+
source: "job_result"
|
|
6061
|
+
});
|
|
6062
|
+
}
|
|
6063
|
+
if (entries.length === 0 && trimString(job.error)) {
|
|
6064
|
+
entries.push({
|
|
6065
|
+
id: `job:${jobId}:result-error`,
|
|
6066
|
+
role: "assistant",
|
|
6067
|
+
content: trimString(job.error),
|
|
6068
|
+
timestamp,
|
|
6069
|
+
jobId,
|
|
6070
|
+
historyContent: `[Assistant reply]
|
|
6071
|
+
${trimString(job.error)}`,
|
|
6072
|
+
source: "job_result"
|
|
6073
|
+
});
|
|
6074
|
+
}
|
|
6075
|
+
if (entries.length === 0 && resultPreview && resultPreview !== "No job result recorded.") {
|
|
6076
|
+
entries.push({
|
|
6077
|
+
id: `job:${jobId}:result-preview`,
|
|
6078
|
+
role: "assistant",
|
|
6079
|
+
content: resultPreview,
|
|
6080
|
+
timestamp,
|
|
6081
|
+
jobId,
|
|
6082
|
+
historyContent: `[Assistant reply]
|
|
6083
|
+
${resultPreview}`,
|
|
6084
|
+
source: "job_result"
|
|
6085
|
+
});
|
|
6086
|
+
}
|
|
6087
|
+
return entries;
|
|
6088
|
+
}
|
|
6089
|
+
function buildJobCodeEntry(jobId, job) {
|
|
6090
|
+
const code = trimString(job.source);
|
|
6091
|
+
if (!code) return null;
|
|
6092
|
+
const jobStatus = asString(job.status);
|
|
6093
|
+
const error = jobStatus === "failed" || jobStatus === "canceled" || jobStatus === "timeout" ? trimString(job.error) || `Job ${jobStatus}` : void 0;
|
|
6094
|
+
return {
|
|
6095
|
+
id: `job:${jobId}:code`,
|
|
6096
|
+
role: "assistant",
|
|
6097
|
+
content: "",
|
|
6098
|
+
timestamp: asNumber(job.submittedAt) || asNumber(job.startedAt) || asNumber(job.finishedAt) || 0,
|
|
6099
|
+
jobId,
|
|
6100
|
+
code,
|
|
6101
|
+
jobStatus,
|
|
6102
|
+
jobResultPreview: stringifyTranscriptValue(job.result, "No job result recorded."),
|
|
6103
|
+
error,
|
|
6104
|
+
source: "job_code"
|
|
6105
|
+
};
|
|
6106
|
+
}
|
|
6107
|
+
function buildSessionTranscript(input) {
|
|
6108
|
+
const liveDoc = input.liveDoc || null;
|
|
6109
|
+
const sessionHeap = input.sessionHeap || EMPTY_HEAP;
|
|
6110
|
+
const transcript = [];
|
|
6111
|
+
const conversationMessages = asArray(asRecord3(liveDoc?.conversation)?.messages).map((message) => normalizeConversationMessage(message)).filter((message) => Boolean(message));
|
|
6112
|
+
const conversationPromptIds = new Set(
|
|
6113
|
+
conversationMessages.map((message) => message.promptId).filter((promptId) => Boolean(promptId))
|
|
6114
|
+
);
|
|
6115
|
+
const assistantConversationJobIds = new Set(
|
|
6116
|
+
conversationMessages.filter((message) => message.role === "assistant" && Boolean(message.jobId)).map((message) => message.jobId)
|
|
6117
|
+
);
|
|
6118
|
+
transcript.push(...conversationMessages);
|
|
6119
|
+
const jobsById = asRecord3(asRecord3(liveDoc?.jobs)?.byId) || {};
|
|
6120
|
+
const jobs = Object.values(jobsById).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
6121
|
+
(left, right) => (asNumber(left.submittedAt) || asNumber(left.startedAt) || asNumber(left.finishedAt) || 0) - (asNumber(right.submittedAt) || asNumber(right.startedAt) || asNumber(right.finishedAt) || 0)
|
|
6122
|
+
);
|
|
6123
|
+
for (const job of jobs) {
|
|
6124
|
+
const jobId = asString(job.jobId);
|
|
6125
|
+
if (!jobId) continue;
|
|
6126
|
+
const codeEntry = buildJobCodeEntry(jobId, job);
|
|
6127
|
+
if (codeEntry) {
|
|
6128
|
+
transcript.push(codeEntry);
|
|
6129
|
+
}
|
|
6130
|
+
transcript.push(
|
|
6131
|
+
...normalizePromptEntries(jobId, job.prompts, conversationPromptIds)
|
|
6132
|
+
);
|
|
6133
|
+
if (!assistantConversationJobIds.has(jobId)) {
|
|
6134
|
+
const agentEntries = normalizeAgentMessageEntries(jobId, job.agentMessages);
|
|
6135
|
+
if (agentEntries.length > 0) {
|
|
6136
|
+
transcript.push(...agentEntries);
|
|
6137
|
+
} else {
|
|
6138
|
+
transcript.push(
|
|
6139
|
+
...buildJobFallbackEntries(
|
|
6140
|
+
jobId,
|
|
6141
|
+
job,
|
|
6142
|
+
sessionHeap
|
|
6143
|
+
)
|
|
6144
|
+
);
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
}
|
|
6148
|
+
return transcript.sort((left, right) => {
|
|
6149
|
+
if (left.timestamp !== right.timestamp) {
|
|
6150
|
+
return left.timestamp - right.timestamp;
|
|
6151
|
+
}
|
|
6152
|
+
return left.id.localeCompare(right.id);
|
|
6153
|
+
});
|
|
6154
|
+
}
|
|
6155
|
+
|
|
5607
6156
|
// src/endpoints.ts
|
|
5608
6157
|
var LOCAL_API_URL = "ws://localhost:8787/granular";
|
|
5609
6158
|
var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
|
|
@@ -12064,7 +12613,9 @@ var Environment = class {
|
|
|
12064
12613
|
}
|
|
12065
12614
|
);
|
|
12066
12615
|
if (result.errors?.length) {
|
|
12067
|
-
throw new Error(
|
|
12616
|
+
throw new Error(
|
|
12617
|
+
`defineRelationship failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12618
|
+
);
|
|
12068
12619
|
}
|
|
12069
12620
|
return result.data.at.define_relationship;
|
|
12070
12621
|
}
|
|
@@ -12100,7 +12651,9 @@ var Environment = class {
|
|
|
12100
12651
|
{ path: modelPath }
|
|
12101
12652
|
);
|
|
12102
12653
|
if (result.errors?.length) {
|
|
12103
|
-
throw new Error(
|
|
12654
|
+
throw new Error(
|
|
12655
|
+
`getRelationships failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12656
|
+
);
|
|
12104
12657
|
}
|
|
12105
12658
|
return result.data?.model?.relationships || [];
|
|
12106
12659
|
}
|
|
@@ -12139,7 +12692,9 @@ var Environment = class {
|
|
|
12139
12692
|
{ target: targetPath }
|
|
12140
12693
|
);
|
|
12141
12694
|
if (result.errors?.length) {
|
|
12142
|
-
throw new Error(
|
|
12695
|
+
throw new Error(
|
|
12696
|
+
`attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12697
|
+
);
|
|
12143
12698
|
}
|
|
12144
12699
|
}
|
|
12145
12700
|
/**
|
|
@@ -12174,7 +12729,9 @@ var Environment = class {
|
|
|
12174
12729
|
}`
|
|
12175
12730
|
);
|
|
12176
12731
|
if (result.errors?.length) {
|
|
12177
|
-
throw new Error(
|
|
12732
|
+
throw new Error(
|
|
12733
|
+
`detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12734
|
+
);
|
|
12178
12735
|
}
|
|
12179
12736
|
}
|
|
12180
12737
|
/**
|
|
@@ -12201,7 +12758,9 @@ var Environment = class {
|
|
|
12201
12758
|
}`
|
|
12202
12759
|
);
|
|
12203
12760
|
if (result.errors?.length) {
|
|
12204
|
-
throw new Error(
|
|
12761
|
+
throw new Error(
|
|
12762
|
+
`listRelated failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12763
|
+
);
|
|
12205
12764
|
}
|
|
12206
12765
|
return result.data?.at?.at?.list_related || [];
|
|
12207
12766
|
}
|
|
@@ -12294,7 +12853,9 @@ var Environment = class {
|
|
|
12294
12853
|
async _runGraphql(query, label) {
|
|
12295
12854
|
const result = await this.graphql(query);
|
|
12296
12855
|
if (result.errors?.length) {
|
|
12297
|
-
throw new Error(
|
|
12856
|
+
throw new Error(
|
|
12857
|
+
`${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12858
|
+
);
|
|
12298
12859
|
}
|
|
12299
12860
|
return result.data;
|
|
12300
12861
|
}
|
|
@@ -12639,7 +13200,11 @@ var Environment = class {
|
|
|
12639
13200
|
*/
|
|
12640
13201
|
async recordObject(options) {
|
|
12641
13202
|
const results = await this.recordObjects([options]);
|
|
12642
|
-
|
|
13203
|
+
const result = results[0];
|
|
13204
|
+
if (!result) {
|
|
13205
|
+
throw new Error("recordObject: no result returned for record");
|
|
13206
|
+
}
|
|
13207
|
+
return result;
|
|
12643
13208
|
}
|
|
12644
13209
|
/**
|
|
12645
13210
|
* Batch version of `recordObject()`.
|
|
@@ -12691,7 +13256,13 @@ var Environment = class {
|
|
|
12691
13256
|
);
|
|
12692
13257
|
}
|
|
12693
13258
|
for (let index = 0; index < items.length; index += 1) {
|
|
12694
|
-
|
|
13259
|
+
const item = items[index];
|
|
13260
|
+
if (!item) {
|
|
13261
|
+
throw new Error(
|
|
13262
|
+
`recordObjects: chunk ${plan.chunkIndex + 1} returned an empty result at index ${index}`
|
|
13263
|
+
);
|
|
13264
|
+
}
|
|
13265
|
+
results[plan.offset + index] = item;
|
|
12695
13266
|
}
|
|
12696
13267
|
if (onChunk) {
|
|
12697
13268
|
const info = {
|
|
@@ -12819,6 +13390,9 @@ var EnvironmentSession = class extends Session {
|
|
|
12819
13390
|
get envName() {
|
|
12820
13391
|
return this.environment.envName;
|
|
12821
13392
|
}
|
|
13393
|
+
get tag() {
|
|
13394
|
+
return this.environment.tag;
|
|
13395
|
+
}
|
|
12822
13396
|
get versionId() {
|
|
12823
13397
|
return this.environment.versionId;
|
|
12824
13398
|
}
|
|
@@ -12838,12 +13412,171 @@ var EnvironmentSession = class extends Session {
|
|
|
12838
13412
|
return this.environment.feedback;
|
|
12839
13413
|
}
|
|
12840
13414
|
/**
|
|
12841
|
-
* Return a plain JS
|
|
13415
|
+
* Return a plain JS copy of the synced session heap.
|
|
12842
13416
|
*/
|
|
12843
13417
|
getHeap() {
|
|
12844
13418
|
const doc = this.document;
|
|
12845
13419
|
return normalizeHeapSnapshot(doc?.heap);
|
|
12846
13420
|
}
|
|
13421
|
+
async sessionDataRequest(path2, query) {
|
|
13422
|
+
const searchParams = new URLSearchParams();
|
|
13423
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
13424
|
+
if (value !== null && typeof value !== "undefined" && value !== "") {
|
|
13425
|
+
searchParams.set(key, String(value));
|
|
13426
|
+
}
|
|
13427
|
+
}
|
|
13428
|
+
const queryString = searchParams.toString();
|
|
13429
|
+
const response = await fetch(
|
|
13430
|
+
`${this.environment.runtimeBaseUrl}/orchestrator/ws/sessions/${encodeURIComponent(this.sessionId)}${path2}${queryString ? `?${queryString}` : ""}`,
|
|
13431
|
+
{
|
|
13432
|
+
method: "GET",
|
|
13433
|
+
headers: {
|
|
13434
|
+
Authorization: `Bearer ${this.environment.authToken}`,
|
|
13435
|
+
"Content-Type": "application/json"
|
|
13436
|
+
}
|
|
13437
|
+
}
|
|
13438
|
+
);
|
|
13439
|
+
if (!response.ok) {
|
|
13440
|
+
const errorText = await response.text();
|
|
13441
|
+
throw new Error(
|
|
13442
|
+
`Session data API Error (${response.status}): ${errorText}`
|
|
13443
|
+
);
|
|
13444
|
+
}
|
|
13445
|
+
return response.json();
|
|
13446
|
+
}
|
|
13447
|
+
async collectAllSessionItems(listPage) {
|
|
13448
|
+
const items = [];
|
|
13449
|
+
let cursor = null;
|
|
13450
|
+
do {
|
|
13451
|
+
const page = await listPage({ limit: 500, cursor });
|
|
13452
|
+
items.push(...page.items);
|
|
13453
|
+
cursor = page.nextCursor;
|
|
13454
|
+
} while (cursor);
|
|
13455
|
+
return items;
|
|
13456
|
+
}
|
|
13457
|
+
/**
|
|
13458
|
+
* Fetch the live session document from the runtime DO.
|
|
13459
|
+
*
|
|
13460
|
+
* For history and saved artifacts, prefer the collection APIs on
|
|
13461
|
+
* `messages`, `timeline`, `jobs`, and `heap`.
|
|
13462
|
+
*/
|
|
13463
|
+
async getDocument() {
|
|
13464
|
+
return this.sessionDataRequest("/document");
|
|
13465
|
+
}
|
|
13466
|
+
get messages() {
|
|
13467
|
+
return {
|
|
13468
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13469
|
+
"/messages",
|
|
13470
|
+
options
|
|
13471
|
+
)
|
|
13472
|
+
};
|
|
13473
|
+
}
|
|
13474
|
+
get timeline() {
|
|
13475
|
+
return {
|
|
13476
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13477
|
+
"/timeline",
|
|
13478
|
+
options
|
|
13479
|
+
)
|
|
13480
|
+
};
|
|
13481
|
+
}
|
|
13482
|
+
get jobs() {
|
|
13483
|
+
return {
|
|
13484
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13485
|
+
"/jobs",
|
|
13486
|
+
options
|
|
13487
|
+
),
|
|
13488
|
+
get: (jobId) => this.sessionDataRequest(
|
|
13489
|
+
`/jobs/${encodeURIComponent(jobId)}`
|
|
13490
|
+
)
|
|
13491
|
+
};
|
|
13492
|
+
}
|
|
13493
|
+
get heap() {
|
|
13494
|
+
return {
|
|
13495
|
+
entries: {
|
|
13496
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13497
|
+
"/heap/entries",
|
|
13498
|
+
options
|
|
13499
|
+
),
|
|
13500
|
+
get: (path2) => this.sessionDataRequest(
|
|
13501
|
+
`/heap/entries/${encodeURIComponent(path2)}`
|
|
13502
|
+
)
|
|
13503
|
+
},
|
|
13504
|
+
lists: {
|
|
13505
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13506
|
+
"/heap/lists",
|
|
13507
|
+
options
|
|
13508
|
+
),
|
|
13509
|
+
get: (name) => this.sessionDataRequest(
|
|
13510
|
+
`/heap/lists/${encodeURIComponent(name)}`
|
|
13511
|
+
)
|
|
13512
|
+
}
|
|
13513
|
+
};
|
|
13514
|
+
}
|
|
13515
|
+
get transcript() {
|
|
13516
|
+
return {
|
|
13517
|
+
list: async (options = {}) => {
|
|
13518
|
+
const [messages, jobs, entries, lists] = await Promise.all([
|
|
13519
|
+
this.collectAllSessionItems(this.messages.list),
|
|
13520
|
+
this.collectAllSessionItems(
|
|
13521
|
+
(pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
|
|
13522
|
+
),
|
|
13523
|
+
this.collectAllSessionItems(this.heap.entries.list),
|
|
13524
|
+
this.collectAllSessionItems(this.heap.lists.list)
|
|
13525
|
+
]);
|
|
13526
|
+
const liveDoc = {
|
|
13527
|
+
conversation: { messages },
|
|
13528
|
+
jobs: {
|
|
13529
|
+
byId: Object.fromEntries(
|
|
13530
|
+
jobs.map((job) => {
|
|
13531
|
+
const record = job && typeof job === "object" ? job : null;
|
|
13532
|
+
const id = typeof record?.jobId === "string" ? record.jobId : typeof record?.id === "string" ? record.id : null;
|
|
13533
|
+
return id ? [id, record] : null;
|
|
13534
|
+
}).filter(
|
|
13535
|
+
(entry) => Boolean(entry)
|
|
13536
|
+
)
|
|
13537
|
+
)
|
|
13538
|
+
}
|
|
13539
|
+
};
|
|
13540
|
+
const heap = normalizeHeapSnapshot({
|
|
13541
|
+
entriesByPath: Object.fromEntries(
|
|
13542
|
+
entries.map((entry) => {
|
|
13543
|
+
return entry?.path ? [
|
|
13544
|
+
entry.path,
|
|
13545
|
+
entry
|
|
13546
|
+
] : null;
|
|
13547
|
+
}).filter(
|
|
13548
|
+
(entry) => Boolean(entry)
|
|
13549
|
+
)
|
|
13550
|
+
),
|
|
13551
|
+
listsByName: Object.fromEntries(
|
|
13552
|
+
lists.map((list) => {
|
|
13553
|
+
return list?.name ? [list.name, list] : null;
|
|
13554
|
+
}).filter(
|
|
13555
|
+
(entry) => Boolean(entry)
|
|
13556
|
+
)
|
|
13557
|
+
),
|
|
13558
|
+
variablesByName: this.getHeap().variablesByName,
|
|
13559
|
+
updatedAt: Date.now()
|
|
13560
|
+
});
|
|
13561
|
+
const allItems = buildSessionTranscript({
|
|
13562
|
+
liveDoc,
|
|
13563
|
+
sessionHeap: heap
|
|
13564
|
+
});
|
|
13565
|
+
const limit = Math.max(
|
|
13566
|
+
1,
|
|
13567
|
+
Math.min(500, Math.floor(options.limit ?? 100))
|
|
13568
|
+
);
|
|
13569
|
+
const offset = typeof options.cursor === "string" ? Number.parseInt(options.cursor, 10) || 0 : 0;
|
|
13570
|
+
const items = allItems.slice(offset, offset + limit);
|
|
13571
|
+
const nextOffset = offset + items.length;
|
|
13572
|
+
return {
|
|
13573
|
+
items,
|
|
13574
|
+
nextCursor: nextOffset < allItems.length ? String(nextOffset) : null,
|
|
13575
|
+
totalCount: allItems.length
|
|
13576
|
+
};
|
|
13577
|
+
}
|
|
13578
|
+
};
|
|
13579
|
+
}
|
|
12847
13580
|
async graphql(query, variables) {
|
|
12848
13581
|
return this.environment.graphql(query, variables);
|
|
12849
13582
|
}
|
|
@@ -14053,15 +14786,15 @@ var Granular = class _Granular {
|
|
|
14053
14786
|
};
|
|
14054
14787
|
|
|
14055
14788
|
// src/agent-harness.ts
|
|
14056
|
-
function
|
|
14789
|
+
function asRecord4(value) {
|
|
14057
14790
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
14058
14791
|
return value;
|
|
14059
14792
|
}
|
|
14060
|
-
function
|
|
14793
|
+
function asArray2(value) {
|
|
14061
14794
|
return Array.isArray(value) ? value : [];
|
|
14062
14795
|
}
|
|
14063
14796
|
function toSortedRecords(value) {
|
|
14064
|
-
return Object.values(
|
|
14797
|
+
return Object.values(asRecord4(value) || {}).map((entry) => asRecord4(entry)).filter((entry) => Boolean(entry));
|
|
14065
14798
|
}
|
|
14066
14799
|
function uniqueStrings(values, maxCount) {
|
|
14067
14800
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -14087,7 +14820,7 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
|
|
|
14087
14820
|
const headline = entry.label || entry.id || entry.path || "Unknown";
|
|
14088
14821
|
const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
|
|
14089
14822
|
const classLabel = entry.className || "unknown";
|
|
14090
|
-
const preview =
|
|
14823
|
+
const preview = asArray2(entry.fields).filter(
|
|
14091
14824
|
(field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
|
|
14092
14825
|
).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
|
|
14093
14826
|
return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
|
|
@@ -14224,14 +14957,14 @@ function normalizeActionSummaryForPrompt(line) {
|
|
|
14224
14957
|
return line.replace(/\blimit=/g, "perPage=").replace(/\blimit:/g, "perPage:");
|
|
14225
14958
|
}
|
|
14226
14959
|
function getCurrentClosureId(liveDoc) {
|
|
14227
|
-
const loop =
|
|
14960
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14228
14961
|
return typeof loop?.currentClosureId === "string" ? loop.currentClosureId : null;
|
|
14229
14962
|
}
|
|
14230
14963
|
function getLatestClosure(liveDoc) {
|
|
14231
|
-
const loop =
|
|
14964
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14232
14965
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14233
|
-
const closuresById =
|
|
14234
|
-
const currentClosure = currentClosureId ?
|
|
14966
|
+
const closuresById = asRecord4(loop?.closuresById) || {};
|
|
14967
|
+
const currentClosure = currentClosureId ? asRecord4(closuresById[currentClosureId]) : null;
|
|
14235
14968
|
if (currentClosure) {
|
|
14236
14969
|
return {
|
|
14237
14970
|
...currentClosure,
|
|
@@ -14240,7 +14973,7 @@ function getLatestClosure(liveDoc) {
|
|
|
14240
14973
|
}
|
|
14241
14974
|
const closures = [];
|
|
14242
14975
|
for (const [closureId, value] of Object.entries(closuresById)) {
|
|
14243
|
-
const record =
|
|
14976
|
+
const record = asRecord4(value);
|
|
14244
14977
|
if (!record) continue;
|
|
14245
14978
|
closures.push({ ...record, closureId });
|
|
14246
14979
|
}
|
|
@@ -14277,10 +15010,10 @@ function getJobTimestamp(job) {
|
|
|
14277
15010
|
return Number(job.finishedAt) || Number(job.startedAt) || Number(job.submittedAt) || 0;
|
|
14278
15011
|
}
|
|
14279
15012
|
function getJobRecords(liveDoc) {
|
|
14280
|
-
const jobsById =
|
|
15013
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14281
15014
|
const jobs = [];
|
|
14282
15015
|
for (const [jobId, value] of Object.entries(jobsById)) {
|
|
14283
|
-
const record =
|
|
15016
|
+
const record = asRecord4(value);
|
|
14284
15017
|
if (!record) continue;
|
|
14285
15018
|
jobs.push({ ...record, jobId });
|
|
14286
15019
|
}
|
|
@@ -14290,7 +15023,7 @@ function getJobRecords(liveDoc) {
|
|
|
14290
15023
|
return jobs;
|
|
14291
15024
|
}
|
|
14292
15025
|
function getPromptRecordsFromJobs(liveDoc) {
|
|
14293
|
-
return getJobRecords(liveDoc).flatMap((job) => Object.values(
|
|
15026
|
+
return getJobRecords(liveDoc).flatMap((job) => Object.values(asRecord4(job.prompts) || {})).map((prompt) => asRecord4(prompt)).filter((prompt) => Boolean(prompt));
|
|
14294
15027
|
}
|
|
14295
15028
|
function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
14296
15029
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
@@ -14305,15 +15038,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14305
15038
|
const openDecisionIds = [];
|
|
14306
15039
|
const openPromptIds = [];
|
|
14307
15040
|
for (const job of jobs) {
|
|
14308
|
-
for (const line of
|
|
15041
|
+
for (const line of asArray2(job.actionSummary)) {
|
|
14309
15042
|
if (typeof line === "string" && line.trim()) {
|
|
14310
15043
|
actionSummaryLines.push(line.trim());
|
|
14311
15044
|
}
|
|
14312
15045
|
}
|
|
14313
|
-
for (const rawEvent of
|
|
14314
|
-
const event =
|
|
14315
|
-
const details =
|
|
14316
|
-
const outcome =
|
|
15046
|
+
for (const rawEvent of asArray2(job.actionTrace)) {
|
|
15047
|
+
const event = asRecord4(rawEvent);
|
|
15048
|
+
const details = asRecord4(event?.details);
|
|
15049
|
+
const outcome = asRecord4(event?.outcome);
|
|
14317
15050
|
if (typeof details?.name === "string") {
|
|
14318
15051
|
variableNames.push(details.name);
|
|
14319
15052
|
}
|
|
@@ -14340,7 +15073,7 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14340
15073
|
}
|
|
14341
15074
|
}
|
|
14342
15075
|
}
|
|
14343
|
-
const loop =
|
|
15076
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14344
15077
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14345
15078
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
14346
15079
|
const status = typeof task.status === "string" ? task.status : "pending";
|
|
@@ -14388,15 +15121,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14388
15121
|
openPromptIds.push(prompt.id);
|
|
14389
15122
|
}
|
|
14390
15123
|
}
|
|
14391
|
-
const heap =
|
|
14392
|
-
const variablesByName =
|
|
14393
|
-
const listsByName =
|
|
15124
|
+
const heap = asRecord4(liveDoc?.heap);
|
|
15125
|
+
const variablesByName = asRecord4(heap?.variablesByName) || {};
|
|
15126
|
+
const listsByName = asRecord4(heap?.listsByName) || {};
|
|
14394
15127
|
const recentHints = extractFocusHintsFromActionSummary(actionSummaryLines);
|
|
14395
15128
|
variableNames.push(...recentHints.variableNames);
|
|
14396
15129
|
listNames.push(...recentHints.listNames);
|
|
14397
15130
|
entryPaths.push(...recentHints.entryPaths);
|
|
14398
15131
|
for (const variableName of uniqueStrings(variableNames)) {
|
|
14399
|
-
const variable =
|
|
15132
|
+
const variable = asRecord4(variablesByName[variableName]);
|
|
14400
15133
|
if (!variable) continue;
|
|
14401
15134
|
if (typeof variable.listName === "string") {
|
|
14402
15135
|
listNames.push(variable.listName);
|
|
@@ -14406,13 +15139,13 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14406
15139
|
}
|
|
14407
15140
|
}
|
|
14408
15141
|
for (const listName of uniqueStrings(listNames)) {
|
|
14409
|
-
const list =
|
|
14410
|
-
for (const path2 of
|
|
15142
|
+
const list = asRecord4(listsByName[listName]);
|
|
15143
|
+
for (const path2 of asArray2(list?.paths).slice(0, 4)) {
|
|
14411
15144
|
entryPaths.push(path2);
|
|
14412
15145
|
}
|
|
14413
15146
|
}
|
|
14414
15147
|
if (variableNames.length === 0 && boundary.reason !== "request_start") {
|
|
14415
|
-
const recentVariables = Object.values(variablesByName).map((value) =>
|
|
15148
|
+
const recentVariables = Object.values(variablesByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, 3);
|
|
14416
15149
|
for (const variable of recentVariables) {
|
|
14417
15150
|
if (typeof variable.name === "string") {
|
|
14418
15151
|
variableNames.push(variable.name);
|
|
@@ -14495,12 +15228,12 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14495
15228
|
}
|
|
14496
15229
|
function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
14497
15230
|
if (pendingPrompts.length > 0) return true;
|
|
14498
|
-
const jobsById =
|
|
15231
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14499
15232
|
for (const job of Object.values(jobsById)) {
|
|
14500
|
-
const prompts =
|
|
15233
|
+
const prompts = asRecord4(asRecord4(job)?.prompts);
|
|
14501
15234
|
if (!prompts) continue;
|
|
14502
15235
|
for (const prompt of Object.values(prompts)) {
|
|
14503
|
-
const record =
|
|
15236
|
+
const record = asRecord4(prompt);
|
|
14504
15237
|
if (record?.status === "open") return true;
|
|
14505
15238
|
}
|
|
14506
15239
|
}
|
|
@@ -14508,7 +15241,7 @@ function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
|
14508
15241
|
}
|
|
14509
15242
|
function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
14510
15243
|
const lines = [];
|
|
14511
|
-
const loop =
|
|
15244
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14512
15245
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
14513
15246
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14514
15247
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
@@ -14568,8 +15301,8 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14568
15301
|
const title = typeof decision.title === "string" && decision.title.trim() ? decision.title.trim() : "Decision";
|
|
14569
15302
|
const decisionId = typeof decision.decisionId === "string" ? decision.decisionId : "unknown";
|
|
14570
15303
|
if (status === "open") {
|
|
14571
|
-
const candidatePreview =
|
|
14572
|
-
const record =
|
|
15304
|
+
const candidatePreview = asArray2(decision.candidates).slice(0, 3).map((candidate) => {
|
|
15305
|
+
const record = asRecord4(candidate);
|
|
14573
15306
|
if (!record) return null;
|
|
14574
15307
|
const candidateId = typeof record.id === "string" ? record.id : "unknown";
|
|
14575
15308
|
const candidateLabel = typeof record.label === "string" && record.label.trim() ? record.label.trim() : candidateId;
|
|
@@ -14579,7 +15312,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14579
15312
|
`- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`
|
|
14580
15313
|
);
|
|
14581
15314
|
} else {
|
|
14582
|
-
const selected =
|
|
15315
|
+
const selected = asRecord4(decision.selected);
|
|
14583
15316
|
const label = typeof selected?.label === "string" ? selected.label : typeof selected?.id === "string" ? selected.id : "unknown";
|
|
14584
15317
|
lines.push(`- [resolved] ${title} (${decisionId}) -> ${label}`);
|
|
14585
15318
|
}
|
|
@@ -14592,12 +15325,12 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14592
15325
|
title: prompt.title,
|
|
14593
15326
|
message: prompt.message
|
|
14594
15327
|
})),
|
|
14595
|
-
...Object.values(
|
|
15328
|
+
...Object.values(asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord4(asRecord4(job)?.prompts) || {})).map((prompt) => asRecord4(prompt)).filter(
|
|
14596
15329
|
(prompt) => Boolean(prompt && prompt.status === "open")
|
|
14597
15330
|
)
|
|
14598
15331
|
];
|
|
14599
15332
|
const visiblePrompts = boundary.reason === "request_start" ? openPrompts.filter((prompt) => {
|
|
14600
|
-
const promptRecord =
|
|
15333
|
+
const promptRecord = asRecord4(prompt);
|
|
14601
15334
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
14602
15335
|
const promptId = typeof promptRecord?.id === "string" ? promptRecord.id : typeof prompt.id === "string" ? prompt.id : null;
|
|
14603
15336
|
return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some(
|
|
@@ -14616,7 +15349,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14616
15349
|
}
|
|
14617
15350
|
}
|
|
14618
15351
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14619
|
-
const closureRecord = currentClosureId ?
|
|
15352
|
+
const closureRecord = currentClosureId ? asRecord4(asRecord4(loop?.closuresById)?.[currentClosureId]) : null;
|
|
14620
15353
|
const visibleClosure = closureRecord && (boundary.reason !== "request_start" || (Number(closureRecord.createdAt) || 0) >= boundary.timestamp) ? closureRecord : null;
|
|
14621
15354
|
lines.push("", "Loop Closure:");
|
|
14622
15355
|
if (visibleClosure) {
|
|
@@ -14629,10 +15362,10 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14629
15362
|
return lines.join("\n");
|
|
14630
15363
|
}
|
|
14631
15364
|
function projectHeapSummary(heap, options) {
|
|
14632
|
-
const heapRecord =
|
|
14633
|
-
const entriesByPath =
|
|
14634
|
-
const listsByName =
|
|
14635
|
-
const variablesByName =
|
|
15365
|
+
const heapRecord = asRecord4(heap) || {};
|
|
15366
|
+
const entriesByPath = asRecord4(heapRecord.entriesByPath) || {};
|
|
15367
|
+
const listsByName = asRecord4(heapRecord.listsByName) || {};
|
|
15368
|
+
const variablesByName = asRecord4(heapRecord.variablesByName) || {};
|
|
14636
15369
|
const focusedVariableNames = new Set(
|
|
14637
15370
|
uniqueStrings(options?.focus?.variableNames || [])
|
|
14638
15371
|
);
|
|
@@ -14647,7 +15380,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14647
15380
|
const maxVariables = options?.maxVariables ?? (hasFocus ? 4 : 6);
|
|
14648
15381
|
const maxLists = options?.maxLists ?? (hasFocus ? 3 : 4);
|
|
14649
15382
|
const maxEntries = options?.maxEntries ?? (hasFocus ? 5 : 6);
|
|
14650
|
-
const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) =>
|
|
15383
|
+
const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).sort((left, right) => {
|
|
14651
15384
|
const leftFocused = left.name && focusedVariableNames.has(left.name) ? 1 : 0;
|
|
14652
15385
|
const rightFocused = right.name && focusedVariableNames.has(right.name) ? 1 : 0;
|
|
14653
15386
|
return rightFocused - leftFocused || (right.updatedAt || 0) - (left.updatedAt || 0);
|
|
@@ -14661,7 +15394,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14661
15394
|
for (const variable of variables) {
|
|
14662
15395
|
if (variable.entryPath) referencedPaths.add(variable.entryPath);
|
|
14663
15396
|
if (variable.listName) {
|
|
14664
|
-
const list =
|
|
15397
|
+
const list = asRecord4(
|
|
14665
15398
|
listsByName[variable.listName]
|
|
14666
15399
|
);
|
|
14667
15400
|
for (const path2 of list?.paths || []) referencedPaths.add(path2);
|
|
@@ -14670,10 +15403,10 @@ function projectHeapSummary(heap, options) {
|
|
|
14670
15403
|
for (const path2 of focusedEntryPaths) {
|
|
14671
15404
|
referencedPaths.add(path2);
|
|
14672
15405
|
}
|
|
14673
|
-
const visibleLists = Object.values(listsByName).map((value) =>
|
|
15406
|
+
const visibleLists = Object.values(listsByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter(
|
|
14674
15407
|
(list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))
|
|
14675
15408
|
).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
|
|
14676
|
-
const visibleEntries = Object.values(entriesByPath).map((value) =>
|
|
15409
|
+
const visibleEntries = Object.values(entriesByPath).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter((entry) => entry.path && referencedPaths.has(entry.path)).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxEntries);
|
|
14677
15410
|
const lines = [];
|
|
14678
15411
|
lines.push("Variables:");
|
|
14679
15412
|
if (variables.length === 0) {
|
|
@@ -14687,7 +15420,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14687
15420
|
continue;
|
|
14688
15421
|
}
|
|
14689
15422
|
if (variable.kind === "entry") {
|
|
14690
|
-
const entry = variable.entryPath ?
|
|
15423
|
+
const entry = variable.entryPath ? asRecord4(
|
|
14691
15424
|
entriesByPath[variable.entryPath]
|
|
14692
15425
|
) : null;
|
|
14693
15426
|
lines.push(
|
|
@@ -14695,7 +15428,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14695
15428
|
);
|
|
14696
15429
|
continue;
|
|
14697
15430
|
}
|
|
14698
|
-
const list = variable.listName ?
|
|
15431
|
+
const list = variable.listName ? asRecord4(listsByName[variable.listName]) : null;
|
|
14699
15432
|
lines.push(
|
|
14700
15433
|
`- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`
|
|
14701
15434
|
);
|
|
@@ -14728,7 +15461,7 @@ function createHarnessVerifierSnapshot(input) {
|
|
|
14728
15461
|
input.projectionOptions
|
|
14729
15462
|
);
|
|
14730
15463
|
const heapDigest = hashString(
|
|
14731
|
-
projectHeapSummary(
|
|
15464
|
+
projectHeapSummary(asRecord4(input.liveDoc?.heap), {
|
|
14732
15465
|
focus: workflowFocus
|
|
14733
15466
|
})
|
|
14734
15467
|
) || "00000000";
|
|
@@ -15054,246 +15787,12 @@ ${loopBlock}
|
|
|
15054
15787
|
- Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
|
|
15055
15788
|
}
|
|
15056
15789
|
|
|
15057
|
-
// src/job-presentation.ts
|
|
15058
|
-
var RESPONSE_KEYS = [
|
|
15059
|
-
"reply",
|
|
15060
|
-
"response",
|
|
15061
|
-
"text",
|
|
15062
|
-
"message",
|
|
15063
|
-
"summary",
|
|
15064
|
-
"answer"
|
|
15065
|
-
];
|
|
15066
|
-
var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
|
|
15067
|
-
var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
|
|
15068
|
-
var LIST_KEY_CANDIDATES = ["listName"];
|
|
15069
|
-
var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
|
|
15070
|
-
var VARIABLE_KEY_CANDIDATES = ["variableName"];
|
|
15071
|
-
var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
|
|
15072
|
-
var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
|
|
15073
|
-
function asRecord3(value) {
|
|
15074
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15075
|
-
return value;
|
|
15076
|
-
}
|
|
15077
|
-
function normalizeText(value) {
|
|
15078
|
-
if (typeof value !== "string") return null;
|
|
15079
|
-
const trimmed = value.trim();
|
|
15080
|
-
if (!trimmed) return null;
|
|
15081
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
15082
|
-
return null;
|
|
15083
|
-
}
|
|
15084
|
-
return trimmed;
|
|
15085
|
-
}
|
|
15086
|
-
function humanTextFromStdout(stdout) {
|
|
15087
|
-
for (const line of [...stdout].reverse()) {
|
|
15088
|
-
const normalized = normalizeText(line);
|
|
15089
|
-
if (!normalized) continue;
|
|
15090
|
-
if (/^[A-Z_]+:/.test(normalized)) continue;
|
|
15091
|
-
return normalized;
|
|
15092
|
-
}
|
|
15093
|
-
return null;
|
|
15094
|
-
}
|
|
15095
|
-
function pushString(target, value) {
|
|
15096
|
-
if (typeof value === "string" && value.trim()) {
|
|
15097
|
-
target.add(value.trim());
|
|
15098
|
-
}
|
|
15099
|
-
}
|
|
15100
|
-
function pushStringArray(target, value) {
|
|
15101
|
-
if (!Array.isArray(value)) return;
|
|
15102
|
-
for (const item of value) {
|
|
15103
|
-
pushString(target, item);
|
|
15104
|
-
}
|
|
15105
|
-
}
|
|
15106
|
-
function collectReferencesFromRecord(record, refs) {
|
|
15107
|
-
for (const key of ENTRY_KEY_CANDIDATES)
|
|
15108
|
-
pushString(refs.entryPaths, record[key]);
|
|
15109
|
-
for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
|
|
15110
|
-
pushStringArray(refs.entryPaths, record[key]);
|
|
15111
|
-
for (const key of LIST_KEY_CANDIDATES)
|
|
15112
|
-
pushString(refs.listNames, record[key]);
|
|
15113
|
-
for (const key of LIST_ARRAY_KEY_CANDIDATES)
|
|
15114
|
-
pushStringArray(refs.listNames, record[key]);
|
|
15115
|
-
for (const key of VARIABLE_KEY_CANDIDATES)
|
|
15116
|
-
pushString(refs.variableNames, record[key]);
|
|
15117
|
-
for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
|
|
15118
|
-
pushStringArray(refs.variableNames, record[key]);
|
|
15119
|
-
}
|
|
15120
|
-
function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
15121
|
-
if (value === null || value === void 0 || depth > 4 || seen.has(value))
|
|
15122
|
-
return;
|
|
15123
|
-
if (typeof value === "string") {
|
|
15124
|
-
const trimmed = value.trim();
|
|
15125
|
-
if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
|
|
15126
|
-
if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
|
|
15127
|
-
if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
|
|
15128
|
-
return;
|
|
15129
|
-
}
|
|
15130
|
-
if (Array.isArray(value)) {
|
|
15131
|
-
seen.add(value);
|
|
15132
|
-
for (const item of value.slice(0, 24)) {
|
|
15133
|
-
scanForHeapReferences(item, heap, refs, depth + 1, seen);
|
|
15134
|
-
}
|
|
15135
|
-
return;
|
|
15136
|
-
}
|
|
15137
|
-
const record = asRecord3(value);
|
|
15138
|
-
if (!record) return;
|
|
15139
|
-
seen.add(value);
|
|
15140
|
-
collectReferencesFromRecord(record, refs);
|
|
15141
|
-
for (const key of UI_CONTAINER_KEYS) {
|
|
15142
|
-
const nested = asRecord3(record[key]);
|
|
15143
|
-
if (nested) collectReferencesFromRecord(nested, refs);
|
|
15144
|
-
}
|
|
15145
|
-
for (const nested of Object.values(record).slice(0, 24)) {
|
|
15146
|
-
scanForHeapReferences(nested, heap, refs, depth + 1, seen);
|
|
15147
|
-
}
|
|
15148
|
-
}
|
|
15149
|
-
function resolveVariablesToReferences(variableNames, heap, refs) {
|
|
15150
|
-
for (const variableName of variableNames) {
|
|
15151
|
-
const variable = heap.variablesByName?.[variableName];
|
|
15152
|
-
if (!variable) continue;
|
|
15153
|
-
if (variable.kind === "entry" && variable.entryPath) {
|
|
15154
|
-
refs.entryPaths.add(variable.entryPath);
|
|
15155
|
-
}
|
|
15156
|
-
if (variable.kind === "list" && variable.listName) {
|
|
15157
|
-
refs.listNames.add(variable.listName);
|
|
15158
|
-
}
|
|
15159
|
-
}
|
|
15160
|
-
}
|
|
15161
|
-
function sortEntries(entries) {
|
|
15162
|
-
return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
15163
|
-
}
|
|
15164
|
-
function sortLists(lists) {
|
|
15165
|
-
return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
15166
|
-
}
|
|
15167
|
-
function dedupeEntries(entries) {
|
|
15168
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15169
|
-
const result = [];
|
|
15170
|
-
for (const entry of entries) {
|
|
15171
|
-
if (!entry?.path || seen.has(entry.path)) continue;
|
|
15172
|
-
seen.add(entry.path);
|
|
15173
|
-
result.push(entry);
|
|
15174
|
-
}
|
|
15175
|
-
return result;
|
|
15176
|
-
}
|
|
15177
|
-
function dedupeLists(lists) {
|
|
15178
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15179
|
-
const result = [];
|
|
15180
|
-
for (const list of lists) {
|
|
15181
|
-
if (!list?.name || seen.has(list.name)) continue;
|
|
15182
|
-
seen.add(list.name);
|
|
15183
|
-
result.push(list);
|
|
15184
|
-
}
|
|
15185
|
-
return result;
|
|
15186
|
-
}
|
|
15187
|
-
function extractResponseText(result, stdout) {
|
|
15188
|
-
const directText = normalizeText(result);
|
|
15189
|
-
if (directText) return directText;
|
|
15190
|
-
const record = asRecord3(result);
|
|
15191
|
-
if (record) {
|
|
15192
|
-
for (const key of RESPONSE_KEYS) {
|
|
15193
|
-
const normalized = normalizeText(record[key]);
|
|
15194
|
-
if (normalized) return normalized;
|
|
15195
|
-
}
|
|
15196
|
-
for (const containerKey of UI_CONTAINER_KEYS) {
|
|
15197
|
-
const nested = asRecord3(record[containerKey]);
|
|
15198
|
-
if (!nested) continue;
|
|
15199
|
-
for (const key of RESPONSE_KEYS) {
|
|
15200
|
-
const normalized = normalizeText(nested[key]);
|
|
15201
|
-
if (normalized) return normalized;
|
|
15202
|
-
}
|
|
15203
|
-
}
|
|
15204
|
-
}
|
|
15205
|
-
return humanTextFromStdout(stdout);
|
|
15206
|
-
}
|
|
15207
|
-
function fallbackResponseText(entries, lists) {
|
|
15208
|
-
if (entries.length > 0) {
|
|
15209
|
-
return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
|
|
15210
|
-
}
|
|
15211
|
-
if (lists.length > 0) {
|
|
15212
|
-
const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
|
|
15213
|
-
if (emptyOnly) {
|
|
15214
|
-
return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
|
|
15215
|
-
}
|
|
15216
|
-
return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
|
|
15217
|
-
}
|
|
15218
|
-
return null;
|
|
15219
|
-
}
|
|
15220
|
-
function getJobRelatedEntries(heap, jobId) {
|
|
15221
|
-
return sortEntries(
|
|
15222
|
-
Object.values(heap.entriesByPath || {}).filter(
|
|
15223
|
-
(entry) => entry.relatedJobIds?.includes(jobId)
|
|
15224
|
-
)
|
|
15225
|
-
);
|
|
15226
|
-
}
|
|
15227
|
-
function getJobRelatedLists(heap, jobId) {
|
|
15228
|
-
return sortLists(
|
|
15229
|
-
Object.values(heap.listsByName || {}).filter(
|
|
15230
|
-
(list) => list.relatedJobIds?.includes(jobId)
|
|
15231
|
-
)
|
|
15232
|
-
);
|
|
15233
|
-
}
|
|
15234
|
-
function entriesFromLists(lists, heap) {
|
|
15235
|
-
const entries = [];
|
|
15236
|
-
for (const list of lists) {
|
|
15237
|
-
for (const path2 of list.paths || []) {
|
|
15238
|
-
const entry = heap.entriesByPath?.[path2];
|
|
15239
|
-
if (entry) entries.push(entry);
|
|
15240
|
-
}
|
|
15241
|
-
}
|
|
15242
|
-
return entries;
|
|
15243
|
-
}
|
|
15244
|
-
function resolveJobPresentation({
|
|
15245
|
-
jobId,
|
|
15246
|
-
result,
|
|
15247
|
-
stdout = [],
|
|
15248
|
-
sessionHeap,
|
|
15249
|
-
allowExplicitArtifacts = true
|
|
15250
|
-
}) {
|
|
15251
|
-
const refs = {
|
|
15252
|
-
entryPaths: /* @__PURE__ */ new Set(),
|
|
15253
|
-
listNames: /* @__PURE__ */ new Set(),
|
|
15254
|
-
variableNames: /* @__PURE__ */ new Set()
|
|
15255
|
-
};
|
|
15256
|
-
if (allowExplicitArtifacts) {
|
|
15257
|
-
scanForHeapReferences(result, sessionHeap, refs);
|
|
15258
|
-
resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
|
|
15259
|
-
}
|
|
15260
|
-
const referencedLists = sortLists(
|
|
15261
|
-
[...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
|
|
15262
|
-
);
|
|
15263
|
-
const referencedEntries = sortEntries(
|
|
15264
|
-
[...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
|
|
15265
|
-
);
|
|
15266
|
-
const jobLists = getJobRelatedLists(sessionHeap, jobId);
|
|
15267
|
-
const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
|
|
15268
|
-
const changedEntries = dedupeEntries([
|
|
15269
|
-
...jobEntries,
|
|
15270
|
-
...entriesFromLists(jobLists, sessionHeap)
|
|
15271
|
-
]);
|
|
15272
|
-
const explicitLists = dedupeLists(referencedLists);
|
|
15273
|
-
const explicitEntries = dedupeEntries([
|
|
15274
|
-
...referencedEntries,
|
|
15275
|
-
...entriesFromLists(referencedLists, sessionHeap)
|
|
15276
|
-
]);
|
|
15277
|
-
const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
|
|
15278
|
-
const lists = hasExplicitArtifacts ? explicitLists : jobLists;
|
|
15279
|
-
const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
|
|
15280
|
-
const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
|
|
15281
|
-
return {
|
|
15282
|
-
responseText,
|
|
15283
|
-
entries,
|
|
15284
|
-
lists,
|
|
15285
|
-
changedEntries,
|
|
15286
|
-
changedLists: jobLists,
|
|
15287
|
-
hasExplicitArtifacts
|
|
15288
|
-
};
|
|
15289
|
-
}
|
|
15290
|
-
|
|
15291
15790
|
// src/agent-evals.ts
|
|
15292
15791
|
var DEFAULT_CONTROLLER_BUDGETS = {
|
|
15293
15792
|
maxIterations: 6,
|
|
15294
15793
|
maxNoProgressIterations: 2
|
|
15295
15794
|
};
|
|
15296
|
-
function
|
|
15795
|
+
function asRecord5(value) {
|
|
15297
15796
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15298
15797
|
return value;
|
|
15299
15798
|
}
|
|
@@ -15341,7 +15840,7 @@ function buildArtifactDir(baseDir, suiteName = "granular-agent-evals") {
|
|
|
15341
15840
|
function createTimestampedArtifactDirectory(options) {
|
|
15342
15841
|
return buildArtifactDir(options?.baseDir, options?.suiteName);
|
|
15343
15842
|
}
|
|
15344
|
-
function
|
|
15843
|
+
function asArray3(value) {
|
|
15345
15844
|
if (!value) return [];
|
|
15346
15845
|
return Array.isArray(value) ? value : [value];
|
|
15347
15846
|
}
|
|
@@ -15359,7 +15858,7 @@ function buildScenarioSteps(scenario) {
|
|
|
15359
15858
|
request: scenario.request,
|
|
15360
15859
|
human: scenario.human,
|
|
15361
15860
|
expect: scenario.expect,
|
|
15362
|
-
inspect: [...
|
|
15861
|
+
inspect: [...asArray3(scenario.inspect), ...asArray3(scenario.verify)],
|
|
15363
15862
|
check: scenario.check,
|
|
15364
15863
|
maxIterations: scenario.maxIterations,
|
|
15365
15864
|
setup: {
|
|
@@ -15400,12 +15899,12 @@ function buildHistory(entries) {
|
|
|
15400
15899
|
);
|
|
15401
15900
|
}
|
|
15402
15901
|
function getOpenPromptsFromDoc(liveDoc) {
|
|
15403
|
-
const jobsById =
|
|
15902
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15404
15903
|
const prompts = [];
|
|
15405
15904
|
for (const job of Object.values(jobsById)) {
|
|
15406
|
-
const promptRecords =
|
|
15905
|
+
const promptRecords = asRecord5(asRecord5(job)?.prompts) || {};
|
|
15407
15906
|
for (const raw of Object.values(promptRecords)) {
|
|
15408
|
-
const record =
|
|
15907
|
+
const record = asRecord5(raw);
|
|
15409
15908
|
if (!record || record.status !== "open" || typeof record.promptId !== "string")
|
|
15410
15909
|
continue;
|
|
15411
15910
|
const prompt = normalizePrompt({
|
|
@@ -15426,11 +15925,11 @@ function getOpenPromptsFromDoc(liveDoc) {
|
|
|
15426
15925
|
return prompts;
|
|
15427
15926
|
}
|
|
15428
15927
|
function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
|
|
15429
|
-
const jobsById =
|
|
15928
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15430
15929
|
return prompts.filter((prompt) => {
|
|
15431
15930
|
for (const jobRecord of Object.values(jobsById)) {
|
|
15432
|
-
const promptsById =
|
|
15433
|
-
const promptRecord =
|
|
15931
|
+
const promptsById = asRecord5(asRecord5(jobRecord)?.prompts) || {};
|
|
15932
|
+
const promptRecord = asRecord5(promptsById[prompt.id]);
|
|
15434
15933
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
15435
15934
|
if (openedAt >= boundaryTimestamp) return true;
|
|
15436
15935
|
}
|
|
@@ -15523,10 +16022,10 @@ ${modelOutputInstruction()}`
|
|
|
15523
16022
|
);
|
|
15524
16023
|
}
|
|
15525
16024
|
const raw = await response.json();
|
|
15526
|
-
const content =
|
|
15527
|
-
|
|
16025
|
+
const content = asRecord5(
|
|
16026
|
+
asRecord5(raw.choices?.[0])?.message
|
|
15528
16027
|
)?.content;
|
|
15529
|
-
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) =>
|
|
16028
|
+
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => asRecord5(part)?.text || "").join("") : "";
|
|
15530
16029
|
const parsed = extractJsonObject(text);
|
|
15531
16030
|
if (!parsed) {
|
|
15532
16031
|
if (attempt < 3) {
|
|
@@ -15578,17 +16077,17 @@ async function withTimeout(promise, ms, label) {
|
|
|
15578
16077
|
}
|
|
15579
16078
|
}
|
|
15580
16079
|
function getActionSummary(liveDoc, jobId) {
|
|
15581
|
-
const jobsById =
|
|
15582
|
-
const job =
|
|
16080
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
16081
|
+
const job = asRecord5(jobsById[jobId]);
|
|
15583
16082
|
return Array.isArray(job?.actionSummary) ? job.actionSummary.filter(
|
|
15584
16083
|
(line) => typeof line === "string"
|
|
15585
16084
|
) : [];
|
|
15586
16085
|
}
|
|
15587
16086
|
function normalizeHeapSnapshot2(heap) {
|
|
15588
16087
|
return {
|
|
15589
|
-
entriesByPath:
|
|
15590
|
-
listsByName:
|
|
15591
|
-
variablesByName:
|
|
16088
|
+
entriesByPath: asRecord5(heap?.entriesByPath) || {},
|
|
16089
|
+
listsByName: asRecord5(heap?.listsByName) || {},
|
|
16090
|
+
variablesByName: asRecord5(heap?.variablesByName) || {},
|
|
15592
16091
|
updatedAt: typeof heap?.updatedAt === "number" ? heap.updatedAt : Date.now()
|
|
15593
16092
|
};
|
|
15594
16093
|
}
|
|
@@ -15774,8 +16273,8 @@ async function runAgentEvalSuite(options) {
|
|
|
15774
16273
|
);
|
|
15775
16274
|
}
|
|
15776
16275
|
const inspectionResults = [];
|
|
15777
|
-
const stepChecks =
|
|
15778
|
-
const stepInspections =
|
|
16276
|
+
const stepChecks = asArray3(step.check);
|
|
16277
|
+
const stepInspections = asArray3(step.inspect);
|
|
15779
16278
|
const context = {
|
|
15780
16279
|
conversation,
|
|
15781
16280
|
environment: conversation.environment,
|
|
@@ -15788,7 +16287,7 @@ async function runAgentEvalSuite(options) {
|
|
|
15788
16287
|
promptInteractions: completed.promptInteractions,
|
|
15789
16288
|
result: completed.result,
|
|
15790
16289
|
heap: normalizeHeapSnapshot2(
|
|
15791
|
-
|
|
16290
|
+
asRecord5(
|
|
15792
16291
|
cloneJson(conversation.environment.document)?.heap
|
|
15793
16292
|
)
|
|
15794
16293
|
),
|
|
@@ -15999,7 +16498,7 @@ function createAgentEvalHarness(options) {
|
|
|
15999
16498
|
}
|
|
16000
16499
|
function buildCheckContext(conversation, completed, turnDir) {
|
|
16001
16500
|
const liveDoc = cloneJson(conversation.environment.document);
|
|
16002
|
-
const heap = normalizeHeapSnapshot2(
|
|
16501
|
+
const heap = normalizeHeapSnapshot2(asRecord5(liveDoc?.heap));
|
|
16003
16502
|
return {
|
|
16004
16503
|
conversation,
|
|
16005
16504
|
environment: conversation.environment,
|
|
@@ -16075,7 +16574,7 @@ function createAgentEvalHarness(options) {
|
|
|
16075
16574
|
jobId: pending.job.id,
|
|
16076
16575
|
result: resumed.result,
|
|
16077
16576
|
stdout: [...pending.stdout, ...resumed.stdout],
|
|
16078
|
-
sessionHeap: normalizeHeapSnapshot2(
|
|
16577
|
+
sessionHeap: normalizeHeapSnapshot2(asRecord5(liveDoc?.heap))
|
|
16079
16578
|
});
|
|
16080
16579
|
const responseText = presentation.responseText || pending.finalReply || "Done.";
|
|
16081
16580
|
pending.conversation.history.push({
|
|
@@ -16285,7 +16784,7 @@ function createAgentEvalHarness(options) {
|
|
|
16285
16784
|
const settledLiveDoc = cloneJson(
|
|
16286
16785
|
conversation.environment.document
|
|
16287
16786
|
);
|
|
16288
|
-
const sessionHeap = normalizeHeapSnapshot2(
|
|
16787
|
+
const sessionHeap = normalizeHeapSnapshot2(asRecord5(settledLiveDoc?.heap));
|
|
16289
16788
|
const presentation = resolveJobPresentation({
|
|
16290
16789
|
jobId: job.id,
|
|
16291
16790
|
result: outcome.result,
|