@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.js
CHANGED
|
@@ -5644,6 +5644,540 @@ var JobImplementation = class {
|
|
|
5644
5644
|
}
|
|
5645
5645
|
};
|
|
5646
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
|
+
|
|
5647
6181
|
// src/endpoints.ts
|
|
5648
6182
|
var LOCAL_API_URL = "ws://localhost:8787/granular";
|
|
5649
6183
|
var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
|
|
@@ -12183,7 +12717,9 @@ var Environment = class {
|
|
|
12183
12717
|
{ target: targetPath }
|
|
12184
12718
|
);
|
|
12185
12719
|
if (result.errors?.length) {
|
|
12186
|
-
throw new Error(
|
|
12720
|
+
throw new Error(
|
|
12721
|
+
`attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12722
|
+
);
|
|
12187
12723
|
}
|
|
12188
12724
|
}
|
|
12189
12725
|
/**
|
|
@@ -12218,7 +12754,9 @@ var Environment = class {
|
|
|
12218
12754
|
}`
|
|
12219
12755
|
);
|
|
12220
12756
|
if (result.errors?.length) {
|
|
12221
|
-
throw new Error(
|
|
12757
|
+
throw new Error(
|
|
12758
|
+
`detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12759
|
+
);
|
|
12222
12760
|
}
|
|
12223
12761
|
}
|
|
12224
12762
|
/**
|
|
@@ -12340,7 +12878,9 @@ var Environment = class {
|
|
|
12340
12878
|
async _runGraphql(query, label) {
|
|
12341
12879
|
const result = await this.graphql(query);
|
|
12342
12880
|
if (result.errors?.length) {
|
|
12343
|
-
throw new Error(
|
|
12881
|
+
throw new Error(
|
|
12882
|
+
`${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12883
|
+
);
|
|
12344
12884
|
}
|
|
12345
12885
|
return result.data;
|
|
12346
12886
|
}
|
|
@@ -12875,6 +13415,9 @@ var EnvironmentSession = class extends Session {
|
|
|
12875
13415
|
get envName() {
|
|
12876
13416
|
return this.environment.envName;
|
|
12877
13417
|
}
|
|
13418
|
+
get tag() {
|
|
13419
|
+
return this.environment.tag;
|
|
13420
|
+
}
|
|
12878
13421
|
get versionId() {
|
|
12879
13422
|
return this.environment.versionId;
|
|
12880
13423
|
}
|
|
@@ -12894,12 +13437,171 @@ var EnvironmentSession = class extends Session {
|
|
|
12894
13437
|
return this.environment.feedback;
|
|
12895
13438
|
}
|
|
12896
13439
|
/**
|
|
12897
|
-
* Return a plain JS
|
|
13440
|
+
* Return a plain JS copy of the synced session heap.
|
|
12898
13441
|
*/
|
|
12899
13442
|
getHeap() {
|
|
12900
13443
|
const doc = this.document;
|
|
12901
13444
|
return normalizeHeapSnapshot(doc?.heap);
|
|
12902
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
|
+
}
|
|
12903
13605
|
async graphql(query, variables) {
|
|
12904
13606
|
return this.environment.graphql(query, variables);
|
|
12905
13607
|
}
|
|
@@ -14109,15 +14811,15 @@ var Granular = class _Granular {
|
|
|
14109
14811
|
};
|
|
14110
14812
|
|
|
14111
14813
|
// src/agent-harness.ts
|
|
14112
|
-
function
|
|
14814
|
+
function asRecord4(value) {
|
|
14113
14815
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
14114
14816
|
return value;
|
|
14115
14817
|
}
|
|
14116
|
-
function
|
|
14818
|
+
function asArray2(value) {
|
|
14117
14819
|
return Array.isArray(value) ? value : [];
|
|
14118
14820
|
}
|
|
14119
14821
|
function toSortedRecords(value) {
|
|
14120
|
-
return Object.values(
|
|
14822
|
+
return Object.values(asRecord4(value) || {}).map((entry) => asRecord4(entry)).filter((entry) => Boolean(entry));
|
|
14121
14823
|
}
|
|
14122
14824
|
function uniqueStrings(values, maxCount) {
|
|
14123
14825
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -14143,7 +14845,7 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
|
|
|
14143
14845
|
const headline = entry.label || entry.id || entry.path || "Unknown";
|
|
14144
14846
|
const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
|
|
14145
14847
|
const classLabel = entry.className || "unknown";
|
|
14146
|
-
const preview =
|
|
14848
|
+
const preview = asArray2(entry.fields).filter(
|
|
14147
14849
|
(field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
|
|
14148
14850
|
).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
|
|
14149
14851
|
return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
|
|
@@ -14280,14 +14982,14 @@ function normalizeActionSummaryForPrompt(line) {
|
|
|
14280
14982
|
return line.replace(/\blimit=/g, "perPage=").replace(/\blimit:/g, "perPage:");
|
|
14281
14983
|
}
|
|
14282
14984
|
function getCurrentClosureId(liveDoc) {
|
|
14283
|
-
const loop =
|
|
14985
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14284
14986
|
return typeof loop?.currentClosureId === "string" ? loop.currentClosureId : null;
|
|
14285
14987
|
}
|
|
14286
14988
|
function getLatestClosure(liveDoc) {
|
|
14287
|
-
const loop =
|
|
14989
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14288
14990
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14289
|
-
const closuresById =
|
|
14290
|
-
const currentClosure = currentClosureId ?
|
|
14991
|
+
const closuresById = asRecord4(loop?.closuresById) || {};
|
|
14992
|
+
const currentClosure = currentClosureId ? asRecord4(closuresById[currentClosureId]) : null;
|
|
14291
14993
|
if (currentClosure) {
|
|
14292
14994
|
return {
|
|
14293
14995
|
...currentClosure,
|
|
@@ -14296,7 +14998,7 @@ function getLatestClosure(liveDoc) {
|
|
|
14296
14998
|
}
|
|
14297
14999
|
const closures = [];
|
|
14298
15000
|
for (const [closureId, value] of Object.entries(closuresById)) {
|
|
14299
|
-
const record =
|
|
15001
|
+
const record = asRecord4(value);
|
|
14300
15002
|
if (!record) continue;
|
|
14301
15003
|
closures.push({ ...record, closureId });
|
|
14302
15004
|
}
|
|
@@ -14333,10 +15035,10 @@ function getJobTimestamp(job) {
|
|
|
14333
15035
|
return Number(job.finishedAt) || Number(job.startedAt) || Number(job.submittedAt) || 0;
|
|
14334
15036
|
}
|
|
14335
15037
|
function getJobRecords(liveDoc) {
|
|
14336
|
-
const jobsById =
|
|
15038
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14337
15039
|
const jobs = [];
|
|
14338
15040
|
for (const [jobId, value] of Object.entries(jobsById)) {
|
|
14339
|
-
const record =
|
|
15041
|
+
const record = asRecord4(value);
|
|
14340
15042
|
if (!record) continue;
|
|
14341
15043
|
jobs.push({ ...record, jobId });
|
|
14342
15044
|
}
|
|
@@ -14346,7 +15048,7 @@ function getJobRecords(liveDoc) {
|
|
|
14346
15048
|
return jobs;
|
|
14347
15049
|
}
|
|
14348
15050
|
function getPromptRecordsFromJobs(liveDoc) {
|
|
14349
|
-
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));
|
|
14350
15052
|
}
|
|
14351
15053
|
function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
14352
15054
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
@@ -14361,15 +15063,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14361
15063
|
const openDecisionIds = [];
|
|
14362
15064
|
const openPromptIds = [];
|
|
14363
15065
|
for (const job of jobs) {
|
|
14364
|
-
for (const line of
|
|
15066
|
+
for (const line of asArray2(job.actionSummary)) {
|
|
14365
15067
|
if (typeof line === "string" && line.trim()) {
|
|
14366
15068
|
actionSummaryLines.push(line.trim());
|
|
14367
15069
|
}
|
|
14368
15070
|
}
|
|
14369
|
-
for (const rawEvent of
|
|
14370
|
-
const event =
|
|
14371
|
-
const details =
|
|
14372
|
-
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);
|
|
14373
15075
|
if (typeof details?.name === "string") {
|
|
14374
15076
|
variableNames.push(details.name);
|
|
14375
15077
|
}
|
|
@@ -14396,7 +15098,7 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14396
15098
|
}
|
|
14397
15099
|
}
|
|
14398
15100
|
}
|
|
14399
|
-
const loop =
|
|
15101
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14400
15102
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14401
15103
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
14402
15104
|
const status = typeof task.status === "string" ? task.status : "pending";
|
|
@@ -14444,15 +15146,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14444
15146
|
openPromptIds.push(prompt.id);
|
|
14445
15147
|
}
|
|
14446
15148
|
}
|
|
14447
|
-
const heap =
|
|
14448
|
-
const variablesByName =
|
|
14449
|
-
const listsByName =
|
|
15149
|
+
const heap = asRecord4(liveDoc?.heap);
|
|
15150
|
+
const variablesByName = asRecord4(heap?.variablesByName) || {};
|
|
15151
|
+
const listsByName = asRecord4(heap?.listsByName) || {};
|
|
14450
15152
|
const recentHints = extractFocusHintsFromActionSummary(actionSummaryLines);
|
|
14451
15153
|
variableNames.push(...recentHints.variableNames);
|
|
14452
15154
|
listNames.push(...recentHints.listNames);
|
|
14453
15155
|
entryPaths.push(...recentHints.entryPaths);
|
|
14454
15156
|
for (const variableName of uniqueStrings(variableNames)) {
|
|
14455
|
-
const variable =
|
|
15157
|
+
const variable = asRecord4(variablesByName[variableName]);
|
|
14456
15158
|
if (!variable) continue;
|
|
14457
15159
|
if (typeof variable.listName === "string") {
|
|
14458
15160
|
listNames.push(variable.listName);
|
|
@@ -14462,13 +15164,13 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14462
15164
|
}
|
|
14463
15165
|
}
|
|
14464
15166
|
for (const listName of uniqueStrings(listNames)) {
|
|
14465
|
-
const list =
|
|
14466
|
-
for (const path2 of
|
|
15167
|
+
const list = asRecord4(listsByName[listName]);
|
|
15168
|
+
for (const path2 of asArray2(list?.paths).slice(0, 4)) {
|
|
14467
15169
|
entryPaths.push(path2);
|
|
14468
15170
|
}
|
|
14469
15171
|
}
|
|
14470
15172
|
if (variableNames.length === 0 && boundary.reason !== "request_start") {
|
|
14471
|
-
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);
|
|
14472
15174
|
for (const variable of recentVariables) {
|
|
14473
15175
|
if (typeof variable.name === "string") {
|
|
14474
15176
|
variableNames.push(variable.name);
|
|
@@ -14551,12 +15253,12 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14551
15253
|
}
|
|
14552
15254
|
function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
14553
15255
|
if (pendingPrompts.length > 0) return true;
|
|
14554
|
-
const jobsById =
|
|
15256
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14555
15257
|
for (const job of Object.values(jobsById)) {
|
|
14556
|
-
const prompts =
|
|
15258
|
+
const prompts = asRecord4(asRecord4(job)?.prompts);
|
|
14557
15259
|
if (!prompts) continue;
|
|
14558
15260
|
for (const prompt of Object.values(prompts)) {
|
|
14559
|
-
const record =
|
|
15261
|
+
const record = asRecord4(prompt);
|
|
14560
15262
|
if (record?.status === "open") return true;
|
|
14561
15263
|
}
|
|
14562
15264
|
}
|
|
@@ -14564,7 +15266,7 @@ function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
|
14564
15266
|
}
|
|
14565
15267
|
function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
14566
15268
|
const lines = [];
|
|
14567
|
-
const loop =
|
|
15269
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14568
15270
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
14569
15271
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14570
15272
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
@@ -14624,8 +15326,8 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14624
15326
|
const title = typeof decision.title === "string" && decision.title.trim() ? decision.title.trim() : "Decision";
|
|
14625
15327
|
const decisionId = typeof decision.decisionId === "string" ? decision.decisionId : "unknown";
|
|
14626
15328
|
if (status === "open") {
|
|
14627
|
-
const candidatePreview =
|
|
14628
|
-
const record =
|
|
15329
|
+
const candidatePreview = asArray2(decision.candidates).slice(0, 3).map((candidate) => {
|
|
15330
|
+
const record = asRecord4(candidate);
|
|
14629
15331
|
if (!record) return null;
|
|
14630
15332
|
const candidateId = typeof record.id === "string" ? record.id : "unknown";
|
|
14631
15333
|
const candidateLabel = typeof record.label === "string" && record.label.trim() ? record.label.trim() : candidateId;
|
|
@@ -14635,7 +15337,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14635
15337
|
`- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`
|
|
14636
15338
|
);
|
|
14637
15339
|
} else {
|
|
14638
|
-
const selected =
|
|
15340
|
+
const selected = asRecord4(decision.selected);
|
|
14639
15341
|
const label = typeof selected?.label === "string" ? selected.label : typeof selected?.id === "string" ? selected.id : "unknown";
|
|
14640
15342
|
lines.push(`- [resolved] ${title} (${decisionId}) -> ${label}`);
|
|
14641
15343
|
}
|
|
@@ -14648,12 +15350,12 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14648
15350
|
title: prompt.title,
|
|
14649
15351
|
message: prompt.message
|
|
14650
15352
|
})),
|
|
14651
|
-
...Object.values(
|
|
15353
|
+
...Object.values(asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord4(asRecord4(job)?.prompts) || {})).map((prompt) => asRecord4(prompt)).filter(
|
|
14652
15354
|
(prompt) => Boolean(prompt && prompt.status === "open")
|
|
14653
15355
|
)
|
|
14654
15356
|
];
|
|
14655
15357
|
const visiblePrompts = boundary.reason === "request_start" ? openPrompts.filter((prompt) => {
|
|
14656
|
-
const promptRecord =
|
|
15358
|
+
const promptRecord = asRecord4(prompt);
|
|
14657
15359
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
14658
15360
|
const promptId = typeof promptRecord?.id === "string" ? promptRecord.id : typeof prompt.id === "string" ? prompt.id : null;
|
|
14659
15361
|
return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some(
|
|
@@ -14672,7 +15374,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14672
15374
|
}
|
|
14673
15375
|
}
|
|
14674
15376
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14675
|
-
const closureRecord = currentClosureId ?
|
|
15377
|
+
const closureRecord = currentClosureId ? asRecord4(asRecord4(loop?.closuresById)?.[currentClosureId]) : null;
|
|
14676
15378
|
const visibleClosure = closureRecord && (boundary.reason !== "request_start" || (Number(closureRecord.createdAt) || 0) >= boundary.timestamp) ? closureRecord : null;
|
|
14677
15379
|
lines.push("", "Loop Closure:");
|
|
14678
15380
|
if (visibleClosure) {
|
|
@@ -14685,10 +15387,10 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14685
15387
|
return lines.join("\n");
|
|
14686
15388
|
}
|
|
14687
15389
|
function projectHeapSummary(heap, options) {
|
|
14688
|
-
const heapRecord =
|
|
14689
|
-
const entriesByPath =
|
|
14690
|
-
const listsByName =
|
|
14691
|
-
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) || {};
|
|
14692
15394
|
const focusedVariableNames = new Set(
|
|
14693
15395
|
uniqueStrings(options?.focus?.variableNames || [])
|
|
14694
15396
|
);
|
|
@@ -14703,7 +15405,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14703
15405
|
const maxVariables = options?.maxVariables ?? (hasFocus ? 4 : 6);
|
|
14704
15406
|
const maxLists = options?.maxLists ?? (hasFocus ? 3 : 4);
|
|
14705
15407
|
const maxEntries = options?.maxEntries ?? (hasFocus ? 5 : 6);
|
|
14706
|
-
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) => {
|
|
14707
15409
|
const leftFocused = left.name && focusedVariableNames.has(left.name) ? 1 : 0;
|
|
14708
15410
|
const rightFocused = right.name && focusedVariableNames.has(right.name) ? 1 : 0;
|
|
14709
15411
|
return rightFocused - leftFocused || (right.updatedAt || 0) - (left.updatedAt || 0);
|
|
@@ -14717,7 +15419,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14717
15419
|
for (const variable of variables) {
|
|
14718
15420
|
if (variable.entryPath) referencedPaths.add(variable.entryPath);
|
|
14719
15421
|
if (variable.listName) {
|
|
14720
|
-
const list =
|
|
15422
|
+
const list = asRecord4(
|
|
14721
15423
|
listsByName[variable.listName]
|
|
14722
15424
|
);
|
|
14723
15425
|
for (const path2 of list?.paths || []) referencedPaths.add(path2);
|
|
@@ -14726,10 +15428,10 @@ function projectHeapSummary(heap, options) {
|
|
|
14726
15428
|
for (const path2 of focusedEntryPaths) {
|
|
14727
15429
|
referencedPaths.add(path2);
|
|
14728
15430
|
}
|
|
14729
|
-
const visibleLists = Object.values(listsByName).map((value) =>
|
|
15431
|
+
const visibleLists = Object.values(listsByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter(
|
|
14730
15432
|
(list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))
|
|
14731
15433
|
).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
|
|
14732
|
-
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);
|
|
14733
15435
|
const lines = [];
|
|
14734
15436
|
lines.push("Variables:");
|
|
14735
15437
|
if (variables.length === 0) {
|
|
@@ -14743,7 +15445,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14743
15445
|
continue;
|
|
14744
15446
|
}
|
|
14745
15447
|
if (variable.kind === "entry") {
|
|
14746
|
-
const entry = variable.entryPath ?
|
|
15448
|
+
const entry = variable.entryPath ? asRecord4(
|
|
14747
15449
|
entriesByPath[variable.entryPath]
|
|
14748
15450
|
) : null;
|
|
14749
15451
|
lines.push(
|
|
@@ -14751,7 +15453,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14751
15453
|
);
|
|
14752
15454
|
continue;
|
|
14753
15455
|
}
|
|
14754
|
-
const list = variable.listName ?
|
|
15456
|
+
const list = variable.listName ? asRecord4(listsByName[variable.listName]) : null;
|
|
14755
15457
|
lines.push(
|
|
14756
15458
|
`- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`
|
|
14757
15459
|
);
|
|
@@ -14784,7 +15486,7 @@ function createHarnessVerifierSnapshot(input) {
|
|
|
14784
15486
|
input.projectionOptions
|
|
14785
15487
|
);
|
|
14786
15488
|
const heapDigest = hashString(
|
|
14787
|
-
projectHeapSummary(
|
|
15489
|
+
projectHeapSummary(asRecord4(input.liveDoc?.heap), {
|
|
14788
15490
|
focus: workflowFocus
|
|
14789
15491
|
})
|
|
14790
15492
|
) || "00000000";
|
|
@@ -15110,246 +15812,12 @@ ${loopBlock}
|
|
|
15110
15812
|
- Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
|
|
15111
15813
|
}
|
|
15112
15814
|
|
|
15113
|
-
// src/job-presentation.ts
|
|
15114
|
-
var RESPONSE_KEYS = [
|
|
15115
|
-
"reply",
|
|
15116
|
-
"response",
|
|
15117
|
-
"text",
|
|
15118
|
-
"message",
|
|
15119
|
-
"summary",
|
|
15120
|
-
"answer"
|
|
15121
|
-
];
|
|
15122
|
-
var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
|
|
15123
|
-
var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
|
|
15124
|
-
var LIST_KEY_CANDIDATES = ["listName"];
|
|
15125
|
-
var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
|
|
15126
|
-
var VARIABLE_KEY_CANDIDATES = ["variableName"];
|
|
15127
|
-
var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
|
|
15128
|
-
var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
|
|
15129
|
-
function asRecord3(value) {
|
|
15130
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15131
|
-
return value;
|
|
15132
|
-
}
|
|
15133
|
-
function normalizeText(value) {
|
|
15134
|
-
if (typeof value !== "string") return null;
|
|
15135
|
-
const trimmed = value.trim();
|
|
15136
|
-
if (!trimmed) return null;
|
|
15137
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
15138
|
-
return null;
|
|
15139
|
-
}
|
|
15140
|
-
return trimmed;
|
|
15141
|
-
}
|
|
15142
|
-
function humanTextFromStdout(stdout) {
|
|
15143
|
-
for (const line of [...stdout].reverse()) {
|
|
15144
|
-
const normalized = normalizeText(line);
|
|
15145
|
-
if (!normalized) continue;
|
|
15146
|
-
if (/^[A-Z_]+:/.test(normalized)) continue;
|
|
15147
|
-
return normalized;
|
|
15148
|
-
}
|
|
15149
|
-
return null;
|
|
15150
|
-
}
|
|
15151
|
-
function pushString(target, value) {
|
|
15152
|
-
if (typeof value === "string" && value.trim()) {
|
|
15153
|
-
target.add(value.trim());
|
|
15154
|
-
}
|
|
15155
|
-
}
|
|
15156
|
-
function pushStringArray(target, value) {
|
|
15157
|
-
if (!Array.isArray(value)) return;
|
|
15158
|
-
for (const item of value) {
|
|
15159
|
-
pushString(target, item);
|
|
15160
|
-
}
|
|
15161
|
-
}
|
|
15162
|
-
function collectReferencesFromRecord(record, refs) {
|
|
15163
|
-
for (const key of ENTRY_KEY_CANDIDATES)
|
|
15164
|
-
pushString(refs.entryPaths, record[key]);
|
|
15165
|
-
for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
|
|
15166
|
-
pushStringArray(refs.entryPaths, record[key]);
|
|
15167
|
-
for (const key of LIST_KEY_CANDIDATES)
|
|
15168
|
-
pushString(refs.listNames, record[key]);
|
|
15169
|
-
for (const key of LIST_ARRAY_KEY_CANDIDATES)
|
|
15170
|
-
pushStringArray(refs.listNames, record[key]);
|
|
15171
|
-
for (const key of VARIABLE_KEY_CANDIDATES)
|
|
15172
|
-
pushString(refs.variableNames, record[key]);
|
|
15173
|
-
for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
|
|
15174
|
-
pushStringArray(refs.variableNames, record[key]);
|
|
15175
|
-
}
|
|
15176
|
-
function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
15177
|
-
if (value === null || value === void 0 || depth > 4 || seen.has(value))
|
|
15178
|
-
return;
|
|
15179
|
-
if (typeof value === "string") {
|
|
15180
|
-
const trimmed = value.trim();
|
|
15181
|
-
if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
|
|
15182
|
-
if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
|
|
15183
|
-
if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
|
|
15184
|
-
return;
|
|
15185
|
-
}
|
|
15186
|
-
if (Array.isArray(value)) {
|
|
15187
|
-
seen.add(value);
|
|
15188
|
-
for (const item of value.slice(0, 24)) {
|
|
15189
|
-
scanForHeapReferences(item, heap, refs, depth + 1, seen);
|
|
15190
|
-
}
|
|
15191
|
-
return;
|
|
15192
|
-
}
|
|
15193
|
-
const record = asRecord3(value);
|
|
15194
|
-
if (!record) return;
|
|
15195
|
-
seen.add(value);
|
|
15196
|
-
collectReferencesFromRecord(record, refs);
|
|
15197
|
-
for (const key of UI_CONTAINER_KEYS) {
|
|
15198
|
-
const nested = asRecord3(record[key]);
|
|
15199
|
-
if (nested) collectReferencesFromRecord(nested, refs);
|
|
15200
|
-
}
|
|
15201
|
-
for (const nested of Object.values(record).slice(0, 24)) {
|
|
15202
|
-
scanForHeapReferences(nested, heap, refs, depth + 1, seen);
|
|
15203
|
-
}
|
|
15204
|
-
}
|
|
15205
|
-
function resolveVariablesToReferences(variableNames, heap, refs) {
|
|
15206
|
-
for (const variableName of variableNames) {
|
|
15207
|
-
const variable = heap.variablesByName?.[variableName];
|
|
15208
|
-
if (!variable) continue;
|
|
15209
|
-
if (variable.kind === "entry" && variable.entryPath) {
|
|
15210
|
-
refs.entryPaths.add(variable.entryPath);
|
|
15211
|
-
}
|
|
15212
|
-
if (variable.kind === "list" && variable.listName) {
|
|
15213
|
-
refs.listNames.add(variable.listName);
|
|
15214
|
-
}
|
|
15215
|
-
}
|
|
15216
|
-
}
|
|
15217
|
-
function sortEntries(entries) {
|
|
15218
|
-
return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
15219
|
-
}
|
|
15220
|
-
function sortLists(lists) {
|
|
15221
|
-
return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
15222
|
-
}
|
|
15223
|
-
function dedupeEntries(entries) {
|
|
15224
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15225
|
-
const result = [];
|
|
15226
|
-
for (const entry of entries) {
|
|
15227
|
-
if (!entry?.path || seen.has(entry.path)) continue;
|
|
15228
|
-
seen.add(entry.path);
|
|
15229
|
-
result.push(entry);
|
|
15230
|
-
}
|
|
15231
|
-
return result;
|
|
15232
|
-
}
|
|
15233
|
-
function dedupeLists(lists) {
|
|
15234
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15235
|
-
const result = [];
|
|
15236
|
-
for (const list of lists) {
|
|
15237
|
-
if (!list?.name || seen.has(list.name)) continue;
|
|
15238
|
-
seen.add(list.name);
|
|
15239
|
-
result.push(list);
|
|
15240
|
-
}
|
|
15241
|
-
return result;
|
|
15242
|
-
}
|
|
15243
|
-
function extractResponseText(result, stdout) {
|
|
15244
|
-
const directText = normalizeText(result);
|
|
15245
|
-
if (directText) return directText;
|
|
15246
|
-
const record = asRecord3(result);
|
|
15247
|
-
if (record) {
|
|
15248
|
-
for (const key of RESPONSE_KEYS) {
|
|
15249
|
-
const normalized = normalizeText(record[key]);
|
|
15250
|
-
if (normalized) return normalized;
|
|
15251
|
-
}
|
|
15252
|
-
for (const containerKey of UI_CONTAINER_KEYS) {
|
|
15253
|
-
const nested = asRecord3(record[containerKey]);
|
|
15254
|
-
if (!nested) continue;
|
|
15255
|
-
for (const key of RESPONSE_KEYS) {
|
|
15256
|
-
const normalized = normalizeText(nested[key]);
|
|
15257
|
-
if (normalized) return normalized;
|
|
15258
|
-
}
|
|
15259
|
-
}
|
|
15260
|
-
}
|
|
15261
|
-
return humanTextFromStdout(stdout);
|
|
15262
|
-
}
|
|
15263
|
-
function fallbackResponseText(entries, lists) {
|
|
15264
|
-
if (entries.length > 0) {
|
|
15265
|
-
return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
|
|
15266
|
-
}
|
|
15267
|
-
if (lists.length > 0) {
|
|
15268
|
-
const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
|
|
15269
|
-
if (emptyOnly) {
|
|
15270
|
-
return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
|
|
15271
|
-
}
|
|
15272
|
-
return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
|
|
15273
|
-
}
|
|
15274
|
-
return null;
|
|
15275
|
-
}
|
|
15276
|
-
function getJobRelatedEntries(heap, jobId) {
|
|
15277
|
-
return sortEntries(
|
|
15278
|
-
Object.values(heap.entriesByPath || {}).filter(
|
|
15279
|
-
(entry) => entry.relatedJobIds?.includes(jobId)
|
|
15280
|
-
)
|
|
15281
|
-
);
|
|
15282
|
-
}
|
|
15283
|
-
function getJobRelatedLists(heap, jobId) {
|
|
15284
|
-
return sortLists(
|
|
15285
|
-
Object.values(heap.listsByName || {}).filter(
|
|
15286
|
-
(list) => list.relatedJobIds?.includes(jobId)
|
|
15287
|
-
)
|
|
15288
|
-
);
|
|
15289
|
-
}
|
|
15290
|
-
function entriesFromLists(lists, heap) {
|
|
15291
|
-
const entries = [];
|
|
15292
|
-
for (const list of lists) {
|
|
15293
|
-
for (const path2 of list.paths || []) {
|
|
15294
|
-
const entry = heap.entriesByPath?.[path2];
|
|
15295
|
-
if (entry) entries.push(entry);
|
|
15296
|
-
}
|
|
15297
|
-
}
|
|
15298
|
-
return entries;
|
|
15299
|
-
}
|
|
15300
|
-
function resolveJobPresentation({
|
|
15301
|
-
jobId,
|
|
15302
|
-
result,
|
|
15303
|
-
stdout = [],
|
|
15304
|
-
sessionHeap,
|
|
15305
|
-
allowExplicitArtifacts = true
|
|
15306
|
-
}) {
|
|
15307
|
-
const refs = {
|
|
15308
|
-
entryPaths: /* @__PURE__ */ new Set(),
|
|
15309
|
-
listNames: /* @__PURE__ */ new Set(),
|
|
15310
|
-
variableNames: /* @__PURE__ */ new Set()
|
|
15311
|
-
};
|
|
15312
|
-
if (allowExplicitArtifacts) {
|
|
15313
|
-
scanForHeapReferences(result, sessionHeap, refs);
|
|
15314
|
-
resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
|
|
15315
|
-
}
|
|
15316
|
-
const referencedLists = sortLists(
|
|
15317
|
-
[...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
|
|
15318
|
-
);
|
|
15319
|
-
const referencedEntries = sortEntries(
|
|
15320
|
-
[...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
|
|
15321
|
-
);
|
|
15322
|
-
const jobLists = getJobRelatedLists(sessionHeap, jobId);
|
|
15323
|
-
const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
|
|
15324
|
-
const changedEntries = dedupeEntries([
|
|
15325
|
-
...jobEntries,
|
|
15326
|
-
...entriesFromLists(jobLists, sessionHeap)
|
|
15327
|
-
]);
|
|
15328
|
-
const explicitLists = dedupeLists(referencedLists);
|
|
15329
|
-
const explicitEntries = dedupeEntries([
|
|
15330
|
-
...referencedEntries,
|
|
15331
|
-
...entriesFromLists(referencedLists, sessionHeap)
|
|
15332
|
-
]);
|
|
15333
|
-
const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
|
|
15334
|
-
const lists = hasExplicitArtifacts ? explicitLists : jobLists;
|
|
15335
|
-
const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
|
|
15336
|
-
const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
|
|
15337
|
-
return {
|
|
15338
|
-
responseText,
|
|
15339
|
-
entries,
|
|
15340
|
-
lists,
|
|
15341
|
-
changedEntries,
|
|
15342
|
-
changedLists: jobLists,
|
|
15343
|
-
hasExplicitArtifacts
|
|
15344
|
-
};
|
|
15345
|
-
}
|
|
15346
|
-
|
|
15347
15815
|
// src/agent-evals.ts
|
|
15348
15816
|
var DEFAULT_CONTROLLER_BUDGETS = {
|
|
15349
15817
|
maxIterations: 6,
|
|
15350
15818
|
maxNoProgressIterations: 2
|
|
15351
15819
|
};
|
|
15352
|
-
function
|
|
15820
|
+
function asRecord5(value) {
|
|
15353
15821
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15354
15822
|
return value;
|
|
15355
15823
|
}
|
|
@@ -15397,7 +15865,7 @@ function buildArtifactDir(baseDir, suiteName = "granular-agent-evals") {
|
|
|
15397
15865
|
function createTimestampedArtifactDirectory(options) {
|
|
15398
15866
|
return buildArtifactDir(options?.baseDir, options?.suiteName);
|
|
15399
15867
|
}
|
|
15400
|
-
function
|
|
15868
|
+
function asArray3(value) {
|
|
15401
15869
|
if (!value) return [];
|
|
15402
15870
|
return Array.isArray(value) ? value : [value];
|
|
15403
15871
|
}
|
|
@@ -15415,7 +15883,7 @@ function buildScenarioSteps(scenario) {
|
|
|
15415
15883
|
request: scenario.request,
|
|
15416
15884
|
human: scenario.human,
|
|
15417
15885
|
expect: scenario.expect,
|
|
15418
|
-
inspect: [...
|
|
15886
|
+
inspect: [...asArray3(scenario.inspect), ...asArray3(scenario.verify)],
|
|
15419
15887
|
check: scenario.check,
|
|
15420
15888
|
maxIterations: scenario.maxIterations,
|
|
15421
15889
|
setup: {
|
|
@@ -15456,12 +15924,12 @@ function buildHistory(entries) {
|
|
|
15456
15924
|
);
|
|
15457
15925
|
}
|
|
15458
15926
|
function getOpenPromptsFromDoc(liveDoc) {
|
|
15459
|
-
const jobsById =
|
|
15927
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15460
15928
|
const prompts = [];
|
|
15461
15929
|
for (const job of Object.values(jobsById)) {
|
|
15462
|
-
const promptRecords =
|
|
15930
|
+
const promptRecords = asRecord5(asRecord5(job)?.prompts) || {};
|
|
15463
15931
|
for (const raw of Object.values(promptRecords)) {
|
|
15464
|
-
const record =
|
|
15932
|
+
const record = asRecord5(raw);
|
|
15465
15933
|
if (!record || record.status !== "open" || typeof record.promptId !== "string")
|
|
15466
15934
|
continue;
|
|
15467
15935
|
const prompt = normalizePrompt({
|
|
@@ -15482,11 +15950,11 @@ function getOpenPromptsFromDoc(liveDoc) {
|
|
|
15482
15950
|
return prompts;
|
|
15483
15951
|
}
|
|
15484
15952
|
function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
|
|
15485
|
-
const jobsById =
|
|
15953
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15486
15954
|
return prompts.filter((prompt) => {
|
|
15487
15955
|
for (const jobRecord of Object.values(jobsById)) {
|
|
15488
|
-
const promptsById =
|
|
15489
|
-
const promptRecord =
|
|
15956
|
+
const promptsById = asRecord5(asRecord5(jobRecord)?.prompts) || {};
|
|
15957
|
+
const promptRecord = asRecord5(promptsById[prompt.id]);
|
|
15490
15958
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
15491
15959
|
if (openedAt >= boundaryTimestamp) return true;
|
|
15492
15960
|
}
|
|
@@ -15579,10 +16047,10 @@ ${modelOutputInstruction()}`
|
|
|
15579
16047
|
);
|
|
15580
16048
|
}
|
|
15581
16049
|
const raw = await response.json();
|
|
15582
|
-
const content =
|
|
15583
|
-
|
|
16050
|
+
const content = asRecord5(
|
|
16051
|
+
asRecord5(raw.choices?.[0])?.message
|
|
15584
16052
|
)?.content;
|
|
15585
|
-
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("") : "";
|
|
15586
16054
|
const parsed = extractJsonObject(text);
|
|
15587
16055
|
if (!parsed) {
|
|
15588
16056
|
if (attempt < 3) {
|
|
@@ -15634,17 +16102,17 @@ async function withTimeout(promise, ms, label) {
|
|
|
15634
16102
|
}
|
|
15635
16103
|
}
|
|
15636
16104
|
function getActionSummary(liveDoc, jobId) {
|
|
15637
|
-
const jobsById =
|
|
15638
|
-
const job =
|
|
16105
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
16106
|
+
const job = asRecord5(jobsById[jobId]);
|
|
15639
16107
|
return Array.isArray(job?.actionSummary) ? job.actionSummary.filter(
|
|
15640
16108
|
(line) => typeof line === "string"
|
|
15641
16109
|
) : [];
|
|
15642
16110
|
}
|
|
15643
16111
|
function normalizeHeapSnapshot2(heap) {
|
|
15644
16112
|
return {
|
|
15645
|
-
entriesByPath:
|
|
15646
|
-
listsByName:
|
|
15647
|
-
variablesByName:
|
|
16113
|
+
entriesByPath: asRecord5(heap?.entriesByPath) || {},
|
|
16114
|
+
listsByName: asRecord5(heap?.listsByName) || {},
|
|
16115
|
+
variablesByName: asRecord5(heap?.variablesByName) || {},
|
|
15648
16116
|
updatedAt: typeof heap?.updatedAt === "number" ? heap.updatedAt : Date.now()
|
|
15649
16117
|
};
|
|
15650
16118
|
}
|
|
@@ -15830,8 +16298,8 @@ async function runAgentEvalSuite(options) {
|
|
|
15830
16298
|
);
|
|
15831
16299
|
}
|
|
15832
16300
|
const inspectionResults = [];
|
|
15833
|
-
const stepChecks =
|
|
15834
|
-
const stepInspections =
|
|
16301
|
+
const stepChecks = asArray3(step.check);
|
|
16302
|
+
const stepInspections = asArray3(step.inspect);
|
|
15835
16303
|
const context = {
|
|
15836
16304
|
conversation,
|
|
15837
16305
|
environment: conversation.environment,
|
|
@@ -15844,7 +16312,7 @@ async function runAgentEvalSuite(options) {
|
|
|
15844
16312
|
promptInteractions: completed.promptInteractions,
|
|
15845
16313
|
result: completed.result,
|
|
15846
16314
|
heap: normalizeHeapSnapshot2(
|
|
15847
|
-
|
|
16315
|
+
asRecord5(
|
|
15848
16316
|
cloneJson(conversation.environment.document)?.heap
|
|
15849
16317
|
)
|
|
15850
16318
|
),
|
|
@@ -16055,7 +16523,7 @@ function createAgentEvalHarness(options) {
|
|
|
16055
16523
|
}
|
|
16056
16524
|
function buildCheckContext(conversation, completed, turnDir) {
|
|
16057
16525
|
const liveDoc = cloneJson(conversation.environment.document);
|
|
16058
|
-
const heap = normalizeHeapSnapshot2(
|
|
16526
|
+
const heap = normalizeHeapSnapshot2(asRecord5(liveDoc?.heap));
|
|
16059
16527
|
return {
|
|
16060
16528
|
conversation,
|
|
16061
16529
|
environment: conversation.environment,
|
|
@@ -16131,7 +16599,7 @@ function createAgentEvalHarness(options) {
|
|
|
16131
16599
|
jobId: pending.job.id,
|
|
16132
16600
|
result: resumed.result,
|
|
16133
16601
|
stdout: [...pending.stdout, ...resumed.stdout],
|
|
16134
|
-
sessionHeap: normalizeHeapSnapshot2(
|
|
16602
|
+
sessionHeap: normalizeHeapSnapshot2(asRecord5(liveDoc?.heap))
|
|
16135
16603
|
});
|
|
16136
16604
|
const responseText = presentation.responseText || pending.finalReply || "Done.";
|
|
16137
16605
|
pending.conversation.history.push({
|
|
@@ -16341,7 +16809,7 @@ function createAgentEvalHarness(options) {
|
|
|
16341
16809
|
const settledLiveDoc = cloneJson(
|
|
16342
16810
|
conversation.environment.document
|
|
16343
16811
|
);
|
|
16344
|
-
const sessionHeap = normalizeHeapSnapshot2(
|
|
16812
|
+
const sessionHeap = normalizeHeapSnapshot2(asRecord5(settledLiveDoc?.heap));
|
|
16345
16813
|
const presentation = resolveJobPresentation({
|
|
16346
16814
|
jobId: job.id,
|
|
16347
16815
|
result: outcome.result,
|