@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.js
CHANGED
|
@@ -4010,7 +4010,11 @@ var WSClient = class {
|
|
|
4010
4010
|
return null;
|
|
4011
4011
|
}
|
|
4012
4012
|
try {
|
|
4013
|
-
const
|
|
4013
|
+
const payloadSegment = parts[1];
|
|
4014
|
+
if (!payloadSegment) {
|
|
4015
|
+
return null;
|
|
4016
|
+
}
|
|
4017
|
+
const payloadRaw = this.decodeBase64Url(payloadSegment);
|
|
4014
4018
|
const payload = JSON.parse(payloadRaw);
|
|
4015
4019
|
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) {
|
|
4016
4020
|
return null;
|
|
@@ -5087,6 +5091,7 @@ import { ${allImports} } from "./sandbox-tools";
|
|
|
5087
5091
|
this.eventListeners.set(event, []);
|
|
5088
5092
|
}
|
|
5089
5093
|
this.eventListeners.get(event).push(handler);
|
|
5094
|
+
return () => this.off(event, handler);
|
|
5090
5095
|
}
|
|
5091
5096
|
/**
|
|
5092
5097
|
* Unsubscribe from session events
|
|
@@ -5543,6 +5548,16 @@ var JobImplementation = class {
|
|
|
5543
5548
|
handler(message);
|
|
5544
5549
|
}
|
|
5545
5550
|
}
|
|
5551
|
+
return () => {
|
|
5552
|
+
const handlers = this.eventListeners.get(event);
|
|
5553
|
+
if (!handlers) {
|
|
5554
|
+
return;
|
|
5555
|
+
}
|
|
5556
|
+
this.eventListeners.set(
|
|
5557
|
+
event,
|
|
5558
|
+
handlers.filter((current) => current !== handler)
|
|
5559
|
+
);
|
|
5560
|
+
};
|
|
5546
5561
|
}
|
|
5547
5562
|
replayAgentMessage(message) {
|
|
5548
5563
|
this.captureAgentMessage(message);
|
|
@@ -5629,6 +5644,540 @@ var JobImplementation = class {
|
|
|
5629
5644
|
}
|
|
5630
5645
|
};
|
|
5631
5646
|
|
|
5647
|
+
// src/job-presentation.ts
|
|
5648
|
+
var RESPONSE_KEYS = [
|
|
5649
|
+
"reply",
|
|
5650
|
+
"response",
|
|
5651
|
+
"text",
|
|
5652
|
+
"message",
|
|
5653
|
+
"summary",
|
|
5654
|
+
"answer"
|
|
5655
|
+
];
|
|
5656
|
+
var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
|
|
5657
|
+
var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
|
|
5658
|
+
var LIST_KEY_CANDIDATES = ["listName"];
|
|
5659
|
+
var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
|
|
5660
|
+
var VARIABLE_KEY_CANDIDATES = ["variableName"];
|
|
5661
|
+
var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
|
|
5662
|
+
var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
|
|
5663
|
+
function asRecord2(value) {
|
|
5664
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
5665
|
+
return value;
|
|
5666
|
+
}
|
|
5667
|
+
function normalizeText(value) {
|
|
5668
|
+
if (typeof value !== "string") return null;
|
|
5669
|
+
const trimmed = value.trim();
|
|
5670
|
+
if (!trimmed) return null;
|
|
5671
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
5672
|
+
return null;
|
|
5673
|
+
}
|
|
5674
|
+
return trimmed;
|
|
5675
|
+
}
|
|
5676
|
+
function humanTextFromStdout(stdout) {
|
|
5677
|
+
for (const line of [...stdout].reverse()) {
|
|
5678
|
+
const normalized = normalizeText(line);
|
|
5679
|
+
if (!normalized) continue;
|
|
5680
|
+
if (/^[A-Z_]+:/.test(normalized)) continue;
|
|
5681
|
+
return normalized;
|
|
5682
|
+
}
|
|
5683
|
+
return null;
|
|
5684
|
+
}
|
|
5685
|
+
function pushString(target, value) {
|
|
5686
|
+
if (typeof value === "string" && value.trim()) {
|
|
5687
|
+
target.add(value.trim());
|
|
5688
|
+
}
|
|
5689
|
+
}
|
|
5690
|
+
function pushStringArray(target, value) {
|
|
5691
|
+
if (!Array.isArray(value)) return;
|
|
5692
|
+
for (const item of value) {
|
|
5693
|
+
pushString(target, item);
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
function collectReferencesFromRecord(record, refs) {
|
|
5697
|
+
for (const key of ENTRY_KEY_CANDIDATES)
|
|
5698
|
+
pushString(refs.entryPaths, record[key]);
|
|
5699
|
+
for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
|
|
5700
|
+
pushStringArray(refs.entryPaths, record[key]);
|
|
5701
|
+
for (const key of LIST_KEY_CANDIDATES)
|
|
5702
|
+
pushString(refs.listNames, record[key]);
|
|
5703
|
+
for (const key of LIST_ARRAY_KEY_CANDIDATES)
|
|
5704
|
+
pushStringArray(refs.listNames, record[key]);
|
|
5705
|
+
for (const key of VARIABLE_KEY_CANDIDATES)
|
|
5706
|
+
pushString(refs.variableNames, record[key]);
|
|
5707
|
+
for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
|
|
5708
|
+
pushStringArray(refs.variableNames, record[key]);
|
|
5709
|
+
}
|
|
5710
|
+
function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
5711
|
+
if (value === null || value === void 0 || depth > 4 || seen.has(value))
|
|
5712
|
+
return;
|
|
5713
|
+
if (typeof value === "string") {
|
|
5714
|
+
const trimmed = value.trim();
|
|
5715
|
+
if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
|
|
5716
|
+
if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
|
|
5717
|
+
if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
|
|
5718
|
+
return;
|
|
5719
|
+
}
|
|
5720
|
+
if (Array.isArray(value)) {
|
|
5721
|
+
seen.add(value);
|
|
5722
|
+
for (const item of value.slice(0, 24)) {
|
|
5723
|
+
scanForHeapReferences(item, heap, refs, depth + 1, seen);
|
|
5724
|
+
}
|
|
5725
|
+
return;
|
|
5726
|
+
}
|
|
5727
|
+
const record = asRecord2(value);
|
|
5728
|
+
if (!record) return;
|
|
5729
|
+
seen.add(value);
|
|
5730
|
+
collectReferencesFromRecord(record, refs);
|
|
5731
|
+
for (const key of UI_CONTAINER_KEYS) {
|
|
5732
|
+
const nested = asRecord2(record[key]);
|
|
5733
|
+
if (nested) collectReferencesFromRecord(nested, refs);
|
|
5734
|
+
}
|
|
5735
|
+
for (const nested of Object.values(record).slice(0, 24)) {
|
|
5736
|
+
scanForHeapReferences(nested, heap, refs, depth + 1, seen);
|
|
5737
|
+
}
|
|
5738
|
+
}
|
|
5739
|
+
function resolveVariablesToReferences(variableNames, heap, refs) {
|
|
5740
|
+
for (const variableName of variableNames) {
|
|
5741
|
+
const variable = heap.variablesByName?.[variableName];
|
|
5742
|
+
if (!variable) continue;
|
|
5743
|
+
if (variable.kind === "entry" && variable.entryPath) {
|
|
5744
|
+
refs.entryPaths.add(variable.entryPath);
|
|
5745
|
+
}
|
|
5746
|
+
if (variable.kind === "list" && variable.listName) {
|
|
5747
|
+
refs.listNames.add(variable.listName);
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
function sortEntries(entries) {
|
|
5752
|
+
return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
5753
|
+
}
|
|
5754
|
+
function sortLists(lists) {
|
|
5755
|
+
return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
5756
|
+
}
|
|
5757
|
+
function dedupeEntries(entries) {
|
|
5758
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5759
|
+
const result = [];
|
|
5760
|
+
for (const entry of entries) {
|
|
5761
|
+
if (!entry?.path || seen.has(entry.path)) continue;
|
|
5762
|
+
seen.add(entry.path);
|
|
5763
|
+
result.push(entry);
|
|
5764
|
+
}
|
|
5765
|
+
return result;
|
|
5766
|
+
}
|
|
5767
|
+
function dedupeLists(lists) {
|
|
5768
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5769
|
+
const result = [];
|
|
5770
|
+
for (const list of lists) {
|
|
5771
|
+
if (!list?.name || seen.has(list.name)) continue;
|
|
5772
|
+
seen.add(list.name);
|
|
5773
|
+
result.push(list);
|
|
5774
|
+
}
|
|
5775
|
+
return result;
|
|
5776
|
+
}
|
|
5777
|
+
function extractResponseText(result, stdout) {
|
|
5778
|
+
const directText = normalizeText(result);
|
|
5779
|
+
if (directText) return directText;
|
|
5780
|
+
const record = asRecord2(result);
|
|
5781
|
+
if (record) {
|
|
5782
|
+
for (const key of RESPONSE_KEYS) {
|
|
5783
|
+
const normalized = normalizeText(record[key]);
|
|
5784
|
+
if (normalized) return normalized;
|
|
5785
|
+
}
|
|
5786
|
+
for (const containerKey of UI_CONTAINER_KEYS) {
|
|
5787
|
+
const nested = asRecord2(record[containerKey]);
|
|
5788
|
+
if (!nested) continue;
|
|
5789
|
+
for (const key of RESPONSE_KEYS) {
|
|
5790
|
+
const normalized = normalizeText(nested[key]);
|
|
5791
|
+
if (normalized) return normalized;
|
|
5792
|
+
}
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
return humanTextFromStdout(stdout);
|
|
5796
|
+
}
|
|
5797
|
+
function fallbackResponseText(entries, lists) {
|
|
5798
|
+
if (entries.length > 0) {
|
|
5799
|
+
return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
|
|
5800
|
+
}
|
|
5801
|
+
if (lists.length > 0) {
|
|
5802
|
+
const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
|
|
5803
|
+
if (emptyOnly) {
|
|
5804
|
+
return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
|
|
5805
|
+
}
|
|
5806
|
+
return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
|
|
5807
|
+
}
|
|
5808
|
+
return null;
|
|
5809
|
+
}
|
|
5810
|
+
function getJobRelatedEntries(heap, jobId) {
|
|
5811
|
+
return sortEntries(
|
|
5812
|
+
Object.values(heap.entriesByPath || {}).filter(
|
|
5813
|
+
(entry) => entry.relatedJobIds?.includes(jobId)
|
|
5814
|
+
)
|
|
5815
|
+
);
|
|
5816
|
+
}
|
|
5817
|
+
function getJobRelatedLists(heap, jobId) {
|
|
5818
|
+
return sortLists(
|
|
5819
|
+
Object.values(heap.listsByName || {}).filter(
|
|
5820
|
+
(list) => list.relatedJobIds?.includes(jobId)
|
|
5821
|
+
)
|
|
5822
|
+
);
|
|
5823
|
+
}
|
|
5824
|
+
function entriesFromLists(lists, heap) {
|
|
5825
|
+
const entries = [];
|
|
5826
|
+
for (const list of lists) {
|
|
5827
|
+
for (const path2 of list.paths || []) {
|
|
5828
|
+
const entry = heap.entriesByPath?.[path2];
|
|
5829
|
+
if (entry) entries.push(entry);
|
|
5830
|
+
}
|
|
5831
|
+
}
|
|
5832
|
+
return entries;
|
|
5833
|
+
}
|
|
5834
|
+
function resolveJobPresentation({
|
|
5835
|
+
jobId,
|
|
5836
|
+
result,
|
|
5837
|
+
stdout = [],
|
|
5838
|
+
sessionHeap,
|
|
5839
|
+
allowExplicitArtifacts = true
|
|
5840
|
+
}) {
|
|
5841
|
+
const refs = {
|
|
5842
|
+
entryPaths: /* @__PURE__ */ new Set(),
|
|
5843
|
+
listNames: /* @__PURE__ */ new Set(),
|
|
5844
|
+
variableNames: /* @__PURE__ */ new Set()
|
|
5845
|
+
};
|
|
5846
|
+
if (allowExplicitArtifacts) {
|
|
5847
|
+
scanForHeapReferences(result, sessionHeap, refs);
|
|
5848
|
+
resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
|
|
5849
|
+
}
|
|
5850
|
+
const referencedLists = sortLists(
|
|
5851
|
+
[...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
|
|
5852
|
+
);
|
|
5853
|
+
const referencedEntries = sortEntries(
|
|
5854
|
+
[...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
|
|
5855
|
+
);
|
|
5856
|
+
const jobLists = getJobRelatedLists(sessionHeap, jobId);
|
|
5857
|
+
const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
|
|
5858
|
+
const changedEntries = dedupeEntries([
|
|
5859
|
+
...jobEntries,
|
|
5860
|
+
...entriesFromLists(jobLists, sessionHeap)
|
|
5861
|
+
]);
|
|
5862
|
+
const explicitLists = dedupeLists(referencedLists);
|
|
5863
|
+
const explicitEntries = dedupeEntries([
|
|
5864
|
+
...referencedEntries,
|
|
5865
|
+
...entriesFromLists(referencedLists, sessionHeap)
|
|
5866
|
+
]);
|
|
5867
|
+
const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
|
|
5868
|
+
const lists = hasExplicitArtifacts ? explicitLists : jobLists;
|
|
5869
|
+
const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
|
|
5870
|
+
const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
|
|
5871
|
+
return {
|
|
5872
|
+
responseText,
|
|
5873
|
+
entries,
|
|
5874
|
+
lists,
|
|
5875
|
+
changedEntries,
|
|
5876
|
+
changedLists: jobLists,
|
|
5877
|
+
hasExplicitArtifacts
|
|
5878
|
+
};
|
|
5879
|
+
}
|
|
5880
|
+
|
|
5881
|
+
// src/session-transcript.ts
|
|
5882
|
+
var EMPTY_HEAP = {
|
|
5883
|
+
entriesByPath: {},
|
|
5884
|
+
listsByName: {},
|
|
5885
|
+
variablesByName: {}};
|
|
5886
|
+
function asRecord3(value) {
|
|
5887
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
5888
|
+
return value;
|
|
5889
|
+
}
|
|
5890
|
+
function asArray(value) {
|
|
5891
|
+
return Array.isArray(value) ? value : [];
|
|
5892
|
+
}
|
|
5893
|
+
function asNumber(value) {
|
|
5894
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
5895
|
+
}
|
|
5896
|
+
function asString(value) {
|
|
5897
|
+
return typeof value === "string" ? value : void 0;
|
|
5898
|
+
}
|
|
5899
|
+
function trimString(value) {
|
|
5900
|
+
return typeof value === "string" ? value.trim() : "";
|
|
5901
|
+
}
|
|
5902
|
+
function normalizeShowRefs(value) {
|
|
5903
|
+
const record = asRecord3(value);
|
|
5904
|
+
if (!record) return void 0;
|
|
5905
|
+
const normalizeRefs = (input) => {
|
|
5906
|
+
if (!Array.isArray(input)) return void 0;
|
|
5907
|
+
const refs = Array.from(
|
|
5908
|
+
new Set(
|
|
5909
|
+
input.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean)
|
|
5910
|
+
)
|
|
5911
|
+
);
|
|
5912
|
+
return refs.length > 0 ? refs : void 0;
|
|
5913
|
+
};
|
|
5914
|
+
const show = {
|
|
5915
|
+
entryPaths: normalizeRefs(record.entryPaths),
|
|
5916
|
+
listNames: normalizeRefs(record.listNames),
|
|
5917
|
+
variableNames: normalizeRefs(record.variableNames)
|
|
5918
|
+
};
|
|
5919
|
+
return show.entryPaths || show.listNames || show.variableNames ? show : void 0;
|
|
5920
|
+
}
|
|
5921
|
+
function stringifyTranscriptValue(value, fallback = "") {
|
|
5922
|
+
if (typeof value === "string") {
|
|
5923
|
+
return value.trim() || fallback;
|
|
5924
|
+
}
|
|
5925
|
+
if (typeof value === "boolean") {
|
|
5926
|
+
return value ? "Confirmed" : "Canceled";
|
|
5927
|
+
}
|
|
5928
|
+
if (value === void 0) {
|
|
5929
|
+
return fallback;
|
|
5930
|
+
}
|
|
5931
|
+
try {
|
|
5932
|
+
const json = JSON.stringify(value, null, 2);
|
|
5933
|
+
if (!json || json === "undefined") return fallback;
|
|
5934
|
+
return json.length > 2e3 ? `${json.slice(0, 2e3)}...` : json;
|
|
5935
|
+
} catch {
|
|
5936
|
+
return String(value);
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
function buildArtifactHistory(show) {
|
|
5940
|
+
if (!show) return void 0;
|
|
5941
|
+
return `[Agent message]
|
|
5942
|
+
${stringifyTranscriptValue({ show }, "")}`;
|
|
5943
|
+
}
|
|
5944
|
+
function normalizeConversationMessage(raw) {
|
|
5945
|
+
const record = asRecord3(raw);
|
|
5946
|
+
if (!record) return null;
|
|
5947
|
+
const role = record.role === "user" ? "user" : record.role === "assistant" ? "assistant" : null;
|
|
5948
|
+
if (!role) return null;
|
|
5949
|
+
const content = trimString(
|
|
5950
|
+
record.content ?? record.reply ?? record.message ?? record.text
|
|
5951
|
+
);
|
|
5952
|
+
const show = normalizeShowRefs(record.show);
|
|
5953
|
+
const id = asString(record.id) || crypto.randomUUID();
|
|
5954
|
+
const timestamp = asNumber(record.timestamp) || asNumber(record.ts) || 0;
|
|
5955
|
+
if (!content && !show) return null;
|
|
5956
|
+
return {
|
|
5957
|
+
id,
|
|
5958
|
+
role,
|
|
5959
|
+
content,
|
|
5960
|
+
timestamp,
|
|
5961
|
+
jobId: asString(record.jobId),
|
|
5962
|
+
promptId: asString(record.promptId),
|
|
5963
|
+
show,
|
|
5964
|
+
historyContent: role === "assistant" ? content ? `[Assistant reply]
|
|
5965
|
+
${content}` : buildArtifactHistory(show) : void 0,
|
|
5966
|
+
source: "conversation"
|
|
5967
|
+
};
|
|
5968
|
+
}
|
|
5969
|
+
function normalizePromptEntries(jobId, rawPrompts, conversationPromptIds) {
|
|
5970
|
+
const promptsById = asRecord3(rawPrompts) || {};
|
|
5971
|
+
return Object.values(promptsById).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
5972
|
+
(left, right) => (asNumber(left.openedAt) || asNumber(left.answeredAt) || 0) - (asNumber(right.openedAt) || asNumber(right.answeredAt) || 0)
|
|
5973
|
+
).flatMap((prompt) => {
|
|
5974
|
+
const promptId = asString(prompt.promptId);
|
|
5975
|
+
if (!promptId || conversationPromptIds.has(promptId)) return [];
|
|
5976
|
+
const title = trimString(prompt.title);
|
|
5977
|
+
const message = trimString(prompt.message);
|
|
5978
|
+
const assistantContent = message || title || "Input required";
|
|
5979
|
+
const openedAt = asNumber(prompt.openedAt) || 0;
|
|
5980
|
+
const answeredAt = asNumber(prompt.answeredAt) || openedAt;
|
|
5981
|
+
const entries = [
|
|
5982
|
+
{
|
|
5983
|
+
id: `prompt:${promptId}:assistant`,
|
|
5984
|
+
role: "assistant",
|
|
5985
|
+
content: assistantContent,
|
|
5986
|
+
timestamp: openedAt,
|
|
5987
|
+
jobId,
|
|
5988
|
+
promptId,
|
|
5989
|
+
historyContent: `[Assistant reply]
|
|
5990
|
+
${assistantContent}`,
|
|
5991
|
+
source: "job_prompt"
|
|
5992
|
+
}
|
|
5993
|
+
];
|
|
5994
|
+
if (Object.prototype.hasOwnProperty.call(prompt, "answer")) {
|
|
5995
|
+
entries.push({
|
|
5996
|
+
id: `prompt:${promptId}:user`,
|
|
5997
|
+
role: "user",
|
|
5998
|
+
content: stringifyTranscriptValue(prompt.answer, ""),
|
|
5999
|
+
timestamp: answeredAt,
|
|
6000
|
+
jobId,
|
|
6001
|
+
promptId,
|
|
6002
|
+
source: "job_prompt"
|
|
6003
|
+
});
|
|
6004
|
+
}
|
|
6005
|
+
return entries;
|
|
6006
|
+
});
|
|
6007
|
+
}
|
|
6008
|
+
function normalizeAgentMessageEntries(jobId, rawMessages) {
|
|
6009
|
+
return asArray(rawMessages).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
6010
|
+
(left, right) => (asNumber(left.timestamp) || asNumber(left.ts) || 0) - (asNumber(right.timestamp) || asNumber(right.ts) || 0)
|
|
6011
|
+
).flatMap((message) => {
|
|
6012
|
+
const messageId = asString(message.messageId) || asString(message.id) || crypto.randomUUID();
|
|
6013
|
+
const timestamp = asNumber(message.timestamp) || asNumber(message.ts) || 0;
|
|
6014
|
+
const reply = trimString(
|
|
6015
|
+
message.reply ?? message.message ?? message.text ?? message.content
|
|
6016
|
+
);
|
|
6017
|
+
const show = normalizeShowRefs(message.show);
|
|
6018
|
+
const entries = [];
|
|
6019
|
+
if (reply) {
|
|
6020
|
+
entries.push({
|
|
6021
|
+
id: `agent:${messageId}:text`,
|
|
6022
|
+
role: "assistant",
|
|
6023
|
+
content: reply,
|
|
6024
|
+
timestamp,
|
|
6025
|
+
jobId,
|
|
6026
|
+
historyContent: `[Assistant reply]
|
|
6027
|
+
${reply}`,
|
|
6028
|
+
source: "job_agent_message"
|
|
6029
|
+
});
|
|
6030
|
+
}
|
|
6031
|
+
if (show) {
|
|
6032
|
+
entries.push({
|
|
6033
|
+
id: `agent:${messageId}:artifacts`,
|
|
6034
|
+
role: "assistant",
|
|
6035
|
+
content: "",
|
|
6036
|
+
timestamp,
|
|
6037
|
+
jobId,
|
|
6038
|
+
show,
|
|
6039
|
+
historyContent: buildArtifactHistory(show),
|
|
6040
|
+
source: "job_agent_message"
|
|
6041
|
+
});
|
|
6042
|
+
}
|
|
6043
|
+
return entries;
|
|
6044
|
+
});
|
|
6045
|
+
}
|
|
6046
|
+
function buildJobFallbackEntries(jobId, job, sessionHeap) {
|
|
6047
|
+
const timestamp = asNumber(job.finishedAt) || asNumber(job.startedAt) || asNumber(job.submittedAt) || 0;
|
|
6048
|
+
const resultPreview = stringifyTranscriptValue(
|
|
6049
|
+
job.result,
|
|
6050
|
+
"No job result recorded."
|
|
6051
|
+
);
|
|
6052
|
+
const presentation = resolveJobPresentation({
|
|
6053
|
+
jobId,
|
|
6054
|
+
result: job.result,
|
|
6055
|
+
stdout: [],
|
|
6056
|
+
sessionHeap
|
|
6057
|
+
});
|
|
6058
|
+
const entries = [];
|
|
6059
|
+
const responseText = presentation.responseText || "";
|
|
6060
|
+
if (responseText) {
|
|
6061
|
+
entries.push({
|
|
6062
|
+
id: `job:${jobId}:result-text`,
|
|
6063
|
+
role: "assistant",
|
|
6064
|
+
content: responseText,
|
|
6065
|
+
timestamp,
|
|
6066
|
+
jobId,
|
|
6067
|
+
historyContent: `[Assistant reply]
|
|
6068
|
+
${responseText}`,
|
|
6069
|
+
source: "job_result"
|
|
6070
|
+
});
|
|
6071
|
+
}
|
|
6072
|
+
const show = {
|
|
6073
|
+
entryPaths: presentation.entries.map((entry) => entry.path),
|
|
6074
|
+
listNames: presentation.lists.map((list) => list.name)
|
|
6075
|
+
};
|
|
6076
|
+
if (show.entryPaths && show.entryPaths.length > 0 || show.listNames && show.listNames.length > 0) {
|
|
6077
|
+
entries.push({
|
|
6078
|
+
id: `job:${jobId}:result-artifacts`,
|
|
6079
|
+
role: "assistant",
|
|
6080
|
+
content: "",
|
|
6081
|
+
timestamp,
|
|
6082
|
+
jobId,
|
|
6083
|
+
show,
|
|
6084
|
+
historyContent: buildArtifactHistory(show),
|
|
6085
|
+
source: "job_result"
|
|
6086
|
+
});
|
|
6087
|
+
}
|
|
6088
|
+
if (entries.length === 0 && trimString(job.error)) {
|
|
6089
|
+
entries.push({
|
|
6090
|
+
id: `job:${jobId}:result-error`,
|
|
6091
|
+
role: "assistant",
|
|
6092
|
+
content: trimString(job.error),
|
|
6093
|
+
timestamp,
|
|
6094
|
+
jobId,
|
|
6095
|
+
historyContent: `[Assistant reply]
|
|
6096
|
+
${trimString(job.error)}`,
|
|
6097
|
+
source: "job_result"
|
|
6098
|
+
});
|
|
6099
|
+
}
|
|
6100
|
+
if (entries.length === 0 && resultPreview && resultPreview !== "No job result recorded.") {
|
|
6101
|
+
entries.push({
|
|
6102
|
+
id: `job:${jobId}:result-preview`,
|
|
6103
|
+
role: "assistant",
|
|
6104
|
+
content: resultPreview,
|
|
6105
|
+
timestamp,
|
|
6106
|
+
jobId,
|
|
6107
|
+
historyContent: `[Assistant reply]
|
|
6108
|
+
${resultPreview}`,
|
|
6109
|
+
source: "job_result"
|
|
6110
|
+
});
|
|
6111
|
+
}
|
|
6112
|
+
return entries;
|
|
6113
|
+
}
|
|
6114
|
+
function buildJobCodeEntry(jobId, job) {
|
|
6115
|
+
const code = trimString(job.source);
|
|
6116
|
+
if (!code) return null;
|
|
6117
|
+
const jobStatus = asString(job.status);
|
|
6118
|
+
const error = jobStatus === "failed" || jobStatus === "canceled" || jobStatus === "timeout" ? trimString(job.error) || `Job ${jobStatus}` : void 0;
|
|
6119
|
+
return {
|
|
6120
|
+
id: `job:${jobId}:code`,
|
|
6121
|
+
role: "assistant",
|
|
6122
|
+
content: "",
|
|
6123
|
+
timestamp: asNumber(job.submittedAt) || asNumber(job.startedAt) || asNumber(job.finishedAt) || 0,
|
|
6124
|
+
jobId,
|
|
6125
|
+
code,
|
|
6126
|
+
jobStatus,
|
|
6127
|
+
jobResultPreview: stringifyTranscriptValue(job.result, "No job result recorded."),
|
|
6128
|
+
error,
|
|
6129
|
+
source: "job_code"
|
|
6130
|
+
};
|
|
6131
|
+
}
|
|
6132
|
+
function buildSessionTranscript(input) {
|
|
6133
|
+
const liveDoc = input.liveDoc || null;
|
|
6134
|
+
const sessionHeap = input.sessionHeap || EMPTY_HEAP;
|
|
6135
|
+
const transcript = [];
|
|
6136
|
+
const conversationMessages = asArray(asRecord3(liveDoc?.conversation)?.messages).map((message) => normalizeConversationMessage(message)).filter((message) => Boolean(message));
|
|
6137
|
+
const conversationPromptIds = new Set(
|
|
6138
|
+
conversationMessages.map((message) => message.promptId).filter((promptId) => Boolean(promptId))
|
|
6139
|
+
);
|
|
6140
|
+
const assistantConversationJobIds = new Set(
|
|
6141
|
+
conversationMessages.filter((message) => message.role === "assistant" && Boolean(message.jobId)).map((message) => message.jobId)
|
|
6142
|
+
);
|
|
6143
|
+
transcript.push(...conversationMessages);
|
|
6144
|
+
const jobsById = asRecord3(asRecord3(liveDoc?.jobs)?.byId) || {};
|
|
6145
|
+
const jobs = Object.values(jobsById).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
6146
|
+
(left, right) => (asNumber(left.submittedAt) || asNumber(left.startedAt) || asNumber(left.finishedAt) || 0) - (asNumber(right.submittedAt) || asNumber(right.startedAt) || asNumber(right.finishedAt) || 0)
|
|
6147
|
+
);
|
|
6148
|
+
for (const job of jobs) {
|
|
6149
|
+
const jobId = asString(job.jobId);
|
|
6150
|
+
if (!jobId) continue;
|
|
6151
|
+
const codeEntry = buildJobCodeEntry(jobId, job);
|
|
6152
|
+
if (codeEntry) {
|
|
6153
|
+
transcript.push(codeEntry);
|
|
6154
|
+
}
|
|
6155
|
+
transcript.push(
|
|
6156
|
+
...normalizePromptEntries(jobId, job.prompts, conversationPromptIds)
|
|
6157
|
+
);
|
|
6158
|
+
if (!assistantConversationJobIds.has(jobId)) {
|
|
6159
|
+
const agentEntries = normalizeAgentMessageEntries(jobId, job.agentMessages);
|
|
6160
|
+
if (agentEntries.length > 0) {
|
|
6161
|
+
transcript.push(...agentEntries);
|
|
6162
|
+
} else {
|
|
6163
|
+
transcript.push(
|
|
6164
|
+
...buildJobFallbackEntries(
|
|
6165
|
+
jobId,
|
|
6166
|
+
job,
|
|
6167
|
+
sessionHeap
|
|
6168
|
+
)
|
|
6169
|
+
);
|
|
6170
|
+
}
|
|
6171
|
+
}
|
|
6172
|
+
}
|
|
6173
|
+
return transcript.sort((left, right) => {
|
|
6174
|
+
if (left.timestamp !== right.timestamp) {
|
|
6175
|
+
return left.timestamp - right.timestamp;
|
|
6176
|
+
}
|
|
6177
|
+
return left.id.localeCompare(right.id);
|
|
6178
|
+
});
|
|
6179
|
+
}
|
|
6180
|
+
|
|
5632
6181
|
// src/endpoints.ts
|
|
5633
6182
|
var LOCAL_API_URL = "ws://localhost:8787/granular";
|
|
5634
6183
|
var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
|
|
@@ -12089,7 +12638,9 @@ var Environment = class {
|
|
|
12089
12638
|
}
|
|
12090
12639
|
);
|
|
12091
12640
|
if (result.errors?.length) {
|
|
12092
|
-
throw new Error(
|
|
12641
|
+
throw new Error(
|
|
12642
|
+
`defineRelationship failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12643
|
+
);
|
|
12093
12644
|
}
|
|
12094
12645
|
return result.data.at.define_relationship;
|
|
12095
12646
|
}
|
|
@@ -12125,7 +12676,9 @@ var Environment = class {
|
|
|
12125
12676
|
{ path: modelPath }
|
|
12126
12677
|
);
|
|
12127
12678
|
if (result.errors?.length) {
|
|
12128
|
-
throw new Error(
|
|
12679
|
+
throw new Error(
|
|
12680
|
+
`getRelationships failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12681
|
+
);
|
|
12129
12682
|
}
|
|
12130
12683
|
return result.data?.model?.relationships || [];
|
|
12131
12684
|
}
|
|
@@ -12164,7 +12717,9 @@ var Environment = class {
|
|
|
12164
12717
|
{ target: targetPath }
|
|
12165
12718
|
);
|
|
12166
12719
|
if (result.errors?.length) {
|
|
12167
|
-
throw new Error(
|
|
12720
|
+
throw new Error(
|
|
12721
|
+
`attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12722
|
+
);
|
|
12168
12723
|
}
|
|
12169
12724
|
}
|
|
12170
12725
|
/**
|
|
@@ -12199,7 +12754,9 @@ var Environment = class {
|
|
|
12199
12754
|
}`
|
|
12200
12755
|
);
|
|
12201
12756
|
if (result.errors?.length) {
|
|
12202
|
-
throw new Error(
|
|
12757
|
+
throw new Error(
|
|
12758
|
+
`detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12759
|
+
);
|
|
12203
12760
|
}
|
|
12204
12761
|
}
|
|
12205
12762
|
/**
|
|
@@ -12226,7 +12783,9 @@ var Environment = class {
|
|
|
12226
12783
|
}`
|
|
12227
12784
|
);
|
|
12228
12785
|
if (result.errors?.length) {
|
|
12229
|
-
throw new Error(
|
|
12786
|
+
throw new Error(
|
|
12787
|
+
`listRelated failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12788
|
+
);
|
|
12230
12789
|
}
|
|
12231
12790
|
return result.data?.at?.at?.list_related || [];
|
|
12232
12791
|
}
|
|
@@ -12319,7 +12878,9 @@ var Environment = class {
|
|
|
12319
12878
|
async _runGraphql(query, label) {
|
|
12320
12879
|
const result = await this.graphql(query);
|
|
12321
12880
|
if (result.errors?.length) {
|
|
12322
|
-
throw new Error(
|
|
12881
|
+
throw new Error(
|
|
12882
|
+
`${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12883
|
+
);
|
|
12323
12884
|
}
|
|
12324
12885
|
return result.data;
|
|
12325
12886
|
}
|
|
@@ -12664,7 +13225,11 @@ var Environment = class {
|
|
|
12664
13225
|
*/
|
|
12665
13226
|
async recordObject(options) {
|
|
12666
13227
|
const results = await this.recordObjects([options]);
|
|
12667
|
-
|
|
13228
|
+
const result = results[0];
|
|
13229
|
+
if (!result) {
|
|
13230
|
+
throw new Error("recordObject: no result returned for record");
|
|
13231
|
+
}
|
|
13232
|
+
return result;
|
|
12668
13233
|
}
|
|
12669
13234
|
/**
|
|
12670
13235
|
* Batch version of `recordObject()`.
|
|
@@ -12716,7 +13281,13 @@ var Environment = class {
|
|
|
12716
13281
|
);
|
|
12717
13282
|
}
|
|
12718
13283
|
for (let index = 0; index < items.length; index += 1) {
|
|
12719
|
-
|
|
13284
|
+
const item = items[index];
|
|
13285
|
+
if (!item) {
|
|
13286
|
+
throw new Error(
|
|
13287
|
+
`recordObjects: chunk ${plan.chunkIndex + 1} returned an empty result at index ${index}`
|
|
13288
|
+
);
|
|
13289
|
+
}
|
|
13290
|
+
results[plan.offset + index] = item;
|
|
12720
13291
|
}
|
|
12721
13292
|
if (onChunk) {
|
|
12722
13293
|
const info = {
|
|
@@ -12844,6 +13415,9 @@ var EnvironmentSession = class extends Session {
|
|
|
12844
13415
|
get envName() {
|
|
12845
13416
|
return this.environment.envName;
|
|
12846
13417
|
}
|
|
13418
|
+
get tag() {
|
|
13419
|
+
return this.environment.tag;
|
|
13420
|
+
}
|
|
12847
13421
|
get versionId() {
|
|
12848
13422
|
return this.environment.versionId;
|
|
12849
13423
|
}
|
|
@@ -12863,12 +13437,171 @@ var EnvironmentSession = class extends Session {
|
|
|
12863
13437
|
return this.environment.feedback;
|
|
12864
13438
|
}
|
|
12865
13439
|
/**
|
|
12866
|
-
* Return a plain JS
|
|
13440
|
+
* Return a plain JS copy of the synced session heap.
|
|
12867
13441
|
*/
|
|
12868
13442
|
getHeap() {
|
|
12869
13443
|
const doc = this.document;
|
|
12870
13444
|
return normalizeHeapSnapshot(doc?.heap);
|
|
12871
13445
|
}
|
|
13446
|
+
async sessionDataRequest(path2, query) {
|
|
13447
|
+
const searchParams = new URLSearchParams();
|
|
13448
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
13449
|
+
if (value !== null && typeof value !== "undefined" && value !== "") {
|
|
13450
|
+
searchParams.set(key, String(value));
|
|
13451
|
+
}
|
|
13452
|
+
}
|
|
13453
|
+
const queryString = searchParams.toString();
|
|
13454
|
+
const response = await fetch(
|
|
13455
|
+
`${this.environment.runtimeBaseUrl}/orchestrator/ws/sessions/${encodeURIComponent(this.sessionId)}${path2}${queryString ? `?${queryString}` : ""}`,
|
|
13456
|
+
{
|
|
13457
|
+
method: "GET",
|
|
13458
|
+
headers: {
|
|
13459
|
+
Authorization: `Bearer ${this.environment.authToken}`,
|
|
13460
|
+
"Content-Type": "application/json"
|
|
13461
|
+
}
|
|
13462
|
+
}
|
|
13463
|
+
);
|
|
13464
|
+
if (!response.ok) {
|
|
13465
|
+
const errorText = await response.text();
|
|
13466
|
+
throw new Error(
|
|
13467
|
+
`Session data API Error (${response.status}): ${errorText}`
|
|
13468
|
+
);
|
|
13469
|
+
}
|
|
13470
|
+
return response.json();
|
|
13471
|
+
}
|
|
13472
|
+
async collectAllSessionItems(listPage) {
|
|
13473
|
+
const items = [];
|
|
13474
|
+
let cursor = null;
|
|
13475
|
+
do {
|
|
13476
|
+
const page = await listPage({ limit: 500, cursor });
|
|
13477
|
+
items.push(...page.items);
|
|
13478
|
+
cursor = page.nextCursor;
|
|
13479
|
+
} while (cursor);
|
|
13480
|
+
return items;
|
|
13481
|
+
}
|
|
13482
|
+
/**
|
|
13483
|
+
* Fetch the live session document from the runtime DO.
|
|
13484
|
+
*
|
|
13485
|
+
* For history and saved artifacts, prefer the collection APIs on
|
|
13486
|
+
* `messages`, `timeline`, `jobs`, and `heap`.
|
|
13487
|
+
*/
|
|
13488
|
+
async getDocument() {
|
|
13489
|
+
return this.sessionDataRequest("/document");
|
|
13490
|
+
}
|
|
13491
|
+
get messages() {
|
|
13492
|
+
return {
|
|
13493
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13494
|
+
"/messages",
|
|
13495
|
+
options
|
|
13496
|
+
)
|
|
13497
|
+
};
|
|
13498
|
+
}
|
|
13499
|
+
get timeline() {
|
|
13500
|
+
return {
|
|
13501
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13502
|
+
"/timeline",
|
|
13503
|
+
options
|
|
13504
|
+
)
|
|
13505
|
+
};
|
|
13506
|
+
}
|
|
13507
|
+
get jobs() {
|
|
13508
|
+
return {
|
|
13509
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13510
|
+
"/jobs",
|
|
13511
|
+
options
|
|
13512
|
+
),
|
|
13513
|
+
get: (jobId) => this.sessionDataRequest(
|
|
13514
|
+
`/jobs/${encodeURIComponent(jobId)}`
|
|
13515
|
+
)
|
|
13516
|
+
};
|
|
13517
|
+
}
|
|
13518
|
+
get heap() {
|
|
13519
|
+
return {
|
|
13520
|
+
entries: {
|
|
13521
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13522
|
+
"/heap/entries",
|
|
13523
|
+
options
|
|
13524
|
+
),
|
|
13525
|
+
get: (path2) => this.sessionDataRequest(
|
|
13526
|
+
`/heap/entries/${encodeURIComponent(path2)}`
|
|
13527
|
+
)
|
|
13528
|
+
},
|
|
13529
|
+
lists: {
|
|
13530
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13531
|
+
"/heap/lists",
|
|
13532
|
+
options
|
|
13533
|
+
),
|
|
13534
|
+
get: (name) => this.sessionDataRequest(
|
|
13535
|
+
`/heap/lists/${encodeURIComponent(name)}`
|
|
13536
|
+
)
|
|
13537
|
+
}
|
|
13538
|
+
};
|
|
13539
|
+
}
|
|
13540
|
+
get transcript() {
|
|
13541
|
+
return {
|
|
13542
|
+
list: async (options = {}) => {
|
|
13543
|
+
const [messages, jobs, entries, lists] = await Promise.all([
|
|
13544
|
+
this.collectAllSessionItems(this.messages.list),
|
|
13545
|
+
this.collectAllSessionItems(
|
|
13546
|
+
(pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
|
|
13547
|
+
),
|
|
13548
|
+
this.collectAllSessionItems(this.heap.entries.list),
|
|
13549
|
+
this.collectAllSessionItems(this.heap.lists.list)
|
|
13550
|
+
]);
|
|
13551
|
+
const liveDoc = {
|
|
13552
|
+
conversation: { messages },
|
|
13553
|
+
jobs: {
|
|
13554
|
+
byId: Object.fromEntries(
|
|
13555
|
+
jobs.map((job) => {
|
|
13556
|
+
const record = job && typeof job === "object" ? job : null;
|
|
13557
|
+
const id = typeof record?.jobId === "string" ? record.jobId : typeof record?.id === "string" ? record.id : null;
|
|
13558
|
+
return id ? [id, record] : null;
|
|
13559
|
+
}).filter(
|
|
13560
|
+
(entry) => Boolean(entry)
|
|
13561
|
+
)
|
|
13562
|
+
)
|
|
13563
|
+
}
|
|
13564
|
+
};
|
|
13565
|
+
const heap = normalizeHeapSnapshot({
|
|
13566
|
+
entriesByPath: Object.fromEntries(
|
|
13567
|
+
entries.map((entry) => {
|
|
13568
|
+
return entry?.path ? [
|
|
13569
|
+
entry.path,
|
|
13570
|
+
entry
|
|
13571
|
+
] : null;
|
|
13572
|
+
}).filter(
|
|
13573
|
+
(entry) => Boolean(entry)
|
|
13574
|
+
)
|
|
13575
|
+
),
|
|
13576
|
+
listsByName: Object.fromEntries(
|
|
13577
|
+
lists.map((list) => {
|
|
13578
|
+
return list?.name ? [list.name, list] : null;
|
|
13579
|
+
}).filter(
|
|
13580
|
+
(entry) => Boolean(entry)
|
|
13581
|
+
)
|
|
13582
|
+
),
|
|
13583
|
+
variablesByName: this.getHeap().variablesByName,
|
|
13584
|
+
updatedAt: Date.now()
|
|
13585
|
+
});
|
|
13586
|
+
const allItems = buildSessionTranscript({
|
|
13587
|
+
liveDoc,
|
|
13588
|
+
sessionHeap: heap
|
|
13589
|
+
});
|
|
13590
|
+
const limit = Math.max(
|
|
13591
|
+
1,
|
|
13592
|
+
Math.min(500, Math.floor(options.limit ?? 100))
|
|
13593
|
+
);
|
|
13594
|
+
const offset = typeof options.cursor === "string" ? Number.parseInt(options.cursor, 10) || 0 : 0;
|
|
13595
|
+
const items = allItems.slice(offset, offset + limit);
|
|
13596
|
+
const nextOffset = offset + items.length;
|
|
13597
|
+
return {
|
|
13598
|
+
items,
|
|
13599
|
+
nextCursor: nextOffset < allItems.length ? String(nextOffset) : null,
|
|
13600
|
+
totalCount: allItems.length
|
|
13601
|
+
};
|
|
13602
|
+
}
|
|
13603
|
+
};
|
|
13604
|
+
}
|
|
12872
13605
|
async graphql(query, variables) {
|
|
12873
13606
|
return this.environment.graphql(query, variables);
|
|
12874
13607
|
}
|
|
@@ -14078,15 +14811,15 @@ var Granular = class _Granular {
|
|
|
14078
14811
|
};
|
|
14079
14812
|
|
|
14080
14813
|
// src/agent-harness.ts
|
|
14081
|
-
function
|
|
14814
|
+
function asRecord4(value) {
|
|
14082
14815
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
14083
14816
|
return value;
|
|
14084
14817
|
}
|
|
14085
|
-
function
|
|
14818
|
+
function asArray2(value) {
|
|
14086
14819
|
return Array.isArray(value) ? value : [];
|
|
14087
14820
|
}
|
|
14088
14821
|
function toSortedRecords(value) {
|
|
14089
|
-
return Object.values(
|
|
14822
|
+
return Object.values(asRecord4(value) || {}).map((entry) => asRecord4(entry)).filter((entry) => Boolean(entry));
|
|
14090
14823
|
}
|
|
14091
14824
|
function uniqueStrings(values, maxCount) {
|
|
14092
14825
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -14112,7 +14845,7 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
|
|
|
14112
14845
|
const headline = entry.label || entry.id || entry.path || "Unknown";
|
|
14113
14846
|
const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
|
|
14114
14847
|
const classLabel = entry.className || "unknown";
|
|
14115
|
-
const preview =
|
|
14848
|
+
const preview = asArray2(entry.fields).filter(
|
|
14116
14849
|
(field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
|
|
14117
14850
|
).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
|
|
14118
14851
|
return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
|
|
@@ -14249,14 +14982,14 @@ function normalizeActionSummaryForPrompt(line) {
|
|
|
14249
14982
|
return line.replace(/\blimit=/g, "perPage=").replace(/\blimit:/g, "perPage:");
|
|
14250
14983
|
}
|
|
14251
14984
|
function getCurrentClosureId(liveDoc) {
|
|
14252
|
-
const loop =
|
|
14985
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14253
14986
|
return typeof loop?.currentClosureId === "string" ? loop.currentClosureId : null;
|
|
14254
14987
|
}
|
|
14255
14988
|
function getLatestClosure(liveDoc) {
|
|
14256
|
-
const loop =
|
|
14989
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14257
14990
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14258
|
-
const closuresById =
|
|
14259
|
-
const currentClosure = currentClosureId ?
|
|
14991
|
+
const closuresById = asRecord4(loop?.closuresById) || {};
|
|
14992
|
+
const currentClosure = currentClosureId ? asRecord4(closuresById[currentClosureId]) : null;
|
|
14260
14993
|
if (currentClosure) {
|
|
14261
14994
|
return {
|
|
14262
14995
|
...currentClosure,
|
|
@@ -14265,7 +14998,7 @@ function getLatestClosure(liveDoc) {
|
|
|
14265
14998
|
}
|
|
14266
14999
|
const closures = [];
|
|
14267
15000
|
for (const [closureId, value] of Object.entries(closuresById)) {
|
|
14268
|
-
const record =
|
|
15001
|
+
const record = asRecord4(value);
|
|
14269
15002
|
if (!record) continue;
|
|
14270
15003
|
closures.push({ ...record, closureId });
|
|
14271
15004
|
}
|
|
@@ -14302,10 +15035,10 @@ function getJobTimestamp(job) {
|
|
|
14302
15035
|
return Number(job.finishedAt) || Number(job.startedAt) || Number(job.submittedAt) || 0;
|
|
14303
15036
|
}
|
|
14304
15037
|
function getJobRecords(liveDoc) {
|
|
14305
|
-
const jobsById =
|
|
15038
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14306
15039
|
const jobs = [];
|
|
14307
15040
|
for (const [jobId, value] of Object.entries(jobsById)) {
|
|
14308
|
-
const record =
|
|
15041
|
+
const record = asRecord4(value);
|
|
14309
15042
|
if (!record) continue;
|
|
14310
15043
|
jobs.push({ ...record, jobId });
|
|
14311
15044
|
}
|
|
@@ -14315,7 +15048,7 @@ function getJobRecords(liveDoc) {
|
|
|
14315
15048
|
return jobs;
|
|
14316
15049
|
}
|
|
14317
15050
|
function getPromptRecordsFromJobs(liveDoc) {
|
|
14318
|
-
return getJobRecords(liveDoc).flatMap((job) => Object.values(
|
|
15051
|
+
return getJobRecords(liveDoc).flatMap((job) => Object.values(asRecord4(job.prompts) || {})).map((prompt) => asRecord4(prompt)).filter((prompt) => Boolean(prompt));
|
|
14319
15052
|
}
|
|
14320
15053
|
function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
14321
15054
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
@@ -14330,15 +15063,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14330
15063
|
const openDecisionIds = [];
|
|
14331
15064
|
const openPromptIds = [];
|
|
14332
15065
|
for (const job of jobs) {
|
|
14333
|
-
for (const line of
|
|
15066
|
+
for (const line of asArray2(job.actionSummary)) {
|
|
14334
15067
|
if (typeof line === "string" && line.trim()) {
|
|
14335
15068
|
actionSummaryLines.push(line.trim());
|
|
14336
15069
|
}
|
|
14337
15070
|
}
|
|
14338
|
-
for (const rawEvent of
|
|
14339
|
-
const event =
|
|
14340
|
-
const details =
|
|
14341
|
-
const outcome =
|
|
15071
|
+
for (const rawEvent of asArray2(job.actionTrace)) {
|
|
15072
|
+
const event = asRecord4(rawEvent);
|
|
15073
|
+
const details = asRecord4(event?.details);
|
|
15074
|
+
const outcome = asRecord4(event?.outcome);
|
|
14342
15075
|
if (typeof details?.name === "string") {
|
|
14343
15076
|
variableNames.push(details.name);
|
|
14344
15077
|
}
|
|
@@ -14365,7 +15098,7 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14365
15098
|
}
|
|
14366
15099
|
}
|
|
14367
15100
|
}
|
|
14368
|
-
const loop =
|
|
15101
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14369
15102
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14370
15103
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
14371
15104
|
const status = typeof task.status === "string" ? task.status : "pending";
|
|
@@ -14413,15 +15146,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14413
15146
|
openPromptIds.push(prompt.id);
|
|
14414
15147
|
}
|
|
14415
15148
|
}
|
|
14416
|
-
const heap =
|
|
14417
|
-
const variablesByName =
|
|
14418
|
-
const listsByName =
|
|
15149
|
+
const heap = asRecord4(liveDoc?.heap);
|
|
15150
|
+
const variablesByName = asRecord4(heap?.variablesByName) || {};
|
|
15151
|
+
const listsByName = asRecord4(heap?.listsByName) || {};
|
|
14419
15152
|
const recentHints = extractFocusHintsFromActionSummary(actionSummaryLines);
|
|
14420
15153
|
variableNames.push(...recentHints.variableNames);
|
|
14421
15154
|
listNames.push(...recentHints.listNames);
|
|
14422
15155
|
entryPaths.push(...recentHints.entryPaths);
|
|
14423
15156
|
for (const variableName of uniqueStrings(variableNames)) {
|
|
14424
|
-
const variable =
|
|
15157
|
+
const variable = asRecord4(variablesByName[variableName]);
|
|
14425
15158
|
if (!variable) continue;
|
|
14426
15159
|
if (typeof variable.listName === "string") {
|
|
14427
15160
|
listNames.push(variable.listName);
|
|
@@ -14431,13 +15164,13 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14431
15164
|
}
|
|
14432
15165
|
}
|
|
14433
15166
|
for (const listName of uniqueStrings(listNames)) {
|
|
14434
|
-
const list =
|
|
14435
|
-
for (const path2 of
|
|
15167
|
+
const list = asRecord4(listsByName[listName]);
|
|
15168
|
+
for (const path2 of asArray2(list?.paths).slice(0, 4)) {
|
|
14436
15169
|
entryPaths.push(path2);
|
|
14437
15170
|
}
|
|
14438
15171
|
}
|
|
14439
15172
|
if (variableNames.length === 0 && boundary.reason !== "request_start") {
|
|
14440
|
-
const recentVariables = Object.values(variablesByName).map((value) =>
|
|
15173
|
+
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);
|
|
14441
15174
|
for (const variable of recentVariables) {
|
|
14442
15175
|
if (typeof variable.name === "string") {
|
|
14443
15176
|
variableNames.push(variable.name);
|
|
@@ -14520,12 +15253,12 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14520
15253
|
}
|
|
14521
15254
|
function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
14522
15255
|
if (pendingPrompts.length > 0) return true;
|
|
14523
|
-
const jobsById =
|
|
15256
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14524
15257
|
for (const job of Object.values(jobsById)) {
|
|
14525
|
-
const prompts =
|
|
15258
|
+
const prompts = asRecord4(asRecord4(job)?.prompts);
|
|
14526
15259
|
if (!prompts) continue;
|
|
14527
15260
|
for (const prompt of Object.values(prompts)) {
|
|
14528
|
-
const record =
|
|
15261
|
+
const record = asRecord4(prompt);
|
|
14529
15262
|
if (record?.status === "open") return true;
|
|
14530
15263
|
}
|
|
14531
15264
|
}
|
|
@@ -14533,7 +15266,7 @@ function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
|
14533
15266
|
}
|
|
14534
15267
|
function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
14535
15268
|
const lines = [];
|
|
14536
|
-
const loop =
|
|
15269
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14537
15270
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
14538
15271
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14539
15272
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
@@ -14593,8 +15326,8 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14593
15326
|
const title = typeof decision.title === "string" && decision.title.trim() ? decision.title.trim() : "Decision";
|
|
14594
15327
|
const decisionId = typeof decision.decisionId === "string" ? decision.decisionId : "unknown";
|
|
14595
15328
|
if (status === "open") {
|
|
14596
|
-
const candidatePreview =
|
|
14597
|
-
const record =
|
|
15329
|
+
const candidatePreview = asArray2(decision.candidates).slice(0, 3).map((candidate) => {
|
|
15330
|
+
const record = asRecord4(candidate);
|
|
14598
15331
|
if (!record) return null;
|
|
14599
15332
|
const candidateId = typeof record.id === "string" ? record.id : "unknown";
|
|
14600
15333
|
const candidateLabel = typeof record.label === "string" && record.label.trim() ? record.label.trim() : candidateId;
|
|
@@ -14604,7 +15337,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14604
15337
|
`- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`
|
|
14605
15338
|
);
|
|
14606
15339
|
} else {
|
|
14607
|
-
const selected =
|
|
15340
|
+
const selected = asRecord4(decision.selected);
|
|
14608
15341
|
const label = typeof selected?.label === "string" ? selected.label : typeof selected?.id === "string" ? selected.id : "unknown";
|
|
14609
15342
|
lines.push(`- [resolved] ${title} (${decisionId}) -> ${label}`);
|
|
14610
15343
|
}
|
|
@@ -14617,12 +15350,12 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14617
15350
|
title: prompt.title,
|
|
14618
15351
|
message: prompt.message
|
|
14619
15352
|
})),
|
|
14620
|
-
...Object.values(
|
|
15353
|
+
...Object.values(asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord4(asRecord4(job)?.prompts) || {})).map((prompt) => asRecord4(prompt)).filter(
|
|
14621
15354
|
(prompt) => Boolean(prompt && prompt.status === "open")
|
|
14622
15355
|
)
|
|
14623
15356
|
];
|
|
14624
15357
|
const visiblePrompts = boundary.reason === "request_start" ? openPrompts.filter((prompt) => {
|
|
14625
|
-
const promptRecord =
|
|
15358
|
+
const promptRecord = asRecord4(prompt);
|
|
14626
15359
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
14627
15360
|
const promptId = typeof promptRecord?.id === "string" ? promptRecord.id : typeof prompt.id === "string" ? prompt.id : null;
|
|
14628
15361
|
return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some(
|
|
@@ -14641,7 +15374,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14641
15374
|
}
|
|
14642
15375
|
}
|
|
14643
15376
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14644
|
-
const closureRecord = currentClosureId ?
|
|
15377
|
+
const closureRecord = currentClosureId ? asRecord4(asRecord4(loop?.closuresById)?.[currentClosureId]) : null;
|
|
14645
15378
|
const visibleClosure = closureRecord && (boundary.reason !== "request_start" || (Number(closureRecord.createdAt) || 0) >= boundary.timestamp) ? closureRecord : null;
|
|
14646
15379
|
lines.push("", "Loop Closure:");
|
|
14647
15380
|
if (visibleClosure) {
|
|
@@ -14654,10 +15387,10 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14654
15387
|
return lines.join("\n");
|
|
14655
15388
|
}
|
|
14656
15389
|
function projectHeapSummary(heap, options) {
|
|
14657
|
-
const heapRecord =
|
|
14658
|
-
const entriesByPath =
|
|
14659
|
-
const listsByName =
|
|
14660
|
-
const variablesByName =
|
|
15390
|
+
const heapRecord = asRecord4(heap) || {};
|
|
15391
|
+
const entriesByPath = asRecord4(heapRecord.entriesByPath) || {};
|
|
15392
|
+
const listsByName = asRecord4(heapRecord.listsByName) || {};
|
|
15393
|
+
const variablesByName = asRecord4(heapRecord.variablesByName) || {};
|
|
14661
15394
|
const focusedVariableNames = new Set(
|
|
14662
15395
|
uniqueStrings(options?.focus?.variableNames || [])
|
|
14663
15396
|
);
|
|
@@ -14672,7 +15405,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14672
15405
|
const maxVariables = options?.maxVariables ?? (hasFocus ? 4 : 6);
|
|
14673
15406
|
const maxLists = options?.maxLists ?? (hasFocus ? 3 : 4);
|
|
14674
15407
|
const maxEntries = options?.maxEntries ?? (hasFocus ? 5 : 6);
|
|
14675
|
-
const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) =>
|
|
15408
|
+
const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).sort((left, right) => {
|
|
14676
15409
|
const leftFocused = left.name && focusedVariableNames.has(left.name) ? 1 : 0;
|
|
14677
15410
|
const rightFocused = right.name && focusedVariableNames.has(right.name) ? 1 : 0;
|
|
14678
15411
|
return rightFocused - leftFocused || (right.updatedAt || 0) - (left.updatedAt || 0);
|
|
@@ -14686,7 +15419,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14686
15419
|
for (const variable of variables) {
|
|
14687
15420
|
if (variable.entryPath) referencedPaths.add(variable.entryPath);
|
|
14688
15421
|
if (variable.listName) {
|
|
14689
|
-
const list =
|
|
15422
|
+
const list = asRecord4(
|
|
14690
15423
|
listsByName[variable.listName]
|
|
14691
15424
|
);
|
|
14692
15425
|
for (const path2 of list?.paths || []) referencedPaths.add(path2);
|
|
@@ -14695,10 +15428,10 @@ function projectHeapSummary(heap, options) {
|
|
|
14695
15428
|
for (const path2 of focusedEntryPaths) {
|
|
14696
15429
|
referencedPaths.add(path2);
|
|
14697
15430
|
}
|
|
14698
|
-
const visibleLists = Object.values(listsByName).map((value) =>
|
|
15431
|
+
const visibleLists = Object.values(listsByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter(
|
|
14699
15432
|
(list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))
|
|
14700
15433
|
).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
|
|
14701
|
-
const visibleEntries = Object.values(entriesByPath).map((value) =>
|
|
15434
|
+
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);
|
|
14702
15435
|
const lines = [];
|
|
14703
15436
|
lines.push("Variables:");
|
|
14704
15437
|
if (variables.length === 0) {
|
|
@@ -14712,7 +15445,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14712
15445
|
continue;
|
|
14713
15446
|
}
|
|
14714
15447
|
if (variable.kind === "entry") {
|
|
14715
|
-
const entry = variable.entryPath ?
|
|
15448
|
+
const entry = variable.entryPath ? asRecord4(
|
|
14716
15449
|
entriesByPath[variable.entryPath]
|
|
14717
15450
|
) : null;
|
|
14718
15451
|
lines.push(
|
|
@@ -14720,7 +15453,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14720
15453
|
);
|
|
14721
15454
|
continue;
|
|
14722
15455
|
}
|
|
14723
|
-
const list = variable.listName ?
|
|
15456
|
+
const list = variable.listName ? asRecord4(listsByName[variable.listName]) : null;
|
|
14724
15457
|
lines.push(
|
|
14725
15458
|
`- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`
|
|
14726
15459
|
);
|
|
@@ -14753,7 +15486,7 @@ function createHarnessVerifierSnapshot(input) {
|
|
|
14753
15486
|
input.projectionOptions
|
|
14754
15487
|
);
|
|
14755
15488
|
const heapDigest = hashString(
|
|
14756
|
-
projectHeapSummary(
|
|
15489
|
+
projectHeapSummary(asRecord4(input.liveDoc?.heap), {
|
|
14757
15490
|
focus: workflowFocus
|
|
14758
15491
|
})
|
|
14759
15492
|
) || "00000000";
|
|
@@ -15079,246 +15812,12 @@ ${loopBlock}
|
|
|
15079
15812
|
- Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
|
|
15080
15813
|
}
|
|
15081
15814
|
|
|
15082
|
-
// src/job-presentation.ts
|
|
15083
|
-
var RESPONSE_KEYS = [
|
|
15084
|
-
"reply",
|
|
15085
|
-
"response",
|
|
15086
|
-
"text",
|
|
15087
|
-
"message",
|
|
15088
|
-
"summary",
|
|
15089
|
-
"answer"
|
|
15090
|
-
];
|
|
15091
|
-
var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
|
|
15092
|
-
var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
|
|
15093
|
-
var LIST_KEY_CANDIDATES = ["listName"];
|
|
15094
|
-
var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
|
|
15095
|
-
var VARIABLE_KEY_CANDIDATES = ["variableName"];
|
|
15096
|
-
var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
|
|
15097
|
-
var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
|
|
15098
|
-
function asRecord3(value) {
|
|
15099
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15100
|
-
return value;
|
|
15101
|
-
}
|
|
15102
|
-
function normalizeText(value) {
|
|
15103
|
-
if (typeof value !== "string") return null;
|
|
15104
|
-
const trimmed = value.trim();
|
|
15105
|
-
if (!trimmed) return null;
|
|
15106
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
15107
|
-
return null;
|
|
15108
|
-
}
|
|
15109
|
-
return trimmed;
|
|
15110
|
-
}
|
|
15111
|
-
function humanTextFromStdout(stdout) {
|
|
15112
|
-
for (const line of [...stdout].reverse()) {
|
|
15113
|
-
const normalized = normalizeText(line);
|
|
15114
|
-
if (!normalized) continue;
|
|
15115
|
-
if (/^[A-Z_]+:/.test(normalized)) continue;
|
|
15116
|
-
return normalized;
|
|
15117
|
-
}
|
|
15118
|
-
return null;
|
|
15119
|
-
}
|
|
15120
|
-
function pushString(target, value) {
|
|
15121
|
-
if (typeof value === "string" && value.trim()) {
|
|
15122
|
-
target.add(value.trim());
|
|
15123
|
-
}
|
|
15124
|
-
}
|
|
15125
|
-
function pushStringArray(target, value) {
|
|
15126
|
-
if (!Array.isArray(value)) return;
|
|
15127
|
-
for (const item of value) {
|
|
15128
|
-
pushString(target, item);
|
|
15129
|
-
}
|
|
15130
|
-
}
|
|
15131
|
-
function collectReferencesFromRecord(record, refs) {
|
|
15132
|
-
for (const key of ENTRY_KEY_CANDIDATES)
|
|
15133
|
-
pushString(refs.entryPaths, record[key]);
|
|
15134
|
-
for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
|
|
15135
|
-
pushStringArray(refs.entryPaths, record[key]);
|
|
15136
|
-
for (const key of LIST_KEY_CANDIDATES)
|
|
15137
|
-
pushString(refs.listNames, record[key]);
|
|
15138
|
-
for (const key of LIST_ARRAY_KEY_CANDIDATES)
|
|
15139
|
-
pushStringArray(refs.listNames, record[key]);
|
|
15140
|
-
for (const key of VARIABLE_KEY_CANDIDATES)
|
|
15141
|
-
pushString(refs.variableNames, record[key]);
|
|
15142
|
-
for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
|
|
15143
|
-
pushStringArray(refs.variableNames, record[key]);
|
|
15144
|
-
}
|
|
15145
|
-
function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
15146
|
-
if (value === null || value === void 0 || depth > 4 || seen.has(value))
|
|
15147
|
-
return;
|
|
15148
|
-
if (typeof value === "string") {
|
|
15149
|
-
const trimmed = value.trim();
|
|
15150
|
-
if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
|
|
15151
|
-
if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
|
|
15152
|
-
if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
|
|
15153
|
-
return;
|
|
15154
|
-
}
|
|
15155
|
-
if (Array.isArray(value)) {
|
|
15156
|
-
seen.add(value);
|
|
15157
|
-
for (const item of value.slice(0, 24)) {
|
|
15158
|
-
scanForHeapReferences(item, heap, refs, depth + 1, seen);
|
|
15159
|
-
}
|
|
15160
|
-
return;
|
|
15161
|
-
}
|
|
15162
|
-
const record = asRecord3(value);
|
|
15163
|
-
if (!record) return;
|
|
15164
|
-
seen.add(value);
|
|
15165
|
-
collectReferencesFromRecord(record, refs);
|
|
15166
|
-
for (const key of UI_CONTAINER_KEYS) {
|
|
15167
|
-
const nested = asRecord3(record[key]);
|
|
15168
|
-
if (nested) collectReferencesFromRecord(nested, refs);
|
|
15169
|
-
}
|
|
15170
|
-
for (const nested of Object.values(record).slice(0, 24)) {
|
|
15171
|
-
scanForHeapReferences(nested, heap, refs, depth + 1, seen);
|
|
15172
|
-
}
|
|
15173
|
-
}
|
|
15174
|
-
function resolveVariablesToReferences(variableNames, heap, refs) {
|
|
15175
|
-
for (const variableName of variableNames) {
|
|
15176
|
-
const variable = heap.variablesByName?.[variableName];
|
|
15177
|
-
if (!variable) continue;
|
|
15178
|
-
if (variable.kind === "entry" && variable.entryPath) {
|
|
15179
|
-
refs.entryPaths.add(variable.entryPath);
|
|
15180
|
-
}
|
|
15181
|
-
if (variable.kind === "list" && variable.listName) {
|
|
15182
|
-
refs.listNames.add(variable.listName);
|
|
15183
|
-
}
|
|
15184
|
-
}
|
|
15185
|
-
}
|
|
15186
|
-
function sortEntries(entries) {
|
|
15187
|
-
return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
15188
|
-
}
|
|
15189
|
-
function sortLists(lists) {
|
|
15190
|
-
return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
15191
|
-
}
|
|
15192
|
-
function dedupeEntries(entries) {
|
|
15193
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15194
|
-
const result = [];
|
|
15195
|
-
for (const entry of entries) {
|
|
15196
|
-
if (!entry?.path || seen.has(entry.path)) continue;
|
|
15197
|
-
seen.add(entry.path);
|
|
15198
|
-
result.push(entry);
|
|
15199
|
-
}
|
|
15200
|
-
return result;
|
|
15201
|
-
}
|
|
15202
|
-
function dedupeLists(lists) {
|
|
15203
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15204
|
-
const result = [];
|
|
15205
|
-
for (const list of lists) {
|
|
15206
|
-
if (!list?.name || seen.has(list.name)) continue;
|
|
15207
|
-
seen.add(list.name);
|
|
15208
|
-
result.push(list);
|
|
15209
|
-
}
|
|
15210
|
-
return result;
|
|
15211
|
-
}
|
|
15212
|
-
function extractResponseText(result, stdout) {
|
|
15213
|
-
const directText = normalizeText(result);
|
|
15214
|
-
if (directText) return directText;
|
|
15215
|
-
const record = asRecord3(result);
|
|
15216
|
-
if (record) {
|
|
15217
|
-
for (const key of RESPONSE_KEYS) {
|
|
15218
|
-
const normalized = normalizeText(record[key]);
|
|
15219
|
-
if (normalized) return normalized;
|
|
15220
|
-
}
|
|
15221
|
-
for (const containerKey of UI_CONTAINER_KEYS) {
|
|
15222
|
-
const nested = asRecord3(record[containerKey]);
|
|
15223
|
-
if (!nested) continue;
|
|
15224
|
-
for (const key of RESPONSE_KEYS) {
|
|
15225
|
-
const normalized = normalizeText(nested[key]);
|
|
15226
|
-
if (normalized) return normalized;
|
|
15227
|
-
}
|
|
15228
|
-
}
|
|
15229
|
-
}
|
|
15230
|
-
return humanTextFromStdout(stdout);
|
|
15231
|
-
}
|
|
15232
|
-
function fallbackResponseText(entries, lists) {
|
|
15233
|
-
if (entries.length > 0) {
|
|
15234
|
-
return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
|
|
15235
|
-
}
|
|
15236
|
-
if (lists.length > 0) {
|
|
15237
|
-
const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
|
|
15238
|
-
if (emptyOnly) {
|
|
15239
|
-
return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
|
|
15240
|
-
}
|
|
15241
|
-
return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
|
|
15242
|
-
}
|
|
15243
|
-
return null;
|
|
15244
|
-
}
|
|
15245
|
-
function getJobRelatedEntries(heap, jobId) {
|
|
15246
|
-
return sortEntries(
|
|
15247
|
-
Object.values(heap.entriesByPath || {}).filter(
|
|
15248
|
-
(entry) => entry.relatedJobIds?.includes(jobId)
|
|
15249
|
-
)
|
|
15250
|
-
);
|
|
15251
|
-
}
|
|
15252
|
-
function getJobRelatedLists(heap, jobId) {
|
|
15253
|
-
return sortLists(
|
|
15254
|
-
Object.values(heap.listsByName || {}).filter(
|
|
15255
|
-
(list) => list.relatedJobIds?.includes(jobId)
|
|
15256
|
-
)
|
|
15257
|
-
);
|
|
15258
|
-
}
|
|
15259
|
-
function entriesFromLists(lists, heap) {
|
|
15260
|
-
const entries = [];
|
|
15261
|
-
for (const list of lists) {
|
|
15262
|
-
for (const path2 of list.paths || []) {
|
|
15263
|
-
const entry = heap.entriesByPath?.[path2];
|
|
15264
|
-
if (entry) entries.push(entry);
|
|
15265
|
-
}
|
|
15266
|
-
}
|
|
15267
|
-
return entries;
|
|
15268
|
-
}
|
|
15269
|
-
function resolveJobPresentation({
|
|
15270
|
-
jobId,
|
|
15271
|
-
result,
|
|
15272
|
-
stdout = [],
|
|
15273
|
-
sessionHeap,
|
|
15274
|
-
allowExplicitArtifacts = true
|
|
15275
|
-
}) {
|
|
15276
|
-
const refs = {
|
|
15277
|
-
entryPaths: /* @__PURE__ */ new Set(),
|
|
15278
|
-
listNames: /* @__PURE__ */ new Set(),
|
|
15279
|
-
variableNames: /* @__PURE__ */ new Set()
|
|
15280
|
-
};
|
|
15281
|
-
if (allowExplicitArtifacts) {
|
|
15282
|
-
scanForHeapReferences(result, sessionHeap, refs);
|
|
15283
|
-
resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
|
|
15284
|
-
}
|
|
15285
|
-
const referencedLists = sortLists(
|
|
15286
|
-
[...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
|
|
15287
|
-
);
|
|
15288
|
-
const referencedEntries = sortEntries(
|
|
15289
|
-
[...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
|
|
15290
|
-
);
|
|
15291
|
-
const jobLists = getJobRelatedLists(sessionHeap, jobId);
|
|
15292
|
-
const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
|
|
15293
|
-
const changedEntries = dedupeEntries([
|
|
15294
|
-
...jobEntries,
|
|
15295
|
-
...entriesFromLists(jobLists, sessionHeap)
|
|
15296
|
-
]);
|
|
15297
|
-
const explicitLists = dedupeLists(referencedLists);
|
|
15298
|
-
const explicitEntries = dedupeEntries([
|
|
15299
|
-
...referencedEntries,
|
|
15300
|
-
...entriesFromLists(referencedLists, sessionHeap)
|
|
15301
|
-
]);
|
|
15302
|
-
const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
|
|
15303
|
-
const lists = hasExplicitArtifacts ? explicitLists : jobLists;
|
|
15304
|
-
const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
|
|
15305
|
-
const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
|
|
15306
|
-
return {
|
|
15307
|
-
responseText,
|
|
15308
|
-
entries,
|
|
15309
|
-
lists,
|
|
15310
|
-
changedEntries,
|
|
15311
|
-
changedLists: jobLists,
|
|
15312
|
-
hasExplicitArtifacts
|
|
15313
|
-
};
|
|
15314
|
-
}
|
|
15315
|
-
|
|
15316
15815
|
// src/agent-evals.ts
|
|
15317
15816
|
var DEFAULT_CONTROLLER_BUDGETS = {
|
|
15318
15817
|
maxIterations: 6,
|
|
15319
15818
|
maxNoProgressIterations: 2
|
|
15320
15819
|
};
|
|
15321
|
-
function
|
|
15820
|
+
function asRecord5(value) {
|
|
15322
15821
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15323
15822
|
return value;
|
|
15324
15823
|
}
|
|
@@ -15366,7 +15865,7 @@ function buildArtifactDir(baseDir, suiteName = "granular-agent-evals") {
|
|
|
15366
15865
|
function createTimestampedArtifactDirectory(options) {
|
|
15367
15866
|
return buildArtifactDir(options?.baseDir, options?.suiteName);
|
|
15368
15867
|
}
|
|
15369
|
-
function
|
|
15868
|
+
function asArray3(value) {
|
|
15370
15869
|
if (!value) return [];
|
|
15371
15870
|
return Array.isArray(value) ? value : [value];
|
|
15372
15871
|
}
|
|
@@ -15384,7 +15883,7 @@ function buildScenarioSteps(scenario) {
|
|
|
15384
15883
|
request: scenario.request,
|
|
15385
15884
|
human: scenario.human,
|
|
15386
15885
|
expect: scenario.expect,
|
|
15387
|
-
inspect: [...
|
|
15886
|
+
inspect: [...asArray3(scenario.inspect), ...asArray3(scenario.verify)],
|
|
15388
15887
|
check: scenario.check,
|
|
15389
15888
|
maxIterations: scenario.maxIterations,
|
|
15390
15889
|
setup: {
|
|
@@ -15425,12 +15924,12 @@ function buildHistory(entries) {
|
|
|
15425
15924
|
);
|
|
15426
15925
|
}
|
|
15427
15926
|
function getOpenPromptsFromDoc(liveDoc) {
|
|
15428
|
-
const jobsById =
|
|
15927
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15429
15928
|
const prompts = [];
|
|
15430
15929
|
for (const job of Object.values(jobsById)) {
|
|
15431
|
-
const promptRecords =
|
|
15930
|
+
const promptRecords = asRecord5(asRecord5(job)?.prompts) || {};
|
|
15432
15931
|
for (const raw of Object.values(promptRecords)) {
|
|
15433
|
-
const record =
|
|
15932
|
+
const record = asRecord5(raw);
|
|
15434
15933
|
if (!record || record.status !== "open" || typeof record.promptId !== "string")
|
|
15435
15934
|
continue;
|
|
15436
15935
|
const prompt = normalizePrompt({
|
|
@@ -15451,11 +15950,11 @@ function getOpenPromptsFromDoc(liveDoc) {
|
|
|
15451
15950
|
return prompts;
|
|
15452
15951
|
}
|
|
15453
15952
|
function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
|
|
15454
|
-
const jobsById =
|
|
15953
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15455
15954
|
return prompts.filter((prompt) => {
|
|
15456
15955
|
for (const jobRecord of Object.values(jobsById)) {
|
|
15457
|
-
const promptsById =
|
|
15458
|
-
const promptRecord =
|
|
15956
|
+
const promptsById = asRecord5(asRecord5(jobRecord)?.prompts) || {};
|
|
15957
|
+
const promptRecord = asRecord5(promptsById[prompt.id]);
|
|
15459
15958
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
15460
15959
|
if (openedAt >= boundaryTimestamp) return true;
|
|
15461
15960
|
}
|
|
@@ -15548,10 +16047,10 @@ ${modelOutputInstruction()}`
|
|
|
15548
16047
|
);
|
|
15549
16048
|
}
|
|
15550
16049
|
const raw = await response.json();
|
|
15551
|
-
const content =
|
|
15552
|
-
|
|
16050
|
+
const content = asRecord5(
|
|
16051
|
+
asRecord5(raw.choices?.[0])?.message
|
|
15553
16052
|
)?.content;
|
|
15554
|
-
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) =>
|
|
16053
|
+
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => asRecord5(part)?.text || "").join("") : "";
|
|
15555
16054
|
const parsed = extractJsonObject(text);
|
|
15556
16055
|
if (!parsed) {
|
|
15557
16056
|
if (attempt < 3) {
|
|
@@ -15603,17 +16102,17 @@ async function withTimeout(promise, ms, label) {
|
|
|
15603
16102
|
}
|
|
15604
16103
|
}
|
|
15605
16104
|
function getActionSummary(liveDoc, jobId) {
|
|
15606
|
-
const jobsById =
|
|
15607
|
-
const job =
|
|
16105
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
16106
|
+
const job = asRecord5(jobsById[jobId]);
|
|
15608
16107
|
return Array.isArray(job?.actionSummary) ? job.actionSummary.filter(
|
|
15609
16108
|
(line) => typeof line === "string"
|
|
15610
16109
|
) : [];
|
|
15611
16110
|
}
|
|
15612
16111
|
function normalizeHeapSnapshot2(heap) {
|
|
15613
16112
|
return {
|
|
15614
|
-
entriesByPath:
|
|
15615
|
-
listsByName:
|
|
15616
|
-
variablesByName:
|
|
16113
|
+
entriesByPath: asRecord5(heap?.entriesByPath) || {},
|
|
16114
|
+
listsByName: asRecord5(heap?.listsByName) || {},
|
|
16115
|
+
variablesByName: asRecord5(heap?.variablesByName) || {},
|
|
15617
16116
|
updatedAt: typeof heap?.updatedAt === "number" ? heap.updatedAt : Date.now()
|
|
15618
16117
|
};
|
|
15619
16118
|
}
|
|
@@ -15799,8 +16298,8 @@ async function runAgentEvalSuite(options) {
|
|
|
15799
16298
|
);
|
|
15800
16299
|
}
|
|
15801
16300
|
const inspectionResults = [];
|
|
15802
|
-
const stepChecks =
|
|
15803
|
-
const stepInspections =
|
|
16301
|
+
const stepChecks = asArray3(step.check);
|
|
16302
|
+
const stepInspections = asArray3(step.inspect);
|
|
15804
16303
|
const context = {
|
|
15805
16304
|
conversation,
|
|
15806
16305
|
environment: conversation.environment,
|
|
@@ -15813,7 +16312,7 @@ async function runAgentEvalSuite(options) {
|
|
|
15813
16312
|
promptInteractions: completed.promptInteractions,
|
|
15814
16313
|
result: completed.result,
|
|
15815
16314
|
heap: normalizeHeapSnapshot2(
|
|
15816
|
-
|
|
16315
|
+
asRecord5(
|
|
15817
16316
|
cloneJson(conversation.environment.document)?.heap
|
|
15818
16317
|
)
|
|
15819
16318
|
),
|
|
@@ -16024,7 +16523,7 @@ function createAgentEvalHarness(options) {
|
|
|
16024
16523
|
}
|
|
16025
16524
|
function buildCheckContext(conversation, completed, turnDir) {
|
|
16026
16525
|
const liveDoc = cloneJson(conversation.environment.document);
|
|
16027
|
-
const heap = normalizeHeapSnapshot2(
|
|
16526
|
+
const heap = normalizeHeapSnapshot2(asRecord5(liveDoc?.heap));
|
|
16028
16527
|
return {
|
|
16029
16528
|
conversation,
|
|
16030
16529
|
environment: conversation.environment,
|
|
@@ -16100,7 +16599,7 @@ function createAgentEvalHarness(options) {
|
|
|
16100
16599
|
jobId: pending.job.id,
|
|
16101
16600
|
result: resumed.result,
|
|
16102
16601
|
stdout: [...pending.stdout, ...resumed.stdout],
|
|
16103
|
-
sessionHeap: normalizeHeapSnapshot2(
|
|
16602
|
+
sessionHeap: normalizeHeapSnapshot2(asRecord5(liveDoc?.heap))
|
|
16104
16603
|
});
|
|
16105
16604
|
const responseText = presentation.responseText || pending.finalReply || "Done.";
|
|
16106
16605
|
pending.conversation.history.push({
|
|
@@ -16310,7 +16809,7 @@ function createAgentEvalHarness(options) {
|
|
|
16310
16809
|
const settledLiveDoc = cloneJson(
|
|
16311
16810
|
conversation.environment.document
|
|
16312
16811
|
);
|
|
16313
|
-
const sessionHeap = normalizeHeapSnapshot2(
|
|
16812
|
+
const sessionHeap = normalizeHeapSnapshot2(asRecord5(settledLiveDoc?.heap));
|
|
16314
16813
|
const presentation = resolveJobPresentation({
|
|
16315
16814
|
jobId: job.id,
|
|
16316
16815
|
result: outcome.result,
|