@granular-software/sdk 0.4.32 → 0.4.34
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 +859 -316
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +859 -316
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/cli/index.js +828 -30
- package/dist/{client-C2Gk641P.d.mts → client-iw76FL_8.d.mts} +153 -34
- package/dist/{client-C2Gk641P.d.ts → client-iw76FL_8.d.ts} +153 -34
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +9363 -9120
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9363 -9120
- 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";
|
|
@@ -10357,6 +10891,50 @@ function computeEffectKey(effect) {
|
|
|
10357
10891
|
}
|
|
10358
10892
|
return effect.static ? `class:${attachedClass}:static:${effect.name}` : `class:${attachedClass}:instance:${effect.name}`;
|
|
10359
10893
|
}
|
|
10894
|
+
function computeEffectVersionSelectorSpecificity(selector) {
|
|
10895
|
+
if (!selector || selector.mode === "all") {
|
|
10896
|
+
return 0;
|
|
10897
|
+
}
|
|
10898
|
+
if (selector.mode === "exact") {
|
|
10899
|
+
return 2;
|
|
10900
|
+
}
|
|
10901
|
+
return 1;
|
|
10902
|
+
}
|
|
10903
|
+
function matchesEffectVersionSelector(selector, buildVersionNumber) {
|
|
10904
|
+
if (!selector || selector.mode === "all") {
|
|
10905
|
+
return true;
|
|
10906
|
+
}
|
|
10907
|
+
if (typeof buildVersionNumber !== "number" || !Number.isFinite(buildVersionNumber)) {
|
|
10908
|
+
return false;
|
|
10909
|
+
}
|
|
10910
|
+
if (selector.mode === "exact") {
|
|
10911
|
+
return buildVersionNumber === selector.versionNumber;
|
|
10912
|
+
}
|
|
10913
|
+
if (selector.mode === "before") {
|
|
10914
|
+
return buildVersionNumber < selector.versionNumber;
|
|
10915
|
+
}
|
|
10916
|
+
return buildVersionNumber > selector.versionNumber;
|
|
10917
|
+
}
|
|
10918
|
+
function selectRegisteredEffect(effectMap, effectKey, buildVersionNumber) {
|
|
10919
|
+
let bestEffect;
|
|
10920
|
+
let bestSpecificity = Number.NEGATIVE_INFINITY;
|
|
10921
|
+
for (const effect of effectMap.values()) {
|
|
10922
|
+
if (computeEffectKey(effect) !== effectKey) {
|
|
10923
|
+
continue;
|
|
10924
|
+
}
|
|
10925
|
+
if (!matchesEffectVersionSelector(effect.versionSelector, buildVersionNumber)) {
|
|
10926
|
+
continue;
|
|
10927
|
+
}
|
|
10928
|
+
const specificity = computeEffectVersionSelectorSpecificity(
|
|
10929
|
+
effect.versionSelector
|
|
10930
|
+
);
|
|
10931
|
+
if (!bestEffect || specificity > bestSpecificity) {
|
|
10932
|
+
bestEffect = effect;
|
|
10933
|
+
bestSpecificity = specificity;
|
|
10934
|
+
}
|
|
10935
|
+
}
|
|
10936
|
+
return bestEffect;
|
|
10937
|
+
}
|
|
10360
10938
|
function normalizeEffectBehaviors(value) {
|
|
10361
10939
|
return normalizeEffectBehaviorSummary(
|
|
10362
10940
|
value
|
|
@@ -10377,9 +10955,17 @@ function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
|
10377
10955
|
return void 0;
|
|
10378
10956
|
}
|
|
10379
10957
|
if (reverseHandler.includes(":")) {
|
|
10380
|
-
return
|
|
10958
|
+
return selectRegisteredEffect(
|
|
10959
|
+
effectMap,
|
|
10960
|
+
reverseHandler,
|
|
10961
|
+
request.context?.buildVersionNumber
|
|
10962
|
+
);
|
|
10381
10963
|
}
|
|
10382
|
-
const directMatch =
|
|
10964
|
+
const directMatch = selectRegisteredEffect(
|
|
10965
|
+
effectMap,
|
|
10966
|
+
reverseHandler,
|
|
10967
|
+
request.context?.buildVersionNumber
|
|
10968
|
+
);
|
|
10383
10969
|
if (directMatch) {
|
|
10384
10970
|
return directMatch;
|
|
10385
10971
|
}
|
|
@@ -10394,7 +10980,11 @@ function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
|
10394
10980
|
})
|
|
10395
10981
|
];
|
|
10396
10982
|
for (const candidateKey of candidateKeys) {
|
|
10397
|
-
const candidate =
|
|
10983
|
+
const candidate = selectRegisteredEffect(
|
|
10984
|
+
effectMap,
|
|
10985
|
+
candidateKey,
|
|
10986
|
+
request.context?.buildVersionNumber
|
|
10987
|
+
);
|
|
10398
10988
|
if (candidate) {
|
|
10399
10989
|
return candidate;
|
|
10400
10990
|
}
|
|
@@ -10430,7 +11020,11 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
10430
11020
|
return { effect, mode, handler: effect.handler };
|
|
10431
11021
|
}
|
|
10432
11022
|
async function invokeRegisteredEffect(effectMap, request) {
|
|
10433
|
-
const effect =
|
|
11023
|
+
const effect = selectRegisteredEffect(
|
|
11024
|
+
effectMap,
|
|
11025
|
+
request.effectKey,
|
|
11026
|
+
request.context?.buildVersionNumber
|
|
11027
|
+
);
|
|
10434
11028
|
if (!effect) {
|
|
10435
11029
|
throw new Error(`Effect handler not found: ${request.effectKey}`);
|
|
10436
11030
|
}
|
|
@@ -11689,6 +12283,17 @@ function computeEffectKey2(effect) {
|
|
|
11689
12283
|
}
|
|
11690
12284
|
return effect.static ? `class:${attachedClass}:static:${effect.name}` : `class:${attachedClass}:instance:${effect.name}`;
|
|
11691
12285
|
}
|
|
12286
|
+
function computeEffectVersionSelectorKey(selector) {
|
|
12287
|
+
if (!selector || selector.mode === "all") {
|
|
12288
|
+
return "all";
|
|
12289
|
+
}
|
|
12290
|
+
return `${selector.mode}:${selector.versionNumber}`;
|
|
12291
|
+
}
|
|
12292
|
+
function computeEffectRegistrationKey(effect) {
|
|
12293
|
+
return `${computeEffectKey2(effect)}@${computeEffectVersionSelectorKey(
|
|
12294
|
+
effect.versionSelector
|
|
12295
|
+
)}`;
|
|
12296
|
+
}
|
|
11692
12297
|
function buildEffectHostUrl(apiUrl, sandboxId, effectClientId, clientId) {
|
|
11693
12298
|
const url = new URL(apiUrl);
|
|
11694
12299
|
if (url.pathname.endsWith("/granular/ws/connect")) {
|
|
@@ -12183,7 +12788,9 @@ var Environment = class {
|
|
|
12183
12788
|
{ target: targetPath }
|
|
12184
12789
|
);
|
|
12185
12790
|
if (result.errors?.length) {
|
|
12186
|
-
throw new Error(
|
|
12791
|
+
throw new Error(
|
|
12792
|
+
`attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12793
|
+
);
|
|
12187
12794
|
}
|
|
12188
12795
|
}
|
|
12189
12796
|
/**
|
|
@@ -12218,7 +12825,9 @@ var Environment = class {
|
|
|
12218
12825
|
}`
|
|
12219
12826
|
);
|
|
12220
12827
|
if (result.errors?.length) {
|
|
12221
|
-
throw new Error(
|
|
12828
|
+
throw new Error(
|
|
12829
|
+
`detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12830
|
+
);
|
|
12222
12831
|
}
|
|
12223
12832
|
}
|
|
12224
12833
|
/**
|
|
@@ -12340,7 +12949,9 @@ var Environment = class {
|
|
|
12340
12949
|
async _runGraphql(query, label) {
|
|
12341
12950
|
const result = await this.graphql(query);
|
|
12342
12951
|
if (result.errors?.length) {
|
|
12343
|
-
throw new Error(
|
|
12952
|
+
throw new Error(
|
|
12953
|
+
`${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12954
|
+
);
|
|
12344
12955
|
}
|
|
12345
12956
|
return result.data;
|
|
12346
12957
|
}
|
|
@@ -12875,6 +13486,9 @@ var EnvironmentSession = class extends Session {
|
|
|
12875
13486
|
get envName() {
|
|
12876
13487
|
return this.environment.envName;
|
|
12877
13488
|
}
|
|
13489
|
+
get tag() {
|
|
13490
|
+
return this.environment.tag;
|
|
13491
|
+
}
|
|
12878
13492
|
get versionId() {
|
|
12879
13493
|
return this.environment.versionId;
|
|
12880
13494
|
}
|
|
@@ -12894,12 +13508,171 @@ var EnvironmentSession = class extends Session {
|
|
|
12894
13508
|
return this.environment.feedback;
|
|
12895
13509
|
}
|
|
12896
13510
|
/**
|
|
12897
|
-
* Return a plain JS
|
|
13511
|
+
* Return a plain JS copy of the synced session heap.
|
|
12898
13512
|
*/
|
|
12899
13513
|
getHeap() {
|
|
12900
13514
|
const doc = this.document;
|
|
12901
13515
|
return normalizeHeapSnapshot(doc?.heap);
|
|
12902
13516
|
}
|
|
13517
|
+
async sessionDataRequest(path2, query) {
|
|
13518
|
+
const searchParams = new URLSearchParams();
|
|
13519
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
13520
|
+
if (value !== null && typeof value !== "undefined" && value !== "") {
|
|
13521
|
+
searchParams.set(key, String(value));
|
|
13522
|
+
}
|
|
13523
|
+
}
|
|
13524
|
+
const queryString = searchParams.toString();
|
|
13525
|
+
const response = await fetch(
|
|
13526
|
+
`${this.environment.runtimeBaseUrl}/orchestrator/ws/sessions/${encodeURIComponent(this.sessionId)}${path2}${queryString ? `?${queryString}` : ""}`,
|
|
13527
|
+
{
|
|
13528
|
+
method: "GET",
|
|
13529
|
+
headers: {
|
|
13530
|
+
Authorization: `Bearer ${this.environment.authToken}`,
|
|
13531
|
+
"Content-Type": "application/json"
|
|
13532
|
+
}
|
|
13533
|
+
}
|
|
13534
|
+
);
|
|
13535
|
+
if (!response.ok) {
|
|
13536
|
+
const errorText = await response.text();
|
|
13537
|
+
throw new Error(
|
|
13538
|
+
`Session data API Error (${response.status}): ${errorText}`
|
|
13539
|
+
);
|
|
13540
|
+
}
|
|
13541
|
+
return response.json();
|
|
13542
|
+
}
|
|
13543
|
+
async collectAllSessionItems(listPage) {
|
|
13544
|
+
const items = [];
|
|
13545
|
+
let cursor = null;
|
|
13546
|
+
do {
|
|
13547
|
+
const page = await listPage({ limit: 500, cursor });
|
|
13548
|
+
items.push(...page.items);
|
|
13549
|
+
cursor = page.nextCursor;
|
|
13550
|
+
} while (cursor);
|
|
13551
|
+
return items;
|
|
13552
|
+
}
|
|
13553
|
+
/**
|
|
13554
|
+
* Fetch the live session document from the runtime DO.
|
|
13555
|
+
*
|
|
13556
|
+
* For history and saved artifacts, prefer the collection APIs on
|
|
13557
|
+
* `messages`, `timeline`, `jobs`, and `heap`.
|
|
13558
|
+
*/
|
|
13559
|
+
async getDocument() {
|
|
13560
|
+
return this.sessionDataRequest("/document");
|
|
13561
|
+
}
|
|
13562
|
+
get messages() {
|
|
13563
|
+
return {
|
|
13564
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13565
|
+
"/messages",
|
|
13566
|
+
options
|
|
13567
|
+
)
|
|
13568
|
+
};
|
|
13569
|
+
}
|
|
13570
|
+
get timeline() {
|
|
13571
|
+
return {
|
|
13572
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13573
|
+
"/timeline",
|
|
13574
|
+
options
|
|
13575
|
+
)
|
|
13576
|
+
};
|
|
13577
|
+
}
|
|
13578
|
+
get jobs() {
|
|
13579
|
+
return {
|
|
13580
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13581
|
+
"/jobs",
|
|
13582
|
+
options
|
|
13583
|
+
),
|
|
13584
|
+
get: (jobId) => this.sessionDataRequest(
|
|
13585
|
+
`/jobs/${encodeURIComponent(jobId)}`
|
|
13586
|
+
)
|
|
13587
|
+
};
|
|
13588
|
+
}
|
|
13589
|
+
get heap() {
|
|
13590
|
+
return {
|
|
13591
|
+
entries: {
|
|
13592
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13593
|
+
"/heap/entries",
|
|
13594
|
+
options
|
|
13595
|
+
),
|
|
13596
|
+
get: (path2) => this.sessionDataRequest(
|
|
13597
|
+
`/heap/entries/${encodeURIComponent(path2)}`
|
|
13598
|
+
)
|
|
13599
|
+
},
|
|
13600
|
+
lists: {
|
|
13601
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13602
|
+
"/heap/lists",
|
|
13603
|
+
options
|
|
13604
|
+
),
|
|
13605
|
+
get: (name) => this.sessionDataRequest(
|
|
13606
|
+
`/heap/lists/${encodeURIComponent(name)}`
|
|
13607
|
+
)
|
|
13608
|
+
}
|
|
13609
|
+
};
|
|
13610
|
+
}
|
|
13611
|
+
get transcript() {
|
|
13612
|
+
return {
|
|
13613
|
+
list: async (options = {}) => {
|
|
13614
|
+
const [messages, jobs, entries, lists] = await Promise.all([
|
|
13615
|
+
this.collectAllSessionItems(this.messages.list),
|
|
13616
|
+
this.collectAllSessionItems(
|
|
13617
|
+
(pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
|
|
13618
|
+
),
|
|
13619
|
+
this.collectAllSessionItems(this.heap.entries.list),
|
|
13620
|
+
this.collectAllSessionItems(this.heap.lists.list)
|
|
13621
|
+
]);
|
|
13622
|
+
const liveDoc = {
|
|
13623
|
+
conversation: { messages },
|
|
13624
|
+
jobs: {
|
|
13625
|
+
byId: Object.fromEntries(
|
|
13626
|
+
jobs.map((job) => {
|
|
13627
|
+
const record = job && typeof job === "object" ? job : null;
|
|
13628
|
+
const id = typeof record?.jobId === "string" ? record.jobId : typeof record?.id === "string" ? record.id : null;
|
|
13629
|
+
return id ? [id, record] : null;
|
|
13630
|
+
}).filter(
|
|
13631
|
+
(entry) => Boolean(entry)
|
|
13632
|
+
)
|
|
13633
|
+
)
|
|
13634
|
+
}
|
|
13635
|
+
};
|
|
13636
|
+
const heap = normalizeHeapSnapshot({
|
|
13637
|
+
entriesByPath: Object.fromEntries(
|
|
13638
|
+
entries.map((entry) => {
|
|
13639
|
+
return entry?.path ? [
|
|
13640
|
+
entry.path,
|
|
13641
|
+
entry
|
|
13642
|
+
] : null;
|
|
13643
|
+
}).filter(
|
|
13644
|
+
(entry) => Boolean(entry)
|
|
13645
|
+
)
|
|
13646
|
+
),
|
|
13647
|
+
listsByName: Object.fromEntries(
|
|
13648
|
+
lists.map((list) => {
|
|
13649
|
+
return list?.name ? [list.name, list] : null;
|
|
13650
|
+
}).filter(
|
|
13651
|
+
(entry) => Boolean(entry)
|
|
13652
|
+
)
|
|
13653
|
+
),
|
|
13654
|
+
variablesByName: this.getHeap().variablesByName,
|
|
13655
|
+
updatedAt: Date.now()
|
|
13656
|
+
});
|
|
13657
|
+
const allItems = buildSessionTranscript({
|
|
13658
|
+
liveDoc,
|
|
13659
|
+
sessionHeap: heap
|
|
13660
|
+
});
|
|
13661
|
+
const limit = Math.max(
|
|
13662
|
+
1,
|
|
13663
|
+
Math.min(500, Math.floor(options.limit ?? 100))
|
|
13664
|
+
);
|
|
13665
|
+
const offset = typeof options.cursor === "string" ? Number.parseInt(options.cursor, 10) || 0 : 0;
|
|
13666
|
+
const items = allItems.slice(offset, offset + limit);
|
|
13667
|
+
const nextOffset = offset + items.length;
|
|
13668
|
+
return {
|
|
13669
|
+
items,
|
|
13670
|
+
nextCursor: nextOffset < allItems.length ? String(nextOffset) : null,
|
|
13671
|
+
totalCount: allItems.length
|
|
13672
|
+
};
|
|
13673
|
+
}
|
|
13674
|
+
};
|
|
13675
|
+
}
|
|
12903
13676
|
async graphql(query, variables) {
|
|
12904
13677
|
return this.environment.graphql(query, variables);
|
|
12905
13678
|
}
|
|
@@ -13042,7 +13815,7 @@ var Granular = class _Granular {
|
|
|
13042
13815
|
onUnexpectedClose;
|
|
13043
13816
|
onReconnectError;
|
|
13044
13817
|
debugHttp = process.env.GRANULAR_DEBUG_HTTP === "1";
|
|
13045
|
-
/** Sandbox-level effect registry: sandboxId → (effectKey → ToolWithHandler) */
|
|
13818
|
+
/** Sandbox-level effect registry: sandboxId → (effectKey@selector → ToolWithHandler) */
|
|
13046
13819
|
sandboxEffects = /* @__PURE__ */ new Map();
|
|
13047
13820
|
/** Live sandbox-scoped effect hosts keyed by sandboxId */
|
|
13048
13821
|
sandboxEffectHosts = /* @__PURE__ */ new Map();
|
|
@@ -13461,7 +14234,8 @@ var Granular = class _Granular {
|
|
|
13461
14234
|
provenance: effect.provenance || { source: "custom" },
|
|
13462
14235
|
tags: effect.tags,
|
|
13463
14236
|
className: effect.className,
|
|
13464
|
-
static: effect.static
|
|
14237
|
+
static: effect.static,
|
|
14238
|
+
versionSelector: effect.versionSelector
|
|
13465
14239
|
};
|
|
13466
14240
|
}
|
|
13467
14241
|
async publishSandboxEffectCatalog(host) {
|
|
@@ -13649,7 +14423,10 @@ var Granular = class _Granular {
|
|
|
13649
14423
|
async registerEffect(sandboxNameOrId, effect) {
|
|
13650
14424
|
const sandbox = await this.findOrCreateSandbox(sandboxNameOrId);
|
|
13651
14425
|
const sandboxId = sandbox.sandboxId;
|
|
13652
|
-
this.getSandboxEffectMap(sandboxId).set(
|
|
14426
|
+
this.getSandboxEffectMap(sandboxId).set(
|
|
14427
|
+
computeEffectRegistrationKey(effect),
|
|
14428
|
+
effect
|
|
14429
|
+
);
|
|
13653
14430
|
await this.syncSandboxEffectCatalog(sandboxId);
|
|
13654
14431
|
}
|
|
13655
14432
|
/**
|
|
@@ -13662,7 +14439,7 @@ var Granular = class _Granular {
|
|
|
13662
14439
|
const sandboxId = sandbox.sandboxId;
|
|
13663
14440
|
const map = this.getSandboxEffectMap(sandboxId);
|
|
13664
14441
|
for (const effect of effects) {
|
|
13665
|
-
map.set(
|
|
14442
|
+
map.set(computeEffectRegistrationKey(effect), effect);
|
|
13666
14443
|
}
|
|
13667
14444
|
await this.syncSandboxEffectCatalog(sandboxId);
|
|
13668
14445
|
}
|
|
@@ -13680,7 +14457,7 @@ var Granular = class _Granular {
|
|
|
13680
14457
|
return;
|
|
13681
14458
|
}
|
|
13682
14459
|
const nextEntries = Array.from(currentMap.entries()).filter(
|
|
13683
|
-
([
|
|
14460
|
+
([, effect]) => computeEffectKey2(effect) !== name && effect.name !== name
|
|
13684
14461
|
);
|
|
13685
14462
|
if (nextEntries.length === currentMap.size) {
|
|
13686
14463
|
return;
|
|
@@ -14109,15 +14886,15 @@ var Granular = class _Granular {
|
|
|
14109
14886
|
};
|
|
14110
14887
|
|
|
14111
14888
|
// src/agent-harness.ts
|
|
14112
|
-
function
|
|
14889
|
+
function asRecord4(value) {
|
|
14113
14890
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
14114
14891
|
return value;
|
|
14115
14892
|
}
|
|
14116
|
-
function
|
|
14893
|
+
function asArray2(value) {
|
|
14117
14894
|
return Array.isArray(value) ? value : [];
|
|
14118
14895
|
}
|
|
14119
14896
|
function toSortedRecords(value) {
|
|
14120
|
-
return Object.values(
|
|
14897
|
+
return Object.values(asRecord4(value) || {}).map((entry) => asRecord4(entry)).filter((entry) => Boolean(entry));
|
|
14121
14898
|
}
|
|
14122
14899
|
function uniqueStrings(values, maxCount) {
|
|
14123
14900
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -14143,7 +14920,7 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
|
|
|
14143
14920
|
const headline = entry.label || entry.id || entry.path || "Unknown";
|
|
14144
14921
|
const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
|
|
14145
14922
|
const classLabel = entry.className || "unknown";
|
|
14146
|
-
const preview =
|
|
14923
|
+
const preview = asArray2(entry.fields).filter(
|
|
14147
14924
|
(field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
|
|
14148
14925
|
).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
|
|
14149
14926
|
return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
|
|
@@ -14280,14 +15057,14 @@ function normalizeActionSummaryForPrompt(line) {
|
|
|
14280
15057
|
return line.replace(/\blimit=/g, "perPage=").replace(/\blimit:/g, "perPage:");
|
|
14281
15058
|
}
|
|
14282
15059
|
function getCurrentClosureId(liveDoc) {
|
|
14283
|
-
const loop =
|
|
15060
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14284
15061
|
return typeof loop?.currentClosureId === "string" ? loop.currentClosureId : null;
|
|
14285
15062
|
}
|
|
14286
15063
|
function getLatestClosure(liveDoc) {
|
|
14287
|
-
const loop =
|
|
15064
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14288
15065
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14289
|
-
const closuresById =
|
|
14290
|
-
const currentClosure = currentClosureId ?
|
|
15066
|
+
const closuresById = asRecord4(loop?.closuresById) || {};
|
|
15067
|
+
const currentClosure = currentClosureId ? asRecord4(closuresById[currentClosureId]) : null;
|
|
14291
15068
|
if (currentClosure) {
|
|
14292
15069
|
return {
|
|
14293
15070
|
...currentClosure,
|
|
@@ -14296,7 +15073,7 @@ function getLatestClosure(liveDoc) {
|
|
|
14296
15073
|
}
|
|
14297
15074
|
const closures = [];
|
|
14298
15075
|
for (const [closureId, value] of Object.entries(closuresById)) {
|
|
14299
|
-
const record =
|
|
15076
|
+
const record = asRecord4(value);
|
|
14300
15077
|
if (!record) continue;
|
|
14301
15078
|
closures.push({ ...record, closureId });
|
|
14302
15079
|
}
|
|
@@ -14333,10 +15110,10 @@ function getJobTimestamp(job) {
|
|
|
14333
15110
|
return Number(job.finishedAt) || Number(job.startedAt) || Number(job.submittedAt) || 0;
|
|
14334
15111
|
}
|
|
14335
15112
|
function getJobRecords(liveDoc) {
|
|
14336
|
-
const jobsById =
|
|
15113
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14337
15114
|
const jobs = [];
|
|
14338
15115
|
for (const [jobId, value] of Object.entries(jobsById)) {
|
|
14339
|
-
const record =
|
|
15116
|
+
const record = asRecord4(value);
|
|
14340
15117
|
if (!record) continue;
|
|
14341
15118
|
jobs.push({ ...record, jobId });
|
|
14342
15119
|
}
|
|
@@ -14346,7 +15123,7 @@ function getJobRecords(liveDoc) {
|
|
|
14346
15123
|
return jobs;
|
|
14347
15124
|
}
|
|
14348
15125
|
function getPromptRecordsFromJobs(liveDoc) {
|
|
14349
|
-
return getJobRecords(liveDoc).flatMap((job) => Object.values(
|
|
15126
|
+
return getJobRecords(liveDoc).flatMap((job) => Object.values(asRecord4(job.prompts) || {})).map((prompt) => asRecord4(prompt)).filter((prompt) => Boolean(prompt));
|
|
14350
15127
|
}
|
|
14351
15128
|
function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
14352
15129
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
@@ -14361,15 +15138,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14361
15138
|
const openDecisionIds = [];
|
|
14362
15139
|
const openPromptIds = [];
|
|
14363
15140
|
for (const job of jobs) {
|
|
14364
|
-
for (const line of
|
|
15141
|
+
for (const line of asArray2(job.actionSummary)) {
|
|
14365
15142
|
if (typeof line === "string" && line.trim()) {
|
|
14366
15143
|
actionSummaryLines.push(line.trim());
|
|
14367
15144
|
}
|
|
14368
15145
|
}
|
|
14369
|
-
for (const rawEvent of
|
|
14370
|
-
const event =
|
|
14371
|
-
const details =
|
|
14372
|
-
const outcome =
|
|
15146
|
+
for (const rawEvent of asArray2(job.actionTrace)) {
|
|
15147
|
+
const event = asRecord4(rawEvent);
|
|
15148
|
+
const details = asRecord4(event?.details);
|
|
15149
|
+
const outcome = asRecord4(event?.outcome);
|
|
14373
15150
|
if (typeof details?.name === "string") {
|
|
14374
15151
|
variableNames.push(details.name);
|
|
14375
15152
|
}
|
|
@@ -14396,7 +15173,7 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14396
15173
|
}
|
|
14397
15174
|
}
|
|
14398
15175
|
}
|
|
14399
|
-
const loop =
|
|
15176
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14400
15177
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14401
15178
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
14402
15179
|
const status = typeof task.status === "string" ? task.status : "pending";
|
|
@@ -14444,15 +15221,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14444
15221
|
openPromptIds.push(prompt.id);
|
|
14445
15222
|
}
|
|
14446
15223
|
}
|
|
14447
|
-
const heap =
|
|
14448
|
-
const variablesByName =
|
|
14449
|
-
const listsByName =
|
|
15224
|
+
const heap = asRecord4(liveDoc?.heap);
|
|
15225
|
+
const variablesByName = asRecord4(heap?.variablesByName) || {};
|
|
15226
|
+
const listsByName = asRecord4(heap?.listsByName) || {};
|
|
14450
15227
|
const recentHints = extractFocusHintsFromActionSummary(actionSummaryLines);
|
|
14451
15228
|
variableNames.push(...recentHints.variableNames);
|
|
14452
15229
|
listNames.push(...recentHints.listNames);
|
|
14453
15230
|
entryPaths.push(...recentHints.entryPaths);
|
|
14454
15231
|
for (const variableName of uniqueStrings(variableNames)) {
|
|
14455
|
-
const variable =
|
|
15232
|
+
const variable = asRecord4(variablesByName[variableName]);
|
|
14456
15233
|
if (!variable) continue;
|
|
14457
15234
|
if (typeof variable.listName === "string") {
|
|
14458
15235
|
listNames.push(variable.listName);
|
|
@@ -14462,13 +15239,13 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14462
15239
|
}
|
|
14463
15240
|
}
|
|
14464
15241
|
for (const listName of uniqueStrings(listNames)) {
|
|
14465
|
-
const list =
|
|
14466
|
-
for (const path2 of
|
|
15242
|
+
const list = asRecord4(listsByName[listName]);
|
|
15243
|
+
for (const path2 of asArray2(list?.paths).slice(0, 4)) {
|
|
14467
15244
|
entryPaths.push(path2);
|
|
14468
15245
|
}
|
|
14469
15246
|
}
|
|
14470
15247
|
if (variableNames.length === 0 && boundary.reason !== "request_start") {
|
|
14471
|
-
const recentVariables = Object.values(variablesByName).map((value) =>
|
|
15248
|
+
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
15249
|
for (const variable of recentVariables) {
|
|
14473
15250
|
if (typeof variable.name === "string") {
|
|
14474
15251
|
variableNames.push(variable.name);
|
|
@@ -14551,12 +15328,12 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14551
15328
|
}
|
|
14552
15329
|
function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
14553
15330
|
if (pendingPrompts.length > 0) return true;
|
|
14554
|
-
const jobsById =
|
|
15331
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14555
15332
|
for (const job of Object.values(jobsById)) {
|
|
14556
|
-
const prompts =
|
|
15333
|
+
const prompts = asRecord4(asRecord4(job)?.prompts);
|
|
14557
15334
|
if (!prompts) continue;
|
|
14558
15335
|
for (const prompt of Object.values(prompts)) {
|
|
14559
|
-
const record =
|
|
15336
|
+
const record = asRecord4(prompt);
|
|
14560
15337
|
if (record?.status === "open") return true;
|
|
14561
15338
|
}
|
|
14562
15339
|
}
|
|
@@ -14564,7 +15341,7 @@ function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
|
14564
15341
|
}
|
|
14565
15342
|
function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
14566
15343
|
const lines = [];
|
|
14567
|
-
const loop =
|
|
15344
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14568
15345
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
14569
15346
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14570
15347
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
@@ -14624,8 +15401,8 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14624
15401
|
const title = typeof decision.title === "string" && decision.title.trim() ? decision.title.trim() : "Decision";
|
|
14625
15402
|
const decisionId = typeof decision.decisionId === "string" ? decision.decisionId : "unknown";
|
|
14626
15403
|
if (status === "open") {
|
|
14627
|
-
const candidatePreview =
|
|
14628
|
-
const record =
|
|
15404
|
+
const candidatePreview = asArray2(decision.candidates).slice(0, 3).map((candidate) => {
|
|
15405
|
+
const record = asRecord4(candidate);
|
|
14629
15406
|
if (!record) return null;
|
|
14630
15407
|
const candidateId = typeof record.id === "string" ? record.id : "unknown";
|
|
14631
15408
|
const candidateLabel = typeof record.label === "string" && record.label.trim() ? record.label.trim() : candidateId;
|
|
@@ -14635,7 +15412,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14635
15412
|
`- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`
|
|
14636
15413
|
);
|
|
14637
15414
|
} else {
|
|
14638
|
-
const selected =
|
|
15415
|
+
const selected = asRecord4(decision.selected);
|
|
14639
15416
|
const label = typeof selected?.label === "string" ? selected.label : typeof selected?.id === "string" ? selected.id : "unknown";
|
|
14640
15417
|
lines.push(`- [resolved] ${title} (${decisionId}) -> ${label}`);
|
|
14641
15418
|
}
|
|
@@ -14648,12 +15425,12 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14648
15425
|
title: prompt.title,
|
|
14649
15426
|
message: prompt.message
|
|
14650
15427
|
})),
|
|
14651
|
-
...Object.values(
|
|
15428
|
+
...Object.values(asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord4(asRecord4(job)?.prompts) || {})).map((prompt) => asRecord4(prompt)).filter(
|
|
14652
15429
|
(prompt) => Boolean(prompt && prompt.status === "open")
|
|
14653
15430
|
)
|
|
14654
15431
|
];
|
|
14655
15432
|
const visiblePrompts = boundary.reason === "request_start" ? openPrompts.filter((prompt) => {
|
|
14656
|
-
const promptRecord =
|
|
15433
|
+
const promptRecord = asRecord4(prompt);
|
|
14657
15434
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
14658
15435
|
const promptId = typeof promptRecord?.id === "string" ? promptRecord.id : typeof prompt.id === "string" ? prompt.id : null;
|
|
14659
15436
|
return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some(
|
|
@@ -14672,7 +15449,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14672
15449
|
}
|
|
14673
15450
|
}
|
|
14674
15451
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14675
|
-
const closureRecord = currentClosureId ?
|
|
15452
|
+
const closureRecord = currentClosureId ? asRecord4(asRecord4(loop?.closuresById)?.[currentClosureId]) : null;
|
|
14676
15453
|
const visibleClosure = closureRecord && (boundary.reason !== "request_start" || (Number(closureRecord.createdAt) || 0) >= boundary.timestamp) ? closureRecord : null;
|
|
14677
15454
|
lines.push("", "Loop Closure:");
|
|
14678
15455
|
if (visibleClosure) {
|
|
@@ -14685,10 +15462,10 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14685
15462
|
return lines.join("\n");
|
|
14686
15463
|
}
|
|
14687
15464
|
function projectHeapSummary(heap, options) {
|
|
14688
|
-
const heapRecord =
|
|
14689
|
-
const entriesByPath =
|
|
14690
|
-
const listsByName =
|
|
14691
|
-
const variablesByName =
|
|
15465
|
+
const heapRecord = asRecord4(heap) || {};
|
|
15466
|
+
const entriesByPath = asRecord4(heapRecord.entriesByPath) || {};
|
|
15467
|
+
const listsByName = asRecord4(heapRecord.listsByName) || {};
|
|
15468
|
+
const variablesByName = asRecord4(heapRecord.variablesByName) || {};
|
|
14692
15469
|
const focusedVariableNames = new Set(
|
|
14693
15470
|
uniqueStrings(options?.focus?.variableNames || [])
|
|
14694
15471
|
);
|
|
@@ -14703,7 +15480,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14703
15480
|
const maxVariables = options?.maxVariables ?? (hasFocus ? 4 : 6);
|
|
14704
15481
|
const maxLists = options?.maxLists ?? (hasFocus ? 3 : 4);
|
|
14705
15482
|
const maxEntries = options?.maxEntries ?? (hasFocus ? 5 : 6);
|
|
14706
|
-
const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) =>
|
|
15483
|
+
const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).sort((left, right) => {
|
|
14707
15484
|
const leftFocused = left.name && focusedVariableNames.has(left.name) ? 1 : 0;
|
|
14708
15485
|
const rightFocused = right.name && focusedVariableNames.has(right.name) ? 1 : 0;
|
|
14709
15486
|
return rightFocused - leftFocused || (right.updatedAt || 0) - (left.updatedAt || 0);
|
|
@@ -14717,7 +15494,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14717
15494
|
for (const variable of variables) {
|
|
14718
15495
|
if (variable.entryPath) referencedPaths.add(variable.entryPath);
|
|
14719
15496
|
if (variable.listName) {
|
|
14720
|
-
const list =
|
|
15497
|
+
const list = asRecord4(
|
|
14721
15498
|
listsByName[variable.listName]
|
|
14722
15499
|
);
|
|
14723
15500
|
for (const path2 of list?.paths || []) referencedPaths.add(path2);
|
|
@@ -14726,10 +15503,10 @@ function projectHeapSummary(heap, options) {
|
|
|
14726
15503
|
for (const path2 of focusedEntryPaths) {
|
|
14727
15504
|
referencedPaths.add(path2);
|
|
14728
15505
|
}
|
|
14729
|
-
const visibleLists = Object.values(listsByName).map((value) =>
|
|
15506
|
+
const visibleLists = Object.values(listsByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter(
|
|
14730
15507
|
(list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))
|
|
14731
15508
|
).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
|
|
14732
|
-
const visibleEntries = Object.values(entriesByPath).map((value) =>
|
|
15509
|
+
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
15510
|
const lines = [];
|
|
14734
15511
|
lines.push("Variables:");
|
|
14735
15512
|
if (variables.length === 0) {
|
|
@@ -14743,7 +15520,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14743
15520
|
continue;
|
|
14744
15521
|
}
|
|
14745
15522
|
if (variable.kind === "entry") {
|
|
14746
|
-
const entry = variable.entryPath ?
|
|
15523
|
+
const entry = variable.entryPath ? asRecord4(
|
|
14747
15524
|
entriesByPath[variable.entryPath]
|
|
14748
15525
|
) : null;
|
|
14749
15526
|
lines.push(
|
|
@@ -14751,7 +15528,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14751
15528
|
);
|
|
14752
15529
|
continue;
|
|
14753
15530
|
}
|
|
14754
|
-
const list = variable.listName ?
|
|
15531
|
+
const list = variable.listName ? asRecord4(listsByName[variable.listName]) : null;
|
|
14755
15532
|
lines.push(
|
|
14756
15533
|
`- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`
|
|
14757
15534
|
);
|
|
@@ -14784,7 +15561,7 @@ function createHarnessVerifierSnapshot(input) {
|
|
|
14784
15561
|
input.projectionOptions
|
|
14785
15562
|
);
|
|
14786
15563
|
const heapDigest = hashString(
|
|
14787
|
-
projectHeapSummary(
|
|
15564
|
+
projectHeapSummary(asRecord4(input.liveDoc?.heap), {
|
|
14788
15565
|
focus: workflowFocus
|
|
14789
15566
|
})
|
|
14790
15567
|
) || "00000000";
|
|
@@ -15110,246 +15887,12 @@ ${loopBlock}
|
|
|
15110
15887
|
- Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
|
|
15111
15888
|
}
|
|
15112
15889
|
|
|
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
15890
|
// src/agent-evals.ts
|
|
15348
15891
|
var DEFAULT_CONTROLLER_BUDGETS = {
|
|
15349
15892
|
maxIterations: 6,
|
|
15350
15893
|
maxNoProgressIterations: 2
|
|
15351
15894
|
};
|
|
15352
|
-
function
|
|
15895
|
+
function asRecord5(value) {
|
|
15353
15896
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15354
15897
|
return value;
|
|
15355
15898
|
}
|
|
@@ -15397,7 +15940,7 @@ function buildArtifactDir(baseDir, suiteName = "granular-agent-evals") {
|
|
|
15397
15940
|
function createTimestampedArtifactDirectory(options) {
|
|
15398
15941
|
return buildArtifactDir(options?.baseDir, options?.suiteName);
|
|
15399
15942
|
}
|
|
15400
|
-
function
|
|
15943
|
+
function asArray3(value) {
|
|
15401
15944
|
if (!value) return [];
|
|
15402
15945
|
return Array.isArray(value) ? value : [value];
|
|
15403
15946
|
}
|
|
@@ -15415,7 +15958,7 @@ function buildScenarioSteps(scenario) {
|
|
|
15415
15958
|
request: scenario.request,
|
|
15416
15959
|
human: scenario.human,
|
|
15417
15960
|
expect: scenario.expect,
|
|
15418
|
-
inspect: [...
|
|
15961
|
+
inspect: [...asArray3(scenario.inspect), ...asArray3(scenario.verify)],
|
|
15419
15962
|
check: scenario.check,
|
|
15420
15963
|
maxIterations: scenario.maxIterations,
|
|
15421
15964
|
setup: {
|
|
@@ -15456,12 +15999,12 @@ function buildHistory(entries) {
|
|
|
15456
15999
|
);
|
|
15457
16000
|
}
|
|
15458
16001
|
function getOpenPromptsFromDoc(liveDoc) {
|
|
15459
|
-
const jobsById =
|
|
16002
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15460
16003
|
const prompts = [];
|
|
15461
16004
|
for (const job of Object.values(jobsById)) {
|
|
15462
|
-
const promptRecords =
|
|
16005
|
+
const promptRecords = asRecord5(asRecord5(job)?.prompts) || {};
|
|
15463
16006
|
for (const raw of Object.values(promptRecords)) {
|
|
15464
|
-
const record =
|
|
16007
|
+
const record = asRecord5(raw);
|
|
15465
16008
|
if (!record || record.status !== "open" || typeof record.promptId !== "string")
|
|
15466
16009
|
continue;
|
|
15467
16010
|
const prompt = normalizePrompt({
|
|
@@ -15482,11 +16025,11 @@ function getOpenPromptsFromDoc(liveDoc) {
|
|
|
15482
16025
|
return prompts;
|
|
15483
16026
|
}
|
|
15484
16027
|
function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
|
|
15485
|
-
const jobsById =
|
|
16028
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15486
16029
|
return prompts.filter((prompt) => {
|
|
15487
16030
|
for (const jobRecord of Object.values(jobsById)) {
|
|
15488
|
-
const promptsById =
|
|
15489
|
-
const promptRecord =
|
|
16031
|
+
const promptsById = asRecord5(asRecord5(jobRecord)?.prompts) || {};
|
|
16032
|
+
const promptRecord = asRecord5(promptsById[prompt.id]);
|
|
15490
16033
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
15491
16034
|
if (openedAt >= boundaryTimestamp) return true;
|
|
15492
16035
|
}
|
|
@@ -15579,10 +16122,10 @@ ${modelOutputInstruction()}`
|
|
|
15579
16122
|
);
|
|
15580
16123
|
}
|
|
15581
16124
|
const raw = await response.json();
|
|
15582
|
-
const content =
|
|
15583
|
-
|
|
16125
|
+
const content = asRecord5(
|
|
16126
|
+
asRecord5(raw.choices?.[0])?.message
|
|
15584
16127
|
)?.content;
|
|
15585
|
-
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) =>
|
|
16128
|
+
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => asRecord5(part)?.text || "").join("") : "";
|
|
15586
16129
|
const parsed = extractJsonObject(text);
|
|
15587
16130
|
if (!parsed) {
|
|
15588
16131
|
if (attempt < 3) {
|
|
@@ -15634,17 +16177,17 @@ async function withTimeout(promise, ms, label) {
|
|
|
15634
16177
|
}
|
|
15635
16178
|
}
|
|
15636
16179
|
function getActionSummary(liveDoc, jobId) {
|
|
15637
|
-
const jobsById =
|
|
15638
|
-
const job =
|
|
16180
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
16181
|
+
const job = asRecord5(jobsById[jobId]);
|
|
15639
16182
|
return Array.isArray(job?.actionSummary) ? job.actionSummary.filter(
|
|
15640
16183
|
(line) => typeof line === "string"
|
|
15641
16184
|
) : [];
|
|
15642
16185
|
}
|
|
15643
16186
|
function normalizeHeapSnapshot2(heap) {
|
|
15644
16187
|
return {
|
|
15645
|
-
entriesByPath:
|
|
15646
|
-
listsByName:
|
|
15647
|
-
variablesByName:
|
|
16188
|
+
entriesByPath: asRecord5(heap?.entriesByPath) || {},
|
|
16189
|
+
listsByName: asRecord5(heap?.listsByName) || {},
|
|
16190
|
+
variablesByName: asRecord5(heap?.variablesByName) || {},
|
|
15648
16191
|
updatedAt: typeof heap?.updatedAt === "number" ? heap.updatedAt : Date.now()
|
|
15649
16192
|
};
|
|
15650
16193
|
}
|
|
@@ -15830,8 +16373,8 @@ async function runAgentEvalSuite(options) {
|
|
|
15830
16373
|
);
|
|
15831
16374
|
}
|
|
15832
16375
|
const inspectionResults = [];
|
|
15833
|
-
const stepChecks =
|
|
15834
|
-
const stepInspections =
|
|
16376
|
+
const stepChecks = asArray3(step.check);
|
|
16377
|
+
const stepInspections = asArray3(step.inspect);
|
|
15835
16378
|
const context = {
|
|
15836
16379
|
conversation,
|
|
15837
16380
|
environment: conversation.environment,
|
|
@@ -15844,7 +16387,7 @@ async function runAgentEvalSuite(options) {
|
|
|
15844
16387
|
promptInteractions: completed.promptInteractions,
|
|
15845
16388
|
result: completed.result,
|
|
15846
16389
|
heap: normalizeHeapSnapshot2(
|
|
15847
|
-
|
|
16390
|
+
asRecord5(
|
|
15848
16391
|
cloneJson(conversation.environment.document)?.heap
|
|
15849
16392
|
)
|
|
15850
16393
|
),
|
|
@@ -16055,7 +16598,7 @@ function createAgentEvalHarness(options) {
|
|
|
16055
16598
|
}
|
|
16056
16599
|
function buildCheckContext(conversation, completed, turnDir) {
|
|
16057
16600
|
const liveDoc = cloneJson(conversation.environment.document);
|
|
16058
|
-
const heap = normalizeHeapSnapshot2(
|
|
16601
|
+
const heap = normalizeHeapSnapshot2(asRecord5(liveDoc?.heap));
|
|
16059
16602
|
return {
|
|
16060
16603
|
conversation,
|
|
16061
16604
|
environment: conversation.environment,
|
|
@@ -16131,7 +16674,7 @@ function createAgentEvalHarness(options) {
|
|
|
16131
16674
|
jobId: pending.job.id,
|
|
16132
16675
|
result: resumed.result,
|
|
16133
16676
|
stdout: [...pending.stdout, ...resumed.stdout],
|
|
16134
|
-
sessionHeap: normalizeHeapSnapshot2(
|
|
16677
|
+
sessionHeap: normalizeHeapSnapshot2(asRecord5(liveDoc?.heap))
|
|
16135
16678
|
});
|
|
16136
16679
|
const responseText = presentation.responseText || pending.finalReply || "Done.";
|
|
16137
16680
|
pending.conversation.history.push({
|
|
@@ -16341,7 +16884,7 @@ function createAgentEvalHarness(options) {
|
|
|
16341
16884
|
const settledLiveDoc = cloneJson(
|
|
16342
16885
|
conversation.environment.document
|
|
16343
16886
|
);
|
|
16344
|
-
const sessionHeap = normalizeHeapSnapshot2(
|
|
16887
|
+
const sessionHeap = normalizeHeapSnapshot2(asRecord5(settledLiveDoc?.heap));
|
|
16345
16888
|
const presentation = resolveJobPresentation({
|
|
16346
16889
|
jobId: job.id,
|
|
16347
16890
|
result: outcome.result,
|