@granular-software/sdk 0.4.32 → 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 +775 -307
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +775 -307
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/cli/index.js +744 -21
- package/dist/{client-C2Gk641P.d.mts → client-DTvI5MUG.d.mts} +129 -33
- package/dist/{client-C2Gk641P.d.ts → client-DTvI5MUG.d.ts} +129 -33
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +9293 -9125
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9293 -9125
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/agent-evals.mjs
CHANGED
|
@@ -5619,6 +5619,540 @@ var JobImplementation = class {
|
|
|
5619
5619
|
}
|
|
5620
5620
|
};
|
|
5621
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
|
+
|
|
5622
6156
|
// src/endpoints.ts
|
|
5623
6157
|
var LOCAL_API_URL = "ws://localhost:8787/granular";
|
|
5624
6158
|
var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
|
|
@@ -12158,7 +12692,9 @@ var Environment = class {
|
|
|
12158
12692
|
{ target: targetPath }
|
|
12159
12693
|
);
|
|
12160
12694
|
if (result.errors?.length) {
|
|
12161
|
-
throw new Error(
|
|
12695
|
+
throw new Error(
|
|
12696
|
+
`attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12697
|
+
);
|
|
12162
12698
|
}
|
|
12163
12699
|
}
|
|
12164
12700
|
/**
|
|
@@ -12193,7 +12729,9 @@ var Environment = class {
|
|
|
12193
12729
|
}`
|
|
12194
12730
|
);
|
|
12195
12731
|
if (result.errors?.length) {
|
|
12196
|
-
throw new Error(
|
|
12732
|
+
throw new Error(
|
|
12733
|
+
`detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12734
|
+
);
|
|
12197
12735
|
}
|
|
12198
12736
|
}
|
|
12199
12737
|
/**
|
|
@@ -12315,7 +12853,9 @@ var Environment = class {
|
|
|
12315
12853
|
async _runGraphql(query, label) {
|
|
12316
12854
|
const result = await this.graphql(query);
|
|
12317
12855
|
if (result.errors?.length) {
|
|
12318
|
-
throw new Error(
|
|
12856
|
+
throw new Error(
|
|
12857
|
+
`${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12858
|
+
);
|
|
12319
12859
|
}
|
|
12320
12860
|
return result.data;
|
|
12321
12861
|
}
|
|
@@ -12850,6 +13390,9 @@ var EnvironmentSession = class extends Session {
|
|
|
12850
13390
|
get envName() {
|
|
12851
13391
|
return this.environment.envName;
|
|
12852
13392
|
}
|
|
13393
|
+
get tag() {
|
|
13394
|
+
return this.environment.tag;
|
|
13395
|
+
}
|
|
12853
13396
|
get versionId() {
|
|
12854
13397
|
return this.environment.versionId;
|
|
12855
13398
|
}
|
|
@@ -12869,12 +13412,171 @@ var EnvironmentSession = class extends Session {
|
|
|
12869
13412
|
return this.environment.feedback;
|
|
12870
13413
|
}
|
|
12871
13414
|
/**
|
|
12872
|
-
* Return a plain JS
|
|
13415
|
+
* Return a plain JS copy of the synced session heap.
|
|
12873
13416
|
*/
|
|
12874
13417
|
getHeap() {
|
|
12875
13418
|
const doc = this.document;
|
|
12876
13419
|
return normalizeHeapSnapshot(doc?.heap);
|
|
12877
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
|
+
}
|
|
12878
13580
|
async graphql(query, variables) {
|
|
12879
13581
|
return this.environment.graphql(query, variables);
|
|
12880
13582
|
}
|
|
@@ -14084,15 +14786,15 @@ var Granular = class _Granular {
|
|
|
14084
14786
|
};
|
|
14085
14787
|
|
|
14086
14788
|
// src/agent-harness.ts
|
|
14087
|
-
function
|
|
14789
|
+
function asRecord4(value) {
|
|
14088
14790
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
14089
14791
|
return value;
|
|
14090
14792
|
}
|
|
14091
|
-
function
|
|
14793
|
+
function asArray2(value) {
|
|
14092
14794
|
return Array.isArray(value) ? value : [];
|
|
14093
14795
|
}
|
|
14094
14796
|
function toSortedRecords(value) {
|
|
14095
|
-
return Object.values(
|
|
14797
|
+
return Object.values(asRecord4(value) || {}).map((entry) => asRecord4(entry)).filter((entry) => Boolean(entry));
|
|
14096
14798
|
}
|
|
14097
14799
|
function uniqueStrings(values, maxCount) {
|
|
14098
14800
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -14118,7 +14820,7 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
|
|
|
14118
14820
|
const headline = entry.label || entry.id || entry.path || "Unknown";
|
|
14119
14821
|
const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
|
|
14120
14822
|
const classLabel = entry.className || "unknown";
|
|
14121
|
-
const preview =
|
|
14823
|
+
const preview = asArray2(entry.fields).filter(
|
|
14122
14824
|
(field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
|
|
14123
14825
|
).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
|
|
14124
14826
|
return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
|
|
@@ -14255,14 +14957,14 @@ function normalizeActionSummaryForPrompt(line) {
|
|
|
14255
14957
|
return line.replace(/\blimit=/g, "perPage=").replace(/\blimit:/g, "perPage:");
|
|
14256
14958
|
}
|
|
14257
14959
|
function getCurrentClosureId(liveDoc) {
|
|
14258
|
-
const loop =
|
|
14960
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14259
14961
|
return typeof loop?.currentClosureId === "string" ? loop.currentClosureId : null;
|
|
14260
14962
|
}
|
|
14261
14963
|
function getLatestClosure(liveDoc) {
|
|
14262
|
-
const loop =
|
|
14964
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14263
14965
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14264
|
-
const closuresById =
|
|
14265
|
-
const currentClosure = currentClosureId ?
|
|
14966
|
+
const closuresById = asRecord4(loop?.closuresById) || {};
|
|
14967
|
+
const currentClosure = currentClosureId ? asRecord4(closuresById[currentClosureId]) : null;
|
|
14266
14968
|
if (currentClosure) {
|
|
14267
14969
|
return {
|
|
14268
14970
|
...currentClosure,
|
|
@@ -14271,7 +14973,7 @@ function getLatestClosure(liveDoc) {
|
|
|
14271
14973
|
}
|
|
14272
14974
|
const closures = [];
|
|
14273
14975
|
for (const [closureId, value] of Object.entries(closuresById)) {
|
|
14274
|
-
const record =
|
|
14976
|
+
const record = asRecord4(value);
|
|
14275
14977
|
if (!record) continue;
|
|
14276
14978
|
closures.push({ ...record, closureId });
|
|
14277
14979
|
}
|
|
@@ -14308,10 +15010,10 @@ function getJobTimestamp(job) {
|
|
|
14308
15010
|
return Number(job.finishedAt) || Number(job.startedAt) || Number(job.submittedAt) || 0;
|
|
14309
15011
|
}
|
|
14310
15012
|
function getJobRecords(liveDoc) {
|
|
14311
|
-
const jobsById =
|
|
15013
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14312
15014
|
const jobs = [];
|
|
14313
15015
|
for (const [jobId, value] of Object.entries(jobsById)) {
|
|
14314
|
-
const record =
|
|
15016
|
+
const record = asRecord4(value);
|
|
14315
15017
|
if (!record) continue;
|
|
14316
15018
|
jobs.push({ ...record, jobId });
|
|
14317
15019
|
}
|
|
@@ -14321,7 +15023,7 @@ function getJobRecords(liveDoc) {
|
|
|
14321
15023
|
return jobs;
|
|
14322
15024
|
}
|
|
14323
15025
|
function getPromptRecordsFromJobs(liveDoc) {
|
|
14324
|
-
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));
|
|
14325
15027
|
}
|
|
14326
15028
|
function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
14327
15029
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
@@ -14336,15 +15038,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14336
15038
|
const openDecisionIds = [];
|
|
14337
15039
|
const openPromptIds = [];
|
|
14338
15040
|
for (const job of jobs) {
|
|
14339
|
-
for (const line of
|
|
15041
|
+
for (const line of asArray2(job.actionSummary)) {
|
|
14340
15042
|
if (typeof line === "string" && line.trim()) {
|
|
14341
15043
|
actionSummaryLines.push(line.trim());
|
|
14342
15044
|
}
|
|
14343
15045
|
}
|
|
14344
|
-
for (const rawEvent of
|
|
14345
|
-
const event =
|
|
14346
|
-
const details =
|
|
14347
|
-
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);
|
|
14348
15050
|
if (typeof details?.name === "string") {
|
|
14349
15051
|
variableNames.push(details.name);
|
|
14350
15052
|
}
|
|
@@ -14371,7 +15073,7 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14371
15073
|
}
|
|
14372
15074
|
}
|
|
14373
15075
|
}
|
|
14374
|
-
const loop =
|
|
15076
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14375
15077
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14376
15078
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
14377
15079
|
const status = typeof task.status === "string" ? task.status : "pending";
|
|
@@ -14419,15 +15121,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14419
15121
|
openPromptIds.push(prompt.id);
|
|
14420
15122
|
}
|
|
14421
15123
|
}
|
|
14422
|
-
const heap =
|
|
14423
|
-
const variablesByName =
|
|
14424
|
-
const listsByName =
|
|
15124
|
+
const heap = asRecord4(liveDoc?.heap);
|
|
15125
|
+
const variablesByName = asRecord4(heap?.variablesByName) || {};
|
|
15126
|
+
const listsByName = asRecord4(heap?.listsByName) || {};
|
|
14425
15127
|
const recentHints = extractFocusHintsFromActionSummary(actionSummaryLines);
|
|
14426
15128
|
variableNames.push(...recentHints.variableNames);
|
|
14427
15129
|
listNames.push(...recentHints.listNames);
|
|
14428
15130
|
entryPaths.push(...recentHints.entryPaths);
|
|
14429
15131
|
for (const variableName of uniqueStrings(variableNames)) {
|
|
14430
|
-
const variable =
|
|
15132
|
+
const variable = asRecord4(variablesByName[variableName]);
|
|
14431
15133
|
if (!variable) continue;
|
|
14432
15134
|
if (typeof variable.listName === "string") {
|
|
14433
15135
|
listNames.push(variable.listName);
|
|
@@ -14437,13 +15139,13 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14437
15139
|
}
|
|
14438
15140
|
}
|
|
14439
15141
|
for (const listName of uniqueStrings(listNames)) {
|
|
14440
|
-
const list =
|
|
14441
|
-
for (const path2 of
|
|
15142
|
+
const list = asRecord4(listsByName[listName]);
|
|
15143
|
+
for (const path2 of asArray2(list?.paths).slice(0, 4)) {
|
|
14442
15144
|
entryPaths.push(path2);
|
|
14443
15145
|
}
|
|
14444
15146
|
}
|
|
14445
15147
|
if (variableNames.length === 0 && boundary.reason !== "request_start") {
|
|
14446
|
-
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);
|
|
14447
15149
|
for (const variable of recentVariables) {
|
|
14448
15150
|
if (typeof variable.name === "string") {
|
|
14449
15151
|
variableNames.push(variable.name);
|
|
@@ -14526,12 +15228,12 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14526
15228
|
}
|
|
14527
15229
|
function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
14528
15230
|
if (pendingPrompts.length > 0) return true;
|
|
14529
|
-
const jobsById =
|
|
15231
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14530
15232
|
for (const job of Object.values(jobsById)) {
|
|
14531
|
-
const prompts =
|
|
15233
|
+
const prompts = asRecord4(asRecord4(job)?.prompts);
|
|
14532
15234
|
if (!prompts) continue;
|
|
14533
15235
|
for (const prompt of Object.values(prompts)) {
|
|
14534
|
-
const record =
|
|
15236
|
+
const record = asRecord4(prompt);
|
|
14535
15237
|
if (record?.status === "open") return true;
|
|
14536
15238
|
}
|
|
14537
15239
|
}
|
|
@@ -14539,7 +15241,7 @@ function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
|
14539
15241
|
}
|
|
14540
15242
|
function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
14541
15243
|
const lines = [];
|
|
14542
|
-
const loop =
|
|
15244
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14543
15245
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
14544
15246
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14545
15247
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
@@ -14599,8 +15301,8 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14599
15301
|
const title = typeof decision.title === "string" && decision.title.trim() ? decision.title.trim() : "Decision";
|
|
14600
15302
|
const decisionId = typeof decision.decisionId === "string" ? decision.decisionId : "unknown";
|
|
14601
15303
|
if (status === "open") {
|
|
14602
|
-
const candidatePreview =
|
|
14603
|
-
const record =
|
|
15304
|
+
const candidatePreview = asArray2(decision.candidates).slice(0, 3).map((candidate) => {
|
|
15305
|
+
const record = asRecord4(candidate);
|
|
14604
15306
|
if (!record) return null;
|
|
14605
15307
|
const candidateId = typeof record.id === "string" ? record.id : "unknown";
|
|
14606
15308
|
const candidateLabel = typeof record.label === "string" && record.label.trim() ? record.label.trim() : candidateId;
|
|
@@ -14610,7 +15312,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14610
15312
|
`- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`
|
|
14611
15313
|
);
|
|
14612
15314
|
} else {
|
|
14613
|
-
const selected =
|
|
15315
|
+
const selected = asRecord4(decision.selected);
|
|
14614
15316
|
const label = typeof selected?.label === "string" ? selected.label : typeof selected?.id === "string" ? selected.id : "unknown";
|
|
14615
15317
|
lines.push(`- [resolved] ${title} (${decisionId}) -> ${label}`);
|
|
14616
15318
|
}
|
|
@@ -14623,12 +15325,12 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14623
15325
|
title: prompt.title,
|
|
14624
15326
|
message: prompt.message
|
|
14625
15327
|
})),
|
|
14626
|
-
...Object.values(
|
|
15328
|
+
...Object.values(asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord4(asRecord4(job)?.prompts) || {})).map((prompt) => asRecord4(prompt)).filter(
|
|
14627
15329
|
(prompt) => Boolean(prompt && prompt.status === "open")
|
|
14628
15330
|
)
|
|
14629
15331
|
];
|
|
14630
15332
|
const visiblePrompts = boundary.reason === "request_start" ? openPrompts.filter((prompt) => {
|
|
14631
|
-
const promptRecord =
|
|
15333
|
+
const promptRecord = asRecord4(prompt);
|
|
14632
15334
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
14633
15335
|
const promptId = typeof promptRecord?.id === "string" ? promptRecord.id : typeof prompt.id === "string" ? prompt.id : null;
|
|
14634
15336
|
return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some(
|
|
@@ -14647,7 +15349,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14647
15349
|
}
|
|
14648
15350
|
}
|
|
14649
15351
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14650
|
-
const closureRecord = currentClosureId ?
|
|
15352
|
+
const closureRecord = currentClosureId ? asRecord4(asRecord4(loop?.closuresById)?.[currentClosureId]) : null;
|
|
14651
15353
|
const visibleClosure = closureRecord && (boundary.reason !== "request_start" || (Number(closureRecord.createdAt) || 0) >= boundary.timestamp) ? closureRecord : null;
|
|
14652
15354
|
lines.push("", "Loop Closure:");
|
|
14653
15355
|
if (visibleClosure) {
|
|
@@ -14660,10 +15362,10 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14660
15362
|
return lines.join("\n");
|
|
14661
15363
|
}
|
|
14662
15364
|
function projectHeapSummary(heap, options) {
|
|
14663
|
-
const heapRecord =
|
|
14664
|
-
const entriesByPath =
|
|
14665
|
-
const listsByName =
|
|
14666
|
-
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) || {};
|
|
14667
15369
|
const focusedVariableNames = new Set(
|
|
14668
15370
|
uniqueStrings(options?.focus?.variableNames || [])
|
|
14669
15371
|
);
|
|
@@ -14678,7 +15380,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14678
15380
|
const maxVariables = options?.maxVariables ?? (hasFocus ? 4 : 6);
|
|
14679
15381
|
const maxLists = options?.maxLists ?? (hasFocus ? 3 : 4);
|
|
14680
15382
|
const maxEntries = options?.maxEntries ?? (hasFocus ? 5 : 6);
|
|
14681
|
-
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) => {
|
|
14682
15384
|
const leftFocused = left.name && focusedVariableNames.has(left.name) ? 1 : 0;
|
|
14683
15385
|
const rightFocused = right.name && focusedVariableNames.has(right.name) ? 1 : 0;
|
|
14684
15386
|
return rightFocused - leftFocused || (right.updatedAt || 0) - (left.updatedAt || 0);
|
|
@@ -14692,7 +15394,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14692
15394
|
for (const variable of variables) {
|
|
14693
15395
|
if (variable.entryPath) referencedPaths.add(variable.entryPath);
|
|
14694
15396
|
if (variable.listName) {
|
|
14695
|
-
const list =
|
|
15397
|
+
const list = asRecord4(
|
|
14696
15398
|
listsByName[variable.listName]
|
|
14697
15399
|
);
|
|
14698
15400
|
for (const path2 of list?.paths || []) referencedPaths.add(path2);
|
|
@@ -14701,10 +15403,10 @@ function projectHeapSummary(heap, options) {
|
|
|
14701
15403
|
for (const path2 of focusedEntryPaths) {
|
|
14702
15404
|
referencedPaths.add(path2);
|
|
14703
15405
|
}
|
|
14704
|
-
const visibleLists = Object.values(listsByName).map((value) =>
|
|
15406
|
+
const visibleLists = Object.values(listsByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter(
|
|
14705
15407
|
(list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))
|
|
14706
15408
|
).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
|
|
14707
|
-
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);
|
|
14708
15410
|
const lines = [];
|
|
14709
15411
|
lines.push("Variables:");
|
|
14710
15412
|
if (variables.length === 0) {
|
|
@@ -14718,7 +15420,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14718
15420
|
continue;
|
|
14719
15421
|
}
|
|
14720
15422
|
if (variable.kind === "entry") {
|
|
14721
|
-
const entry = variable.entryPath ?
|
|
15423
|
+
const entry = variable.entryPath ? asRecord4(
|
|
14722
15424
|
entriesByPath[variable.entryPath]
|
|
14723
15425
|
) : null;
|
|
14724
15426
|
lines.push(
|
|
@@ -14726,7 +15428,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14726
15428
|
);
|
|
14727
15429
|
continue;
|
|
14728
15430
|
}
|
|
14729
|
-
const list = variable.listName ?
|
|
15431
|
+
const list = variable.listName ? asRecord4(listsByName[variable.listName]) : null;
|
|
14730
15432
|
lines.push(
|
|
14731
15433
|
`- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`
|
|
14732
15434
|
);
|
|
@@ -14759,7 +15461,7 @@ function createHarnessVerifierSnapshot(input) {
|
|
|
14759
15461
|
input.projectionOptions
|
|
14760
15462
|
);
|
|
14761
15463
|
const heapDigest = hashString(
|
|
14762
|
-
projectHeapSummary(
|
|
15464
|
+
projectHeapSummary(asRecord4(input.liveDoc?.heap), {
|
|
14763
15465
|
focus: workflowFocus
|
|
14764
15466
|
})
|
|
14765
15467
|
) || "00000000";
|
|
@@ -15085,246 +15787,12 @@ ${loopBlock}
|
|
|
15085
15787
|
- Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
|
|
15086
15788
|
}
|
|
15087
15789
|
|
|
15088
|
-
// src/job-presentation.ts
|
|
15089
|
-
var RESPONSE_KEYS = [
|
|
15090
|
-
"reply",
|
|
15091
|
-
"response",
|
|
15092
|
-
"text",
|
|
15093
|
-
"message",
|
|
15094
|
-
"summary",
|
|
15095
|
-
"answer"
|
|
15096
|
-
];
|
|
15097
|
-
var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
|
|
15098
|
-
var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
|
|
15099
|
-
var LIST_KEY_CANDIDATES = ["listName"];
|
|
15100
|
-
var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
|
|
15101
|
-
var VARIABLE_KEY_CANDIDATES = ["variableName"];
|
|
15102
|
-
var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
|
|
15103
|
-
var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
|
|
15104
|
-
function asRecord3(value) {
|
|
15105
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15106
|
-
return value;
|
|
15107
|
-
}
|
|
15108
|
-
function normalizeText(value) {
|
|
15109
|
-
if (typeof value !== "string") return null;
|
|
15110
|
-
const trimmed = value.trim();
|
|
15111
|
-
if (!trimmed) return null;
|
|
15112
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
15113
|
-
return null;
|
|
15114
|
-
}
|
|
15115
|
-
return trimmed;
|
|
15116
|
-
}
|
|
15117
|
-
function humanTextFromStdout(stdout) {
|
|
15118
|
-
for (const line of [...stdout].reverse()) {
|
|
15119
|
-
const normalized = normalizeText(line);
|
|
15120
|
-
if (!normalized) continue;
|
|
15121
|
-
if (/^[A-Z_]+:/.test(normalized)) continue;
|
|
15122
|
-
return normalized;
|
|
15123
|
-
}
|
|
15124
|
-
return null;
|
|
15125
|
-
}
|
|
15126
|
-
function pushString(target, value) {
|
|
15127
|
-
if (typeof value === "string" && value.trim()) {
|
|
15128
|
-
target.add(value.trim());
|
|
15129
|
-
}
|
|
15130
|
-
}
|
|
15131
|
-
function pushStringArray(target, value) {
|
|
15132
|
-
if (!Array.isArray(value)) return;
|
|
15133
|
-
for (const item of value) {
|
|
15134
|
-
pushString(target, item);
|
|
15135
|
-
}
|
|
15136
|
-
}
|
|
15137
|
-
function collectReferencesFromRecord(record, refs) {
|
|
15138
|
-
for (const key of ENTRY_KEY_CANDIDATES)
|
|
15139
|
-
pushString(refs.entryPaths, record[key]);
|
|
15140
|
-
for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
|
|
15141
|
-
pushStringArray(refs.entryPaths, record[key]);
|
|
15142
|
-
for (const key of LIST_KEY_CANDIDATES)
|
|
15143
|
-
pushString(refs.listNames, record[key]);
|
|
15144
|
-
for (const key of LIST_ARRAY_KEY_CANDIDATES)
|
|
15145
|
-
pushStringArray(refs.listNames, record[key]);
|
|
15146
|
-
for (const key of VARIABLE_KEY_CANDIDATES)
|
|
15147
|
-
pushString(refs.variableNames, record[key]);
|
|
15148
|
-
for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
|
|
15149
|
-
pushStringArray(refs.variableNames, record[key]);
|
|
15150
|
-
}
|
|
15151
|
-
function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
15152
|
-
if (value === null || value === void 0 || depth > 4 || seen.has(value))
|
|
15153
|
-
return;
|
|
15154
|
-
if (typeof value === "string") {
|
|
15155
|
-
const trimmed = value.trim();
|
|
15156
|
-
if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
|
|
15157
|
-
if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
|
|
15158
|
-
if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
|
|
15159
|
-
return;
|
|
15160
|
-
}
|
|
15161
|
-
if (Array.isArray(value)) {
|
|
15162
|
-
seen.add(value);
|
|
15163
|
-
for (const item of value.slice(0, 24)) {
|
|
15164
|
-
scanForHeapReferences(item, heap, refs, depth + 1, seen);
|
|
15165
|
-
}
|
|
15166
|
-
return;
|
|
15167
|
-
}
|
|
15168
|
-
const record = asRecord3(value);
|
|
15169
|
-
if (!record) return;
|
|
15170
|
-
seen.add(value);
|
|
15171
|
-
collectReferencesFromRecord(record, refs);
|
|
15172
|
-
for (const key of UI_CONTAINER_KEYS) {
|
|
15173
|
-
const nested = asRecord3(record[key]);
|
|
15174
|
-
if (nested) collectReferencesFromRecord(nested, refs);
|
|
15175
|
-
}
|
|
15176
|
-
for (const nested of Object.values(record).slice(0, 24)) {
|
|
15177
|
-
scanForHeapReferences(nested, heap, refs, depth + 1, seen);
|
|
15178
|
-
}
|
|
15179
|
-
}
|
|
15180
|
-
function resolveVariablesToReferences(variableNames, heap, refs) {
|
|
15181
|
-
for (const variableName of variableNames) {
|
|
15182
|
-
const variable = heap.variablesByName?.[variableName];
|
|
15183
|
-
if (!variable) continue;
|
|
15184
|
-
if (variable.kind === "entry" && variable.entryPath) {
|
|
15185
|
-
refs.entryPaths.add(variable.entryPath);
|
|
15186
|
-
}
|
|
15187
|
-
if (variable.kind === "list" && variable.listName) {
|
|
15188
|
-
refs.listNames.add(variable.listName);
|
|
15189
|
-
}
|
|
15190
|
-
}
|
|
15191
|
-
}
|
|
15192
|
-
function sortEntries(entries) {
|
|
15193
|
-
return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
15194
|
-
}
|
|
15195
|
-
function sortLists(lists) {
|
|
15196
|
-
return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
15197
|
-
}
|
|
15198
|
-
function dedupeEntries(entries) {
|
|
15199
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15200
|
-
const result = [];
|
|
15201
|
-
for (const entry of entries) {
|
|
15202
|
-
if (!entry?.path || seen.has(entry.path)) continue;
|
|
15203
|
-
seen.add(entry.path);
|
|
15204
|
-
result.push(entry);
|
|
15205
|
-
}
|
|
15206
|
-
return result;
|
|
15207
|
-
}
|
|
15208
|
-
function dedupeLists(lists) {
|
|
15209
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15210
|
-
const result = [];
|
|
15211
|
-
for (const list of lists) {
|
|
15212
|
-
if (!list?.name || seen.has(list.name)) continue;
|
|
15213
|
-
seen.add(list.name);
|
|
15214
|
-
result.push(list);
|
|
15215
|
-
}
|
|
15216
|
-
return result;
|
|
15217
|
-
}
|
|
15218
|
-
function extractResponseText(result, stdout) {
|
|
15219
|
-
const directText = normalizeText(result);
|
|
15220
|
-
if (directText) return directText;
|
|
15221
|
-
const record = asRecord3(result);
|
|
15222
|
-
if (record) {
|
|
15223
|
-
for (const key of RESPONSE_KEYS) {
|
|
15224
|
-
const normalized = normalizeText(record[key]);
|
|
15225
|
-
if (normalized) return normalized;
|
|
15226
|
-
}
|
|
15227
|
-
for (const containerKey of UI_CONTAINER_KEYS) {
|
|
15228
|
-
const nested = asRecord3(record[containerKey]);
|
|
15229
|
-
if (!nested) continue;
|
|
15230
|
-
for (const key of RESPONSE_KEYS) {
|
|
15231
|
-
const normalized = normalizeText(nested[key]);
|
|
15232
|
-
if (normalized) return normalized;
|
|
15233
|
-
}
|
|
15234
|
-
}
|
|
15235
|
-
}
|
|
15236
|
-
return humanTextFromStdout(stdout);
|
|
15237
|
-
}
|
|
15238
|
-
function fallbackResponseText(entries, lists) {
|
|
15239
|
-
if (entries.length > 0) {
|
|
15240
|
-
return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
|
|
15241
|
-
}
|
|
15242
|
-
if (lists.length > 0) {
|
|
15243
|
-
const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
|
|
15244
|
-
if (emptyOnly) {
|
|
15245
|
-
return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
|
|
15246
|
-
}
|
|
15247
|
-
return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
|
|
15248
|
-
}
|
|
15249
|
-
return null;
|
|
15250
|
-
}
|
|
15251
|
-
function getJobRelatedEntries(heap, jobId) {
|
|
15252
|
-
return sortEntries(
|
|
15253
|
-
Object.values(heap.entriesByPath || {}).filter(
|
|
15254
|
-
(entry) => entry.relatedJobIds?.includes(jobId)
|
|
15255
|
-
)
|
|
15256
|
-
);
|
|
15257
|
-
}
|
|
15258
|
-
function getJobRelatedLists(heap, jobId) {
|
|
15259
|
-
return sortLists(
|
|
15260
|
-
Object.values(heap.listsByName || {}).filter(
|
|
15261
|
-
(list) => list.relatedJobIds?.includes(jobId)
|
|
15262
|
-
)
|
|
15263
|
-
);
|
|
15264
|
-
}
|
|
15265
|
-
function entriesFromLists(lists, heap) {
|
|
15266
|
-
const entries = [];
|
|
15267
|
-
for (const list of lists) {
|
|
15268
|
-
for (const path2 of list.paths || []) {
|
|
15269
|
-
const entry = heap.entriesByPath?.[path2];
|
|
15270
|
-
if (entry) entries.push(entry);
|
|
15271
|
-
}
|
|
15272
|
-
}
|
|
15273
|
-
return entries;
|
|
15274
|
-
}
|
|
15275
|
-
function resolveJobPresentation({
|
|
15276
|
-
jobId,
|
|
15277
|
-
result,
|
|
15278
|
-
stdout = [],
|
|
15279
|
-
sessionHeap,
|
|
15280
|
-
allowExplicitArtifacts = true
|
|
15281
|
-
}) {
|
|
15282
|
-
const refs = {
|
|
15283
|
-
entryPaths: /* @__PURE__ */ new Set(),
|
|
15284
|
-
listNames: /* @__PURE__ */ new Set(),
|
|
15285
|
-
variableNames: /* @__PURE__ */ new Set()
|
|
15286
|
-
};
|
|
15287
|
-
if (allowExplicitArtifacts) {
|
|
15288
|
-
scanForHeapReferences(result, sessionHeap, refs);
|
|
15289
|
-
resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
|
|
15290
|
-
}
|
|
15291
|
-
const referencedLists = sortLists(
|
|
15292
|
-
[...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
|
|
15293
|
-
);
|
|
15294
|
-
const referencedEntries = sortEntries(
|
|
15295
|
-
[...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
|
|
15296
|
-
);
|
|
15297
|
-
const jobLists = getJobRelatedLists(sessionHeap, jobId);
|
|
15298
|
-
const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
|
|
15299
|
-
const changedEntries = dedupeEntries([
|
|
15300
|
-
...jobEntries,
|
|
15301
|
-
...entriesFromLists(jobLists, sessionHeap)
|
|
15302
|
-
]);
|
|
15303
|
-
const explicitLists = dedupeLists(referencedLists);
|
|
15304
|
-
const explicitEntries = dedupeEntries([
|
|
15305
|
-
...referencedEntries,
|
|
15306
|
-
...entriesFromLists(referencedLists, sessionHeap)
|
|
15307
|
-
]);
|
|
15308
|
-
const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
|
|
15309
|
-
const lists = hasExplicitArtifacts ? explicitLists : jobLists;
|
|
15310
|
-
const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
|
|
15311
|
-
const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
|
|
15312
|
-
return {
|
|
15313
|
-
responseText,
|
|
15314
|
-
entries,
|
|
15315
|
-
lists,
|
|
15316
|
-
changedEntries,
|
|
15317
|
-
changedLists: jobLists,
|
|
15318
|
-
hasExplicitArtifacts
|
|
15319
|
-
};
|
|
15320
|
-
}
|
|
15321
|
-
|
|
15322
15790
|
// src/agent-evals.ts
|
|
15323
15791
|
var DEFAULT_CONTROLLER_BUDGETS = {
|
|
15324
15792
|
maxIterations: 6,
|
|
15325
15793
|
maxNoProgressIterations: 2
|
|
15326
15794
|
};
|
|
15327
|
-
function
|
|
15795
|
+
function asRecord5(value) {
|
|
15328
15796
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15329
15797
|
return value;
|
|
15330
15798
|
}
|
|
@@ -15372,7 +15840,7 @@ function buildArtifactDir(baseDir, suiteName = "granular-agent-evals") {
|
|
|
15372
15840
|
function createTimestampedArtifactDirectory(options) {
|
|
15373
15841
|
return buildArtifactDir(options?.baseDir, options?.suiteName);
|
|
15374
15842
|
}
|
|
15375
|
-
function
|
|
15843
|
+
function asArray3(value) {
|
|
15376
15844
|
if (!value) return [];
|
|
15377
15845
|
return Array.isArray(value) ? value : [value];
|
|
15378
15846
|
}
|
|
@@ -15390,7 +15858,7 @@ function buildScenarioSteps(scenario) {
|
|
|
15390
15858
|
request: scenario.request,
|
|
15391
15859
|
human: scenario.human,
|
|
15392
15860
|
expect: scenario.expect,
|
|
15393
|
-
inspect: [...
|
|
15861
|
+
inspect: [...asArray3(scenario.inspect), ...asArray3(scenario.verify)],
|
|
15394
15862
|
check: scenario.check,
|
|
15395
15863
|
maxIterations: scenario.maxIterations,
|
|
15396
15864
|
setup: {
|
|
@@ -15431,12 +15899,12 @@ function buildHistory(entries) {
|
|
|
15431
15899
|
);
|
|
15432
15900
|
}
|
|
15433
15901
|
function getOpenPromptsFromDoc(liveDoc) {
|
|
15434
|
-
const jobsById =
|
|
15902
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15435
15903
|
const prompts = [];
|
|
15436
15904
|
for (const job of Object.values(jobsById)) {
|
|
15437
|
-
const promptRecords =
|
|
15905
|
+
const promptRecords = asRecord5(asRecord5(job)?.prompts) || {};
|
|
15438
15906
|
for (const raw of Object.values(promptRecords)) {
|
|
15439
|
-
const record =
|
|
15907
|
+
const record = asRecord5(raw);
|
|
15440
15908
|
if (!record || record.status !== "open" || typeof record.promptId !== "string")
|
|
15441
15909
|
continue;
|
|
15442
15910
|
const prompt = normalizePrompt({
|
|
@@ -15457,11 +15925,11 @@ function getOpenPromptsFromDoc(liveDoc) {
|
|
|
15457
15925
|
return prompts;
|
|
15458
15926
|
}
|
|
15459
15927
|
function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
|
|
15460
|
-
const jobsById =
|
|
15928
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15461
15929
|
return prompts.filter((prompt) => {
|
|
15462
15930
|
for (const jobRecord of Object.values(jobsById)) {
|
|
15463
|
-
const promptsById =
|
|
15464
|
-
const promptRecord =
|
|
15931
|
+
const promptsById = asRecord5(asRecord5(jobRecord)?.prompts) || {};
|
|
15932
|
+
const promptRecord = asRecord5(promptsById[prompt.id]);
|
|
15465
15933
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
15466
15934
|
if (openedAt >= boundaryTimestamp) return true;
|
|
15467
15935
|
}
|
|
@@ -15554,10 +16022,10 @@ ${modelOutputInstruction()}`
|
|
|
15554
16022
|
);
|
|
15555
16023
|
}
|
|
15556
16024
|
const raw = await response.json();
|
|
15557
|
-
const content =
|
|
15558
|
-
|
|
16025
|
+
const content = asRecord5(
|
|
16026
|
+
asRecord5(raw.choices?.[0])?.message
|
|
15559
16027
|
)?.content;
|
|
15560
|
-
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("") : "";
|
|
15561
16029
|
const parsed = extractJsonObject(text);
|
|
15562
16030
|
if (!parsed) {
|
|
15563
16031
|
if (attempt < 3) {
|
|
@@ -15609,17 +16077,17 @@ async function withTimeout(promise, ms, label) {
|
|
|
15609
16077
|
}
|
|
15610
16078
|
}
|
|
15611
16079
|
function getActionSummary(liveDoc, jobId) {
|
|
15612
|
-
const jobsById =
|
|
15613
|
-
const job =
|
|
16080
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
16081
|
+
const job = asRecord5(jobsById[jobId]);
|
|
15614
16082
|
return Array.isArray(job?.actionSummary) ? job.actionSummary.filter(
|
|
15615
16083
|
(line) => typeof line === "string"
|
|
15616
16084
|
) : [];
|
|
15617
16085
|
}
|
|
15618
16086
|
function normalizeHeapSnapshot2(heap) {
|
|
15619
16087
|
return {
|
|
15620
|
-
entriesByPath:
|
|
15621
|
-
listsByName:
|
|
15622
|
-
variablesByName:
|
|
16088
|
+
entriesByPath: asRecord5(heap?.entriesByPath) || {},
|
|
16089
|
+
listsByName: asRecord5(heap?.listsByName) || {},
|
|
16090
|
+
variablesByName: asRecord5(heap?.variablesByName) || {},
|
|
15623
16091
|
updatedAt: typeof heap?.updatedAt === "number" ? heap.updatedAt : Date.now()
|
|
15624
16092
|
};
|
|
15625
16093
|
}
|
|
@@ -15805,8 +16273,8 @@ async function runAgentEvalSuite(options) {
|
|
|
15805
16273
|
);
|
|
15806
16274
|
}
|
|
15807
16275
|
const inspectionResults = [];
|
|
15808
|
-
const stepChecks =
|
|
15809
|
-
const stepInspections =
|
|
16276
|
+
const stepChecks = asArray3(step.check);
|
|
16277
|
+
const stepInspections = asArray3(step.inspect);
|
|
15810
16278
|
const context = {
|
|
15811
16279
|
conversation,
|
|
15812
16280
|
environment: conversation.environment,
|
|
@@ -15819,7 +16287,7 @@ async function runAgentEvalSuite(options) {
|
|
|
15819
16287
|
promptInteractions: completed.promptInteractions,
|
|
15820
16288
|
result: completed.result,
|
|
15821
16289
|
heap: normalizeHeapSnapshot2(
|
|
15822
|
-
|
|
16290
|
+
asRecord5(
|
|
15823
16291
|
cloneJson(conversation.environment.document)?.heap
|
|
15824
16292
|
)
|
|
15825
16293
|
),
|
|
@@ -16030,7 +16498,7 @@ function createAgentEvalHarness(options) {
|
|
|
16030
16498
|
}
|
|
16031
16499
|
function buildCheckContext(conversation, completed, turnDir) {
|
|
16032
16500
|
const liveDoc = cloneJson(conversation.environment.document);
|
|
16033
|
-
const heap = normalizeHeapSnapshot2(
|
|
16501
|
+
const heap = normalizeHeapSnapshot2(asRecord5(liveDoc?.heap));
|
|
16034
16502
|
return {
|
|
16035
16503
|
conversation,
|
|
16036
16504
|
environment: conversation.environment,
|
|
@@ -16106,7 +16574,7 @@ function createAgentEvalHarness(options) {
|
|
|
16106
16574
|
jobId: pending.job.id,
|
|
16107
16575
|
result: resumed.result,
|
|
16108
16576
|
stdout: [...pending.stdout, ...resumed.stdout],
|
|
16109
|
-
sessionHeap: normalizeHeapSnapshot2(
|
|
16577
|
+
sessionHeap: normalizeHeapSnapshot2(asRecord5(liveDoc?.heap))
|
|
16110
16578
|
});
|
|
16111
16579
|
const responseText = presentation.responseText || pending.finalReply || "Done.";
|
|
16112
16580
|
pending.conversation.history.push({
|
|
@@ -16316,7 +16784,7 @@ function createAgentEvalHarness(options) {
|
|
|
16316
16784
|
const settledLiveDoc = cloneJson(
|
|
16317
16785
|
conversation.environment.document
|
|
16318
16786
|
);
|
|
16319
|
-
const sessionHeap = normalizeHeapSnapshot2(
|
|
16787
|
+
const sessionHeap = normalizeHeapSnapshot2(asRecord5(settledLiveDoc?.heap));
|
|
16320
16788
|
const presentation = resolveJobPresentation({
|
|
16321
16789
|
jobId: job.id,
|
|
16322
16790
|
result: outcome.result,
|