@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.mjs
CHANGED
|
@@ -5619,6 +5619,540 @@ var JobImplementation = class {
|
|
|
5619
5619
|
}
|
|
5620
5620
|
};
|
|
5621
5621
|
|
|
5622
|
+
// src/job-presentation.ts
|
|
5623
|
+
var RESPONSE_KEYS = [
|
|
5624
|
+
"reply",
|
|
5625
|
+
"response",
|
|
5626
|
+
"text",
|
|
5627
|
+
"message",
|
|
5628
|
+
"summary",
|
|
5629
|
+
"answer"
|
|
5630
|
+
];
|
|
5631
|
+
var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
|
|
5632
|
+
var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
|
|
5633
|
+
var LIST_KEY_CANDIDATES = ["listName"];
|
|
5634
|
+
var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
|
|
5635
|
+
var VARIABLE_KEY_CANDIDATES = ["variableName"];
|
|
5636
|
+
var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
|
|
5637
|
+
var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
|
|
5638
|
+
function asRecord2(value) {
|
|
5639
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
5640
|
+
return value;
|
|
5641
|
+
}
|
|
5642
|
+
function normalizeText(value) {
|
|
5643
|
+
if (typeof value !== "string") return null;
|
|
5644
|
+
const trimmed = value.trim();
|
|
5645
|
+
if (!trimmed) return null;
|
|
5646
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
5647
|
+
return null;
|
|
5648
|
+
}
|
|
5649
|
+
return trimmed;
|
|
5650
|
+
}
|
|
5651
|
+
function humanTextFromStdout(stdout) {
|
|
5652
|
+
for (const line of [...stdout].reverse()) {
|
|
5653
|
+
const normalized = normalizeText(line);
|
|
5654
|
+
if (!normalized) continue;
|
|
5655
|
+
if (/^[A-Z_]+:/.test(normalized)) continue;
|
|
5656
|
+
return normalized;
|
|
5657
|
+
}
|
|
5658
|
+
return null;
|
|
5659
|
+
}
|
|
5660
|
+
function pushString(target, value) {
|
|
5661
|
+
if (typeof value === "string" && value.trim()) {
|
|
5662
|
+
target.add(value.trim());
|
|
5663
|
+
}
|
|
5664
|
+
}
|
|
5665
|
+
function pushStringArray(target, value) {
|
|
5666
|
+
if (!Array.isArray(value)) return;
|
|
5667
|
+
for (const item of value) {
|
|
5668
|
+
pushString(target, item);
|
|
5669
|
+
}
|
|
5670
|
+
}
|
|
5671
|
+
function collectReferencesFromRecord(record, refs) {
|
|
5672
|
+
for (const key of ENTRY_KEY_CANDIDATES)
|
|
5673
|
+
pushString(refs.entryPaths, record[key]);
|
|
5674
|
+
for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
|
|
5675
|
+
pushStringArray(refs.entryPaths, record[key]);
|
|
5676
|
+
for (const key of LIST_KEY_CANDIDATES)
|
|
5677
|
+
pushString(refs.listNames, record[key]);
|
|
5678
|
+
for (const key of LIST_ARRAY_KEY_CANDIDATES)
|
|
5679
|
+
pushStringArray(refs.listNames, record[key]);
|
|
5680
|
+
for (const key of VARIABLE_KEY_CANDIDATES)
|
|
5681
|
+
pushString(refs.variableNames, record[key]);
|
|
5682
|
+
for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
|
|
5683
|
+
pushStringArray(refs.variableNames, record[key]);
|
|
5684
|
+
}
|
|
5685
|
+
function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
5686
|
+
if (value === null || value === void 0 || depth > 4 || seen.has(value))
|
|
5687
|
+
return;
|
|
5688
|
+
if (typeof value === "string") {
|
|
5689
|
+
const trimmed = value.trim();
|
|
5690
|
+
if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
|
|
5691
|
+
if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
|
|
5692
|
+
if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
|
|
5693
|
+
return;
|
|
5694
|
+
}
|
|
5695
|
+
if (Array.isArray(value)) {
|
|
5696
|
+
seen.add(value);
|
|
5697
|
+
for (const item of value.slice(0, 24)) {
|
|
5698
|
+
scanForHeapReferences(item, heap, refs, depth + 1, seen);
|
|
5699
|
+
}
|
|
5700
|
+
return;
|
|
5701
|
+
}
|
|
5702
|
+
const record = asRecord2(value);
|
|
5703
|
+
if (!record) return;
|
|
5704
|
+
seen.add(value);
|
|
5705
|
+
collectReferencesFromRecord(record, refs);
|
|
5706
|
+
for (const key of UI_CONTAINER_KEYS) {
|
|
5707
|
+
const nested = asRecord2(record[key]);
|
|
5708
|
+
if (nested) collectReferencesFromRecord(nested, refs);
|
|
5709
|
+
}
|
|
5710
|
+
for (const nested of Object.values(record).slice(0, 24)) {
|
|
5711
|
+
scanForHeapReferences(nested, heap, refs, depth + 1, seen);
|
|
5712
|
+
}
|
|
5713
|
+
}
|
|
5714
|
+
function resolveVariablesToReferences(variableNames, heap, refs) {
|
|
5715
|
+
for (const variableName of variableNames) {
|
|
5716
|
+
const variable = heap.variablesByName?.[variableName];
|
|
5717
|
+
if (!variable) continue;
|
|
5718
|
+
if (variable.kind === "entry" && variable.entryPath) {
|
|
5719
|
+
refs.entryPaths.add(variable.entryPath);
|
|
5720
|
+
}
|
|
5721
|
+
if (variable.kind === "list" && variable.listName) {
|
|
5722
|
+
refs.listNames.add(variable.listName);
|
|
5723
|
+
}
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
function sortEntries(entries) {
|
|
5727
|
+
return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
5728
|
+
}
|
|
5729
|
+
function sortLists(lists) {
|
|
5730
|
+
return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
5731
|
+
}
|
|
5732
|
+
function dedupeEntries(entries) {
|
|
5733
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5734
|
+
const result = [];
|
|
5735
|
+
for (const entry of entries) {
|
|
5736
|
+
if (!entry?.path || seen.has(entry.path)) continue;
|
|
5737
|
+
seen.add(entry.path);
|
|
5738
|
+
result.push(entry);
|
|
5739
|
+
}
|
|
5740
|
+
return result;
|
|
5741
|
+
}
|
|
5742
|
+
function dedupeLists(lists) {
|
|
5743
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5744
|
+
const result = [];
|
|
5745
|
+
for (const list of lists) {
|
|
5746
|
+
if (!list?.name || seen.has(list.name)) continue;
|
|
5747
|
+
seen.add(list.name);
|
|
5748
|
+
result.push(list);
|
|
5749
|
+
}
|
|
5750
|
+
return result;
|
|
5751
|
+
}
|
|
5752
|
+
function extractResponseText(result, stdout) {
|
|
5753
|
+
const directText = normalizeText(result);
|
|
5754
|
+
if (directText) return directText;
|
|
5755
|
+
const record = asRecord2(result);
|
|
5756
|
+
if (record) {
|
|
5757
|
+
for (const key of RESPONSE_KEYS) {
|
|
5758
|
+
const normalized = normalizeText(record[key]);
|
|
5759
|
+
if (normalized) return normalized;
|
|
5760
|
+
}
|
|
5761
|
+
for (const containerKey of UI_CONTAINER_KEYS) {
|
|
5762
|
+
const nested = asRecord2(record[containerKey]);
|
|
5763
|
+
if (!nested) continue;
|
|
5764
|
+
for (const key of RESPONSE_KEYS) {
|
|
5765
|
+
const normalized = normalizeText(nested[key]);
|
|
5766
|
+
if (normalized) return normalized;
|
|
5767
|
+
}
|
|
5768
|
+
}
|
|
5769
|
+
}
|
|
5770
|
+
return humanTextFromStdout(stdout);
|
|
5771
|
+
}
|
|
5772
|
+
function fallbackResponseText(entries, lists) {
|
|
5773
|
+
if (entries.length > 0) {
|
|
5774
|
+
return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
|
|
5775
|
+
}
|
|
5776
|
+
if (lists.length > 0) {
|
|
5777
|
+
const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
|
|
5778
|
+
if (emptyOnly) {
|
|
5779
|
+
return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
|
|
5780
|
+
}
|
|
5781
|
+
return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
|
|
5782
|
+
}
|
|
5783
|
+
return null;
|
|
5784
|
+
}
|
|
5785
|
+
function getJobRelatedEntries(heap, jobId) {
|
|
5786
|
+
return sortEntries(
|
|
5787
|
+
Object.values(heap.entriesByPath || {}).filter(
|
|
5788
|
+
(entry) => entry.relatedJobIds?.includes(jobId)
|
|
5789
|
+
)
|
|
5790
|
+
);
|
|
5791
|
+
}
|
|
5792
|
+
function getJobRelatedLists(heap, jobId) {
|
|
5793
|
+
return sortLists(
|
|
5794
|
+
Object.values(heap.listsByName || {}).filter(
|
|
5795
|
+
(list) => list.relatedJobIds?.includes(jobId)
|
|
5796
|
+
)
|
|
5797
|
+
);
|
|
5798
|
+
}
|
|
5799
|
+
function entriesFromLists(lists, heap) {
|
|
5800
|
+
const entries = [];
|
|
5801
|
+
for (const list of lists) {
|
|
5802
|
+
for (const path2 of list.paths || []) {
|
|
5803
|
+
const entry = heap.entriesByPath?.[path2];
|
|
5804
|
+
if (entry) entries.push(entry);
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
return entries;
|
|
5808
|
+
}
|
|
5809
|
+
function resolveJobPresentation({
|
|
5810
|
+
jobId,
|
|
5811
|
+
result,
|
|
5812
|
+
stdout = [],
|
|
5813
|
+
sessionHeap,
|
|
5814
|
+
allowExplicitArtifacts = true
|
|
5815
|
+
}) {
|
|
5816
|
+
const refs = {
|
|
5817
|
+
entryPaths: /* @__PURE__ */ new Set(),
|
|
5818
|
+
listNames: /* @__PURE__ */ new Set(),
|
|
5819
|
+
variableNames: /* @__PURE__ */ new Set()
|
|
5820
|
+
};
|
|
5821
|
+
if (allowExplicitArtifacts) {
|
|
5822
|
+
scanForHeapReferences(result, sessionHeap, refs);
|
|
5823
|
+
resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
|
|
5824
|
+
}
|
|
5825
|
+
const referencedLists = sortLists(
|
|
5826
|
+
[...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
|
|
5827
|
+
);
|
|
5828
|
+
const referencedEntries = sortEntries(
|
|
5829
|
+
[...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
|
|
5830
|
+
);
|
|
5831
|
+
const jobLists = getJobRelatedLists(sessionHeap, jobId);
|
|
5832
|
+
const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
|
|
5833
|
+
const changedEntries = dedupeEntries([
|
|
5834
|
+
...jobEntries,
|
|
5835
|
+
...entriesFromLists(jobLists, sessionHeap)
|
|
5836
|
+
]);
|
|
5837
|
+
const explicitLists = dedupeLists(referencedLists);
|
|
5838
|
+
const explicitEntries = dedupeEntries([
|
|
5839
|
+
...referencedEntries,
|
|
5840
|
+
...entriesFromLists(referencedLists, sessionHeap)
|
|
5841
|
+
]);
|
|
5842
|
+
const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
|
|
5843
|
+
const lists = hasExplicitArtifacts ? explicitLists : jobLists;
|
|
5844
|
+
const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
|
|
5845
|
+
const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
|
|
5846
|
+
return {
|
|
5847
|
+
responseText,
|
|
5848
|
+
entries,
|
|
5849
|
+
lists,
|
|
5850
|
+
changedEntries,
|
|
5851
|
+
changedLists: jobLists,
|
|
5852
|
+
hasExplicitArtifacts
|
|
5853
|
+
};
|
|
5854
|
+
}
|
|
5855
|
+
|
|
5856
|
+
// src/session-transcript.ts
|
|
5857
|
+
var EMPTY_HEAP = {
|
|
5858
|
+
entriesByPath: {},
|
|
5859
|
+
listsByName: {},
|
|
5860
|
+
variablesByName: {}};
|
|
5861
|
+
function asRecord3(value) {
|
|
5862
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
5863
|
+
return value;
|
|
5864
|
+
}
|
|
5865
|
+
function asArray(value) {
|
|
5866
|
+
return Array.isArray(value) ? value : [];
|
|
5867
|
+
}
|
|
5868
|
+
function asNumber(value) {
|
|
5869
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
5870
|
+
}
|
|
5871
|
+
function asString(value) {
|
|
5872
|
+
return typeof value === "string" ? value : void 0;
|
|
5873
|
+
}
|
|
5874
|
+
function trimString(value) {
|
|
5875
|
+
return typeof value === "string" ? value.trim() : "";
|
|
5876
|
+
}
|
|
5877
|
+
function normalizeShowRefs(value) {
|
|
5878
|
+
const record = asRecord3(value);
|
|
5879
|
+
if (!record) return void 0;
|
|
5880
|
+
const normalizeRefs = (input) => {
|
|
5881
|
+
if (!Array.isArray(input)) return void 0;
|
|
5882
|
+
const refs = Array.from(
|
|
5883
|
+
new Set(
|
|
5884
|
+
input.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean)
|
|
5885
|
+
)
|
|
5886
|
+
);
|
|
5887
|
+
return refs.length > 0 ? refs : void 0;
|
|
5888
|
+
};
|
|
5889
|
+
const show = {
|
|
5890
|
+
entryPaths: normalizeRefs(record.entryPaths),
|
|
5891
|
+
listNames: normalizeRefs(record.listNames),
|
|
5892
|
+
variableNames: normalizeRefs(record.variableNames)
|
|
5893
|
+
};
|
|
5894
|
+
return show.entryPaths || show.listNames || show.variableNames ? show : void 0;
|
|
5895
|
+
}
|
|
5896
|
+
function stringifyTranscriptValue(value, fallback = "") {
|
|
5897
|
+
if (typeof value === "string") {
|
|
5898
|
+
return value.trim() || fallback;
|
|
5899
|
+
}
|
|
5900
|
+
if (typeof value === "boolean") {
|
|
5901
|
+
return value ? "Confirmed" : "Canceled";
|
|
5902
|
+
}
|
|
5903
|
+
if (value === void 0) {
|
|
5904
|
+
return fallback;
|
|
5905
|
+
}
|
|
5906
|
+
try {
|
|
5907
|
+
const json = JSON.stringify(value, null, 2);
|
|
5908
|
+
if (!json || json === "undefined") return fallback;
|
|
5909
|
+
return json.length > 2e3 ? `${json.slice(0, 2e3)}...` : json;
|
|
5910
|
+
} catch {
|
|
5911
|
+
return String(value);
|
|
5912
|
+
}
|
|
5913
|
+
}
|
|
5914
|
+
function buildArtifactHistory(show) {
|
|
5915
|
+
if (!show) return void 0;
|
|
5916
|
+
return `[Agent message]
|
|
5917
|
+
${stringifyTranscriptValue({ show }, "")}`;
|
|
5918
|
+
}
|
|
5919
|
+
function normalizeConversationMessage(raw) {
|
|
5920
|
+
const record = asRecord3(raw);
|
|
5921
|
+
if (!record) return null;
|
|
5922
|
+
const role = record.role === "user" ? "user" : record.role === "assistant" ? "assistant" : null;
|
|
5923
|
+
if (!role) return null;
|
|
5924
|
+
const content = trimString(
|
|
5925
|
+
record.content ?? record.reply ?? record.message ?? record.text
|
|
5926
|
+
);
|
|
5927
|
+
const show = normalizeShowRefs(record.show);
|
|
5928
|
+
const id = asString(record.id) || crypto.randomUUID();
|
|
5929
|
+
const timestamp = asNumber(record.timestamp) || asNumber(record.ts) || 0;
|
|
5930
|
+
if (!content && !show) return null;
|
|
5931
|
+
return {
|
|
5932
|
+
id,
|
|
5933
|
+
role,
|
|
5934
|
+
content,
|
|
5935
|
+
timestamp,
|
|
5936
|
+
jobId: asString(record.jobId),
|
|
5937
|
+
promptId: asString(record.promptId),
|
|
5938
|
+
show,
|
|
5939
|
+
historyContent: role === "assistant" ? content ? `[Assistant reply]
|
|
5940
|
+
${content}` : buildArtifactHistory(show) : void 0,
|
|
5941
|
+
source: "conversation"
|
|
5942
|
+
};
|
|
5943
|
+
}
|
|
5944
|
+
function normalizePromptEntries(jobId, rawPrompts, conversationPromptIds) {
|
|
5945
|
+
const promptsById = asRecord3(rawPrompts) || {};
|
|
5946
|
+
return Object.values(promptsById).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
5947
|
+
(left, right) => (asNumber(left.openedAt) || asNumber(left.answeredAt) || 0) - (asNumber(right.openedAt) || asNumber(right.answeredAt) || 0)
|
|
5948
|
+
).flatMap((prompt) => {
|
|
5949
|
+
const promptId = asString(prompt.promptId);
|
|
5950
|
+
if (!promptId || conversationPromptIds.has(promptId)) return [];
|
|
5951
|
+
const title = trimString(prompt.title);
|
|
5952
|
+
const message = trimString(prompt.message);
|
|
5953
|
+
const assistantContent = message || title || "Input required";
|
|
5954
|
+
const openedAt = asNumber(prompt.openedAt) || 0;
|
|
5955
|
+
const answeredAt = asNumber(prompt.answeredAt) || openedAt;
|
|
5956
|
+
const entries = [
|
|
5957
|
+
{
|
|
5958
|
+
id: `prompt:${promptId}:assistant`,
|
|
5959
|
+
role: "assistant",
|
|
5960
|
+
content: assistantContent,
|
|
5961
|
+
timestamp: openedAt,
|
|
5962
|
+
jobId,
|
|
5963
|
+
promptId,
|
|
5964
|
+
historyContent: `[Assistant reply]
|
|
5965
|
+
${assistantContent}`,
|
|
5966
|
+
source: "job_prompt"
|
|
5967
|
+
}
|
|
5968
|
+
];
|
|
5969
|
+
if (Object.prototype.hasOwnProperty.call(prompt, "answer")) {
|
|
5970
|
+
entries.push({
|
|
5971
|
+
id: `prompt:${promptId}:user`,
|
|
5972
|
+
role: "user",
|
|
5973
|
+
content: stringifyTranscriptValue(prompt.answer, ""),
|
|
5974
|
+
timestamp: answeredAt,
|
|
5975
|
+
jobId,
|
|
5976
|
+
promptId,
|
|
5977
|
+
source: "job_prompt"
|
|
5978
|
+
});
|
|
5979
|
+
}
|
|
5980
|
+
return entries;
|
|
5981
|
+
});
|
|
5982
|
+
}
|
|
5983
|
+
function normalizeAgentMessageEntries(jobId, rawMessages) {
|
|
5984
|
+
return asArray(rawMessages).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
5985
|
+
(left, right) => (asNumber(left.timestamp) || asNumber(left.ts) || 0) - (asNumber(right.timestamp) || asNumber(right.ts) || 0)
|
|
5986
|
+
).flatMap((message) => {
|
|
5987
|
+
const messageId = asString(message.messageId) || asString(message.id) || crypto.randomUUID();
|
|
5988
|
+
const timestamp = asNumber(message.timestamp) || asNumber(message.ts) || 0;
|
|
5989
|
+
const reply = trimString(
|
|
5990
|
+
message.reply ?? message.message ?? message.text ?? message.content
|
|
5991
|
+
);
|
|
5992
|
+
const show = normalizeShowRefs(message.show);
|
|
5993
|
+
const entries = [];
|
|
5994
|
+
if (reply) {
|
|
5995
|
+
entries.push({
|
|
5996
|
+
id: `agent:${messageId}:text`,
|
|
5997
|
+
role: "assistant",
|
|
5998
|
+
content: reply,
|
|
5999
|
+
timestamp,
|
|
6000
|
+
jobId,
|
|
6001
|
+
historyContent: `[Assistant reply]
|
|
6002
|
+
${reply}`,
|
|
6003
|
+
source: "job_agent_message"
|
|
6004
|
+
});
|
|
6005
|
+
}
|
|
6006
|
+
if (show) {
|
|
6007
|
+
entries.push({
|
|
6008
|
+
id: `agent:${messageId}:artifacts`,
|
|
6009
|
+
role: "assistant",
|
|
6010
|
+
content: "",
|
|
6011
|
+
timestamp,
|
|
6012
|
+
jobId,
|
|
6013
|
+
show,
|
|
6014
|
+
historyContent: buildArtifactHistory(show),
|
|
6015
|
+
source: "job_agent_message"
|
|
6016
|
+
});
|
|
6017
|
+
}
|
|
6018
|
+
return entries;
|
|
6019
|
+
});
|
|
6020
|
+
}
|
|
6021
|
+
function buildJobFallbackEntries(jobId, job, sessionHeap) {
|
|
6022
|
+
const timestamp = asNumber(job.finishedAt) || asNumber(job.startedAt) || asNumber(job.submittedAt) || 0;
|
|
6023
|
+
const resultPreview = stringifyTranscriptValue(
|
|
6024
|
+
job.result,
|
|
6025
|
+
"No job result recorded."
|
|
6026
|
+
);
|
|
6027
|
+
const presentation = resolveJobPresentation({
|
|
6028
|
+
jobId,
|
|
6029
|
+
result: job.result,
|
|
6030
|
+
stdout: [],
|
|
6031
|
+
sessionHeap
|
|
6032
|
+
});
|
|
6033
|
+
const entries = [];
|
|
6034
|
+
const responseText = presentation.responseText || "";
|
|
6035
|
+
if (responseText) {
|
|
6036
|
+
entries.push({
|
|
6037
|
+
id: `job:${jobId}:result-text`,
|
|
6038
|
+
role: "assistant",
|
|
6039
|
+
content: responseText,
|
|
6040
|
+
timestamp,
|
|
6041
|
+
jobId,
|
|
6042
|
+
historyContent: `[Assistant reply]
|
|
6043
|
+
${responseText}`,
|
|
6044
|
+
source: "job_result"
|
|
6045
|
+
});
|
|
6046
|
+
}
|
|
6047
|
+
const show = {
|
|
6048
|
+
entryPaths: presentation.entries.map((entry) => entry.path),
|
|
6049
|
+
listNames: presentation.lists.map((list) => list.name)
|
|
6050
|
+
};
|
|
6051
|
+
if (show.entryPaths && show.entryPaths.length > 0 || show.listNames && show.listNames.length > 0) {
|
|
6052
|
+
entries.push({
|
|
6053
|
+
id: `job:${jobId}:result-artifacts`,
|
|
6054
|
+
role: "assistant",
|
|
6055
|
+
content: "",
|
|
6056
|
+
timestamp,
|
|
6057
|
+
jobId,
|
|
6058
|
+
show,
|
|
6059
|
+
historyContent: buildArtifactHistory(show),
|
|
6060
|
+
source: "job_result"
|
|
6061
|
+
});
|
|
6062
|
+
}
|
|
6063
|
+
if (entries.length === 0 && trimString(job.error)) {
|
|
6064
|
+
entries.push({
|
|
6065
|
+
id: `job:${jobId}:result-error`,
|
|
6066
|
+
role: "assistant",
|
|
6067
|
+
content: trimString(job.error),
|
|
6068
|
+
timestamp,
|
|
6069
|
+
jobId,
|
|
6070
|
+
historyContent: `[Assistant reply]
|
|
6071
|
+
${trimString(job.error)}`,
|
|
6072
|
+
source: "job_result"
|
|
6073
|
+
});
|
|
6074
|
+
}
|
|
6075
|
+
if (entries.length === 0 && resultPreview && resultPreview !== "No job result recorded.") {
|
|
6076
|
+
entries.push({
|
|
6077
|
+
id: `job:${jobId}:result-preview`,
|
|
6078
|
+
role: "assistant",
|
|
6079
|
+
content: resultPreview,
|
|
6080
|
+
timestamp,
|
|
6081
|
+
jobId,
|
|
6082
|
+
historyContent: `[Assistant reply]
|
|
6083
|
+
${resultPreview}`,
|
|
6084
|
+
source: "job_result"
|
|
6085
|
+
});
|
|
6086
|
+
}
|
|
6087
|
+
return entries;
|
|
6088
|
+
}
|
|
6089
|
+
function buildJobCodeEntry(jobId, job) {
|
|
6090
|
+
const code = trimString(job.source);
|
|
6091
|
+
if (!code) return null;
|
|
6092
|
+
const jobStatus = asString(job.status);
|
|
6093
|
+
const error = jobStatus === "failed" || jobStatus === "canceled" || jobStatus === "timeout" ? trimString(job.error) || `Job ${jobStatus}` : void 0;
|
|
6094
|
+
return {
|
|
6095
|
+
id: `job:${jobId}:code`,
|
|
6096
|
+
role: "assistant",
|
|
6097
|
+
content: "",
|
|
6098
|
+
timestamp: asNumber(job.submittedAt) || asNumber(job.startedAt) || asNumber(job.finishedAt) || 0,
|
|
6099
|
+
jobId,
|
|
6100
|
+
code,
|
|
6101
|
+
jobStatus,
|
|
6102
|
+
jobResultPreview: stringifyTranscriptValue(job.result, "No job result recorded."),
|
|
6103
|
+
error,
|
|
6104
|
+
source: "job_code"
|
|
6105
|
+
};
|
|
6106
|
+
}
|
|
6107
|
+
function buildSessionTranscript(input) {
|
|
6108
|
+
const liveDoc = input.liveDoc || null;
|
|
6109
|
+
const sessionHeap = input.sessionHeap || EMPTY_HEAP;
|
|
6110
|
+
const transcript = [];
|
|
6111
|
+
const conversationMessages = asArray(asRecord3(liveDoc?.conversation)?.messages).map((message) => normalizeConversationMessage(message)).filter((message) => Boolean(message));
|
|
6112
|
+
const conversationPromptIds = new Set(
|
|
6113
|
+
conversationMessages.map((message) => message.promptId).filter((promptId) => Boolean(promptId))
|
|
6114
|
+
);
|
|
6115
|
+
const assistantConversationJobIds = new Set(
|
|
6116
|
+
conversationMessages.filter((message) => message.role === "assistant" && Boolean(message.jobId)).map((message) => message.jobId)
|
|
6117
|
+
);
|
|
6118
|
+
transcript.push(...conversationMessages);
|
|
6119
|
+
const jobsById = asRecord3(asRecord3(liveDoc?.jobs)?.byId) || {};
|
|
6120
|
+
const jobs = Object.values(jobsById).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
|
|
6121
|
+
(left, right) => (asNumber(left.submittedAt) || asNumber(left.startedAt) || asNumber(left.finishedAt) || 0) - (asNumber(right.submittedAt) || asNumber(right.startedAt) || asNumber(right.finishedAt) || 0)
|
|
6122
|
+
);
|
|
6123
|
+
for (const job of jobs) {
|
|
6124
|
+
const jobId = asString(job.jobId);
|
|
6125
|
+
if (!jobId) continue;
|
|
6126
|
+
const codeEntry = buildJobCodeEntry(jobId, job);
|
|
6127
|
+
if (codeEntry) {
|
|
6128
|
+
transcript.push(codeEntry);
|
|
6129
|
+
}
|
|
6130
|
+
transcript.push(
|
|
6131
|
+
...normalizePromptEntries(jobId, job.prompts, conversationPromptIds)
|
|
6132
|
+
);
|
|
6133
|
+
if (!assistantConversationJobIds.has(jobId)) {
|
|
6134
|
+
const agentEntries = normalizeAgentMessageEntries(jobId, job.agentMessages);
|
|
6135
|
+
if (agentEntries.length > 0) {
|
|
6136
|
+
transcript.push(...agentEntries);
|
|
6137
|
+
} else {
|
|
6138
|
+
transcript.push(
|
|
6139
|
+
...buildJobFallbackEntries(
|
|
6140
|
+
jobId,
|
|
6141
|
+
job,
|
|
6142
|
+
sessionHeap
|
|
6143
|
+
)
|
|
6144
|
+
);
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
}
|
|
6148
|
+
return transcript.sort((left, right) => {
|
|
6149
|
+
if (left.timestamp !== right.timestamp) {
|
|
6150
|
+
return left.timestamp - right.timestamp;
|
|
6151
|
+
}
|
|
6152
|
+
return left.id.localeCompare(right.id);
|
|
6153
|
+
});
|
|
6154
|
+
}
|
|
6155
|
+
|
|
5622
6156
|
// src/endpoints.ts
|
|
5623
6157
|
var LOCAL_API_URL = "ws://localhost:8787/granular";
|
|
5624
6158
|
var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
|
|
@@ -10332,6 +10866,50 @@ function computeEffectKey(effect) {
|
|
|
10332
10866
|
}
|
|
10333
10867
|
return effect.static ? `class:${attachedClass}:static:${effect.name}` : `class:${attachedClass}:instance:${effect.name}`;
|
|
10334
10868
|
}
|
|
10869
|
+
function computeEffectVersionSelectorSpecificity(selector) {
|
|
10870
|
+
if (!selector || selector.mode === "all") {
|
|
10871
|
+
return 0;
|
|
10872
|
+
}
|
|
10873
|
+
if (selector.mode === "exact") {
|
|
10874
|
+
return 2;
|
|
10875
|
+
}
|
|
10876
|
+
return 1;
|
|
10877
|
+
}
|
|
10878
|
+
function matchesEffectVersionSelector(selector, buildVersionNumber) {
|
|
10879
|
+
if (!selector || selector.mode === "all") {
|
|
10880
|
+
return true;
|
|
10881
|
+
}
|
|
10882
|
+
if (typeof buildVersionNumber !== "number" || !Number.isFinite(buildVersionNumber)) {
|
|
10883
|
+
return false;
|
|
10884
|
+
}
|
|
10885
|
+
if (selector.mode === "exact") {
|
|
10886
|
+
return buildVersionNumber === selector.versionNumber;
|
|
10887
|
+
}
|
|
10888
|
+
if (selector.mode === "before") {
|
|
10889
|
+
return buildVersionNumber < selector.versionNumber;
|
|
10890
|
+
}
|
|
10891
|
+
return buildVersionNumber > selector.versionNumber;
|
|
10892
|
+
}
|
|
10893
|
+
function selectRegisteredEffect(effectMap, effectKey, buildVersionNumber) {
|
|
10894
|
+
let bestEffect;
|
|
10895
|
+
let bestSpecificity = Number.NEGATIVE_INFINITY;
|
|
10896
|
+
for (const effect of effectMap.values()) {
|
|
10897
|
+
if (computeEffectKey(effect) !== effectKey) {
|
|
10898
|
+
continue;
|
|
10899
|
+
}
|
|
10900
|
+
if (!matchesEffectVersionSelector(effect.versionSelector, buildVersionNumber)) {
|
|
10901
|
+
continue;
|
|
10902
|
+
}
|
|
10903
|
+
const specificity = computeEffectVersionSelectorSpecificity(
|
|
10904
|
+
effect.versionSelector
|
|
10905
|
+
);
|
|
10906
|
+
if (!bestEffect || specificity > bestSpecificity) {
|
|
10907
|
+
bestEffect = effect;
|
|
10908
|
+
bestSpecificity = specificity;
|
|
10909
|
+
}
|
|
10910
|
+
}
|
|
10911
|
+
return bestEffect;
|
|
10912
|
+
}
|
|
10335
10913
|
function normalizeEffectBehaviors(value) {
|
|
10336
10914
|
return normalizeEffectBehaviorSummary(
|
|
10337
10915
|
value
|
|
@@ -10352,9 +10930,17 @@ function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
|
10352
10930
|
return void 0;
|
|
10353
10931
|
}
|
|
10354
10932
|
if (reverseHandler.includes(":")) {
|
|
10355
|
-
return
|
|
10933
|
+
return selectRegisteredEffect(
|
|
10934
|
+
effectMap,
|
|
10935
|
+
reverseHandler,
|
|
10936
|
+
request.context?.buildVersionNumber
|
|
10937
|
+
);
|
|
10356
10938
|
}
|
|
10357
|
-
const directMatch =
|
|
10939
|
+
const directMatch = selectRegisteredEffect(
|
|
10940
|
+
effectMap,
|
|
10941
|
+
reverseHandler,
|
|
10942
|
+
request.context?.buildVersionNumber
|
|
10943
|
+
);
|
|
10358
10944
|
if (directMatch) {
|
|
10359
10945
|
return directMatch;
|
|
10360
10946
|
}
|
|
@@ -10369,7 +10955,11 @@ function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
|
10369
10955
|
})
|
|
10370
10956
|
];
|
|
10371
10957
|
for (const candidateKey of candidateKeys) {
|
|
10372
|
-
const candidate =
|
|
10958
|
+
const candidate = selectRegisteredEffect(
|
|
10959
|
+
effectMap,
|
|
10960
|
+
candidateKey,
|
|
10961
|
+
request.context?.buildVersionNumber
|
|
10962
|
+
);
|
|
10373
10963
|
if (candidate) {
|
|
10374
10964
|
return candidate;
|
|
10375
10965
|
}
|
|
@@ -10405,7 +10995,11 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
10405
10995
|
return { effect, mode, handler: effect.handler };
|
|
10406
10996
|
}
|
|
10407
10997
|
async function invokeRegisteredEffect(effectMap, request) {
|
|
10408
|
-
const effect =
|
|
10998
|
+
const effect = selectRegisteredEffect(
|
|
10999
|
+
effectMap,
|
|
11000
|
+
request.effectKey,
|
|
11001
|
+
request.context?.buildVersionNumber
|
|
11002
|
+
);
|
|
10409
11003
|
if (!effect) {
|
|
10410
11004
|
throw new Error(`Effect handler not found: ${request.effectKey}`);
|
|
10411
11005
|
}
|
|
@@ -11664,6 +12258,17 @@ function computeEffectKey2(effect) {
|
|
|
11664
12258
|
}
|
|
11665
12259
|
return effect.static ? `class:${attachedClass}:static:${effect.name}` : `class:${attachedClass}:instance:${effect.name}`;
|
|
11666
12260
|
}
|
|
12261
|
+
function computeEffectVersionSelectorKey(selector) {
|
|
12262
|
+
if (!selector || selector.mode === "all") {
|
|
12263
|
+
return "all";
|
|
12264
|
+
}
|
|
12265
|
+
return `${selector.mode}:${selector.versionNumber}`;
|
|
12266
|
+
}
|
|
12267
|
+
function computeEffectRegistrationKey(effect) {
|
|
12268
|
+
return `${computeEffectKey2(effect)}@${computeEffectVersionSelectorKey(
|
|
12269
|
+
effect.versionSelector
|
|
12270
|
+
)}`;
|
|
12271
|
+
}
|
|
11667
12272
|
function buildEffectHostUrl(apiUrl, sandboxId, effectClientId, clientId) {
|
|
11668
12273
|
const url = new URL(apiUrl);
|
|
11669
12274
|
if (url.pathname.endsWith("/granular/ws/connect")) {
|
|
@@ -12158,7 +12763,9 @@ var Environment = class {
|
|
|
12158
12763
|
{ target: targetPath }
|
|
12159
12764
|
);
|
|
12160
12765
|
if (result.errors?.length) {
|
|
12161
|
-
throw new Error(
|
|
12766
|
+
throw new Error(
|
|
12767
|
+
`attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12768
|
+
);
|
|
12162
12769
|
}
|
|
12163
12770
|
}
|
|
12164
12771
|
/**
|
|
@@ -12193,7 +12800,9 @@ var Environment = class {
|
|
|
12193
12800
|
}`
|
|
12194
12801
|
);
|
|
12195
12802
|
if (result.errors?.length) {
|
|
12196
|
-
throw new Error(
|
|
12803
|
+
throw new Error(
|
|
12804
|
+
`detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12805
|
+
);
|
|
12197
12806
|
}
|
|
12198
12807
|
}
|
|
12199
12808
|
/**
|
|
@@ -12315,7 +12924,9 @@ var Environment = class {
|
|
|
12315
12924
|
async _runGraphql(query, label) {
|
|
12316
12925
|
const result = await this.graphql(query);
|
|
12317
12926
|
if (result.errors?.length) {
|
|
12318
|
-
throw new Error(
|
|
12927
|
+
throw new Error(
|
|
12928
|
+
`${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
|
|
12929
|
+
);
|
|
12319
12930
|
}
|
|
12320
12931
|
return result.data;
|
|
12321
12932
|
}
|
|
@@ -12850,6 +13461,9 @@ var EnvironmentSession = class extends Session {
|
|
|
12850
13461
|
get envName() {
|
|
12851
13462
|
return this.environment.envName;
|
|
12852
13463
|
}
|
|
13464
|
+
get tag() {
|
|
13465
|
+
return this.environment.tag;
|
|
13466
|
+
}
|
|
12853
13467
|
get versionId() {
|
|
12854
13468
|
return this.environment.versionId;
|
|
12855
13469
|
}
|
|
@@ -12869,12 +13483,171 @@ var EnvironmentSession = class extends Session {
|
|
|
12869
13483
|
return this.environment.feedback;
|
|
12870
13484
|
}
|
|
12871
13485
|
/**
|
|
12872
|
-
* Return a plain JS
|
|
13486
|
+
* Return a plain JS copy of the synced session heap.
|
|
12873
13487
|
*/
|
|
12874
13488
|
getHeap() {
|
|
12875
13489
|
const doc = this.document;
|
|
12876
13490
|
return normalizeHeapSnapshot(doc?.heap);
|
|
12877
13491
|
}
|
|
13492
|
+
async sessionDataRequest(path2, query) {
|
|
13493
|
+
const searchParams = new URLSearchParams();
|
|
13494
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
13495
|
+
if (value !== null && typeof value !== "undefined" && value !== "") {
|
|
13496
|
+
searchParams.set(key, String(value));
|
|
13497
|
+
}
|
|
13498
|
+
}
|
|
13499
|
+
const queryString = searchParams.toString();
|
|
13500
|
+
const response = await fetch(
|
|
13501
|
+
`${this.environment.runtimeBaseUrl}/orchestrator/ws/sessions/${encodeURIComponent(this.sessionId)}${path2}${queryString ? `?${queryString}` : ""}`,
|
|
13502
|
+
{
|
|
13503
|
+
method: "GET",
|
|
13504
|
+
headers: {
|
|
13505
|
+
Authorization: `Bearer ${this.environment.authToken}`,
|
|
13506
|
+
"Content-Type": "application/json"
|
|
13507
|
+
}
|
|
13508
|
+
}
|
|
13509
|
+
);
|
|
13510
|
+
if (!response.ok) {
|
|
13511
|
+
const errorText = await response.text();
|
|
13512
|
+
throw new Error(
|
|
13513
|
+
`Session data API Error (${response.status}): ${errorText}`
|
|
13514
|
+
);
|
|
13515
|
+
}
|
|
13516
|
+
return response.json();
|
|
13517
|
+
}
|
|
13518
|
+
async collectAllSessionItems(listPage) {
|
|
13519
|
+
const items = [];
|
|
13520
|
+
let cursor = null;
|
|
13521
|
+
do {
|
|
13522
|
+
const page = await listPage({ limit: 500, cursor });
|
|
13523
|
+
items.push(...page.items);
|
|
13524
|
+
cursor = page.nextCursor;
|
|
13525
|
+
} while (cursor);
|
|
13526
|
+
return items;
|
|
13527
|
+
}
|
|
13528
|
+
/**
|
|
13529
|
+
* Fetch the live session document from the runtime DO.
|
|
13530
|
+
*
|
|
13531
|
+
* For history and saved artifacts, prefer the collection APIs on
|
|
13532
|
+
* `messages`, `timeline`, `jobs`, and `heap`.
|
|
13533
|
+
*/
|
|
13534
|
+
async getDocument() {
|
|
13535
|
+
return this.sessionDataRequest("/document");
|
|
13536
|
+
}
|
|
13537
|
+
get messages() {
|
|
13538
|
+
return {
|
|
13539
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13540
|
+
"/messages",
|
|
13541
|
+
options
|
|
13542
|
+
)
|
|
13543
|
+
};
|
|
13544
|
+
}
|
|
13545
|
+
get timeline() {
|
|
13546
|
+
return {
|
|
13547
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13548
|
+
"/timeline",
|
|
13549
|
+
options
|
|
13550
|
+
)
|
|
13551
|
+
};
|
|
13552
|
+
}
|
|
13553
|
+
get jobs() {
|
|
13554
|
+
return {
|
|
13555
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13556
|
+
"/jobs",
|
|
13557
|
+
options
|
|
13558
|
+
),
|
|
13559
|
+
get: (jobId) => this.sessionDataRequest(
|
|
13560
|
+
`/jobs/${encodeURIComponent(jobId)}`
|
|
13561
|
+
)
|
|
13562
|
+
};
|
|
13563
|
+
}
|
|
13564
|
+
get heap() {
|
|
13565
|
+
return {
|
|
13566
|
+
entries: {
|
|
13567
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13568
|
+
"/heap/entries",
|
|
13569
|
+
options
|
|
13570
|
+
),
|
|
13571
|
+
get: (path2) => this.sessionDataRequest(
|
|
13572
|
+
`/heap/entries/${encodeURIComponent(path2)}`
|
|
13573
|
+
)
|
|
13574
|
+
},
|
|
13575
|
+
lists: {
|
|
13576
|
+
list: (options = {}) => this.sessionDataRequest(
|
|
13577
|
+
"/heap/lists",
|
|
13578
|
+
options
|
|
13579
|
+
),
|
|
13580
|
+
get: (name) => this.sessionDataRequest(
|
|
13581
|
+
`/heap/lists/${encodeURIComponent(name)}`
|
|
13582
|
+
)
|
|
13583
|
+
}
|
|
13584
|
+
};
|
|
13585
|
+
}
|
|
13586
|
+
get transcript() {
|
|
13587
|
+
return {
|
|
13588
|
+
list: async (options = {}) => {
|
|
13589
|
+
const [messages, jobs, entries, lists] = await Promise.all([
|
|
13590
|
+
this.collectAllSessionItems(this.messages.list),
|
|
13591
|
+
this.collectAllSessionItems(
|
|
13592
|
+
(pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
|
|
13593
|
+
),
|
|
13594
|
+
this.collectAllSessionItems(this.heap.entries.list),
|
|
13595
|
+
this.collectAllSessionItems(this.heap.lists.list)
|
|
13596
|
+
]);
|
|
13597
|
+
const liveDoc = {
|
|
13598
|
+
conversation: { messages },
|
|
13599
|
+
jobs: {
|
|
13600
|
+
byId: Object.fromEntries(
|
|
13601
|
+
jobs.map((job) => {
|
|
13602
|
+
const record = job && typeof job === "object" ? job : null;
|
|
13603
|
+
const id = typeof record?.jobId === "string" ? record.jobId : typeof record?.id === "string" ? record.id : null;
|
|
13604
|
+
return id ? [id, record] : null;
|
|
13605
|
+
}).filter(
|
|
13606
|
+
(entry) => Boolean(entry)
|
|
13607
|
+
)
|
|
13608
|
+
)
|
|
13609
|
+
}
|
|
13610
|
+
};
|
|
13611
|
+
const heap = normalizeHeapSnapshot({
|
|
13612
|
+
entriesByPath: Object.fromEntries(
|
|
13613
|
+
entries.map((entry) => {
|
|
13614
|
+
return entry?.path ? [
|
|
13615
|
+
entry.path,
|
|
13616
|
+
entry
|
|
13617
|
+
] : null;
|
|
13618
|
+
}).filter(
|
|
13619
|
+
(entry) => Boolean(entry)
|
|
13620
|
+
)
|
|
13621
|
+
),
|
|
13622
|
+
listsByName: Object.fromEntries(
|
|
13623
|
+
lists.map((list) => {
|
|
13624
|
+
return list?.name ? [list.name, list] : null;
|
|
13625
|
+
}).filter(
|
|
13626
|
+
(entry) => Boolean(entry)
|
|
13627
|
+
)
|
|
13628
|
+
),
|
|
13629
|
+
variablesByName: this.getHeap().variablesByName,
|
|
13630
|
+
updatedAt: Date.now()
|
|
13631
|
+
});
|
|
13632
|
+
const allItems = buildSessionTranscript({
|
|
13633
|
+
liveDoc,
|
|
13634
|
+
sessionHeap: heap
|
|
13635
|
+
});
|
|
13636
|
+
const limit = Math.max(
|
|
13637
|
+
1,
|
|
13638
|
+
Math.min(500, Math.floor(options.limit ?? 100))
|
|
13639
|
+
);
|
|
13640
|
+
const offset = typeof options.cursor === "string" ? Number.parseInt(options.cursor, 10) || 0 : 0;
|
|
13641
|
+
const items = allItems.slice(offset, offset + limit);
|
|
13642
|
+
const nextOffset = offset + items.length;
|
|
13643
|
+
return {
|
|
13644
|
+
items,
|
|
13645
|
+
nextCursor: nextOffset < allItems.length ? String(nextOffset) : null,
|
|
13646
|
+
totalCount: allItems.length
|
|
13647
|
+
};
|
|
13648
|
+
}
|
|
13649
|
+
};
|
|
13650
|
+
}
|
|
12878
13651
|
async graphql(query, variables) {
|
|
12879
13652
|
return this.environment.graphql(query, variables);
|
|
12880
13653
|
}
|
|
@@ -13017,7 +13790,7 @@ var Granular = class _Granular {
|
|
|
13017
13790
|
onUnexpectedClose;
|
|
13018
13791
|
onReconnectError;
|
|
13019
13792
|
debugHttp = process.env.GRANULAR_DEBUG_HTTP === "1";
|
|
13020
|
-
/** Sandbox-level effect registry: sandboxId → (effectKey → ToolWithHandler) */
|
|
13793
|
+
/** Sandbox-level effect registry: sandboxId → (effectKey@selector → ToolWithHandler) */
|
|
13021
13794
|
sandboxEffects = /* @__PURE__ */ new Map();
|
|
13022
13795
|
/** Live sandbox-scoped effect hosts keyed by sandboxId */
|
|
13023
13796
|
sandboxEffectHosts = /* @__PURE__ */ new Map();
|
|
@@ -13436,7 +14209,8 @@ var Granular = class _Granular {
|
|
|
13436
14209
|
provenance: effect.provenance || { source: "custom" },
|
|
13437
14210
|
tags: effect.tags,
|
|
13438
14211
|
className: effect.className,
|
|
13439
|
-
static: effect.static
|
|
14212
|
+
static: effect.static,
|
|
14213
|
+
versionSelector: effect.versionSelector
|
|
13440
14214
|
};
|
|
13441
14215
|
}
|
|
13442
14216
|
async publishSandboxEffectCatalog(host) {
|
|
@@ -13624,7 +14398,10 @@ var Granular = class _Granular {
|
|
|
13624
14398
|
async registerEffect(sandboxNameOrId, effect) {
|
|
13625
14399
|
const sandbox = await this.findOrCreateSandbox(sandboxNameOrId);
|
|
13626
14400
|
const sandboxId = sandbox.sandboxId;
|
|
13627
|
-
this.getSandboxEffectMap(sandboxId).set(
|
|
14401
|
+
this.getSandboxEffectMap(sandboxId).set(
|
|
14402
|
+
computeEffectRegistrationKey(effect),
|
|
14403
|
+
effect
|
|
14404
|
+
);
|
|
13628
14405
|
await this.syncSandboxEffectCatalog(sandboxId);
|
|
13629
14406
|
}
|
|
13630
14407
|
/**
|
|
@@ -13637,7 +14414,7 @@ var Granular = class _Granular {
|
|
|
13637
14414
|
const sandboxId = sandbox.sandboxId;
|
|
13638
14415
|
const map = this.getSandboxEffectMap(sandboxId);
|
|
13639
14416
|
for (const effect of effects) {
|
|
13640
|
-
map.set(
|
|
14417
|
+
map.set(computeEffectRegistrationKey(effect), effect);
|
|
13641
14418
|
}
|
|
13642
14419
|
await this.syncSandboxEffectCatalog(sandboxId);
|
|
13643
14420
|
}
|
|
@@ -13655,7 +14432,7 @@ var Granular = class _Granular {
|
|
|
13655
14432
|
return;
|
|
13656
14433
|
}
|
|
13657
14434
|
const nextEntries = Array.from(currentMap.entries()).filter(
|
|
13658
|
-
([
|
|
14435
|
+
([, effect]) => computeEffectKey2(effect) !== name && effect.name !== name
|
|
13659
14436
|
);
|
|
13660
14437
|
if (nextEntries.length === currentMap.size) {
|
|
13661
14438
|
return;
|
|
@@ -14084,15 +14861,15 @@ var Granular = class _Granular {
|
|
|
14084
14861
|
};
|
|
14085
14862
|
|
|
14086
14863
|
// src/agent-harness.ts
|
|
14087
|
-
function
|
|
14864
|
+
function asRecord4(value) {
|
|
14088
14865
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
14089
14866
|
return value;
|
|
14090
14867
|
}
|
|
14091
|
-
function
|
|
14868
|
+
function asArray2(value) {
|
|
14092
14869
|
return Array.isArray(value) ? value : [];
|
|
14093
14870
|
}
|
|
14094
14871
|
function toSortedRecords(value) {
|
|
14095
|
-
return Object.values(
|
|
14872
|
+
return Object.values(asRecord4(value) || {}).map((entry) => asRecord4(entry)).filter((entry) => Boolean(entry));
|
|
14096
14873
|
}
|
|
14097
14874
|
function uniqueStrings(values, maxCount) {
|
|
14098
14875
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -14118,7 +14895,7 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
|
|
|
14118
14895
|
const headline = entry.label || entry.id || entry.path || "Unknown";
|
|
14119
14896
|
const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
|
|
14120
14897
|
const classLabel = entry.className || "unknown";
|
|
14121
|
-
const preview =
|
|
14898
|
+
const preview = asArray2(entry.fields).filter(
|
|
14122
14899
|
(field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
|
|
14123
14900
|
).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
|
|
14124
14901
|
return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
|
|
@@ -14255,14 +15032,14 @@ function normalizeActionSummaryForPrompt(line) {
|
|
|
14255
15032
|
return line.replace(/\blimit=/g, "perPage=").replace(/\blimit:/g, "perPage:");
|
|
14256
15033
|
}
|
|
14257
15034
|
function getCurrentClosureId(liveDoc) {
|
|
14258
|
-
const loop =
|
|
15035
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14259
15036
|
return typeof loop?.currentClosureId === "string" ? loop.currentClosureId : null;
|
|
14260
15037
|
}
|
|
14261
15038
|
function getLatestClosure(liveDoc) {
|
|
14262
|
-
const loop =
|
|
15039
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14263
15040
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14264
|
-
const closuresById =
|
|
14265
|
-
const currentClosure = currentClosureId ?
|
|
15041
|
+
const closuresById = asRecord4(loop?.closuresById) || {};
|
|
15042
|
+
const currentClosure = currentClosureId ? asRecord4(closuresById[currentClosureId]) : null;
|
|
14266
15043
|
if (currentClosure) {
|
|
14267
15044
|
return {
|
|
14268
15045
|
...currentClosure,
|
|
@@ -14271,7 +15048,7 @@ function getLatestClosure(liveDoc) {
|
|
|
14271
15048
|
}
|
|
14272
15049
|
const closures = [];
|
|
14273
15050
|
for (const [closureId, value] of Object.entries(closuresById)) {
|
|
14274
|
-
const record =
|
|
15051
|
+
const record = asRecord4(value);
|
|
14275
15052
|
if (!record) continue;
|
|
14276
15053
|
closures.push({ ...record, closureId });
|
|
14277
15054
|
}
|
|
@@ -14308,10 +15085,10 @@ function getJobTimestamp(job) {
|
|
|
14308
15085
|
return Number(job.finishedAt) || Number(job.startedAt) || Number(job.submittedAt) || 0;
|
|
14309
15086
|
}
|
|
14310
15087
|
function getJobRecords(liveDoc) {
|
|
14311
|
-
const jobsById =
|
|
15088
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14312
15089
|
const jobs = [];
|
|
14313
15090
|
for (const [jobId, value] of Object.entries(jobsById)) {
|
|
14314
|
-
const record =
|
|
15091
|
+
const record = asRecord4(value);
|
|
14315
15092
|
if (!record) continue;
|
|
14316
15093
|
jobs.push({ ...record, jobId });
|
|
14317
15094
|
}
|
|
@@ -14321,7 +15098,7 @@ function getJobRecords(liveDoc) {
|
|
|
14321
15098
|
return jobs;
|
|
14322
15099
|
}
|
|
14323
15100
|
function getPromptRecordsFromJobs(liveDoc) {
|
|
14324
|
-
return getJobRecords(liveDoc).flatMap((job) => Object.values(
|
|
15101
|
+
return getJobRecords(liveDoc).flatMap((job) => Object.values(asRecord4(job.prompts) || {})).map((prompt) => asRecord4(prompt)).filter((prompt) => Boolean(prompt));
|
|
14325
15102
|
}
|
|
14326
15103
|
function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
14327
15104
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
@@ -14336,15 +15113,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14336
15113
|
const openDecisionIds = [];
|
|
14337
15114
|
const openPromptIds = [];
|
|
14338
15115
|
for (const job of jobs) {
|
|
14339
|
-
for (const line of
|
|
15116
|
+
for (const line of asArray2(job.actionSummary)) {
|
|
14340
15117
|
if (typeof line === "string" && line.trim()) {
|
|
14341
15118
|
actionSummaryLines.push(line.trim());
|
|
14342
15119
|
}
|
|
14343
15120
|
}
|
|
14344
|
-
for (const rawEvent of
|
|
14345
|
-
const event =
|
|
14346
|
-
const details =
|
|
14347
|
-
const outcome =
|
|
15121
|
+
for (const rawEvent of asArray2(job.actionTrace)) {
|
|
15122
|
+
const event = asRecord4(rawEvent);
|
|
15123
|
+
const details = asRecord4(event?.details);
|
|
15124
|
+
const outcome = asRecord4(event?.outcome);
|
|
14348
15125
|
if (typeof details?.name === "string") {
|
|
14349
15126
|
variableNames.push(details.name);
|
|
14350
15127
|
}
|
|
@@ -14371,7 +15148,7 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14371
15148
|
}
|
|
14372
15149
|
}
|
|
14373
15150
|
}
|
|
14374
|
-
const loop =
|
|
15151
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14375
15152
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14376
15153
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
14377
15154
|
const status = typeof task.status === "string" ? task.status : "pending";
|
|
@@ -14419,15 +15196,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14419
15196
|
openPromptIds.push(prompt.id);
|
|
14420
15197
|
}
|
|
14421
15198
|
}
|
|
14422
|
-
const heap =
|
|
14423
|
-
const variablesByName =
|
|
14424
|
-
const listsByName =
|
|
15199
|
+
const heap = asRecord4(liveDoc?.heap);
|
|
15200
|
+
const variablesByName = asRecord4(heap?.variablesByName) || {};
|
|
15201
|
+
const listsByName = asRecord4(heap?.listsByName) || {};
|
|
14425
15202
|
const recentHints = extractFocusHintsFromActionSummary(actionSummaryLines);
|
|
14426
15203
|
variableNames.push(...recentHints.variableNames);
|
|
14427
15204
|
listNames.push(...recentHints.listNames);
|
|
14428
15205
|
entryPaths.push(...recentHints.entryPaths);
|
|
14429
15206
|
for (const variableName of uniqueStrings(variableNames)) {
|
|
14430
|
-
const variable =
|
|
15207
|
+
const variable = asRecord4(variablesByName[variableName]);
|
|
14431
15208
|
if (!variable) continue;
|
|
14432
15209
|
if (typeof variable.listName === "string") {
|
|
14433
15210
|
listNames.push(variable.listName);
|
|
@@ -14437,13 +15214,13 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
14437
15214
|
}
|
|
14438
15215
|
}
|
|
14439
15216
|
for (const listName of uniqueStrings(listNames)) {
|
|
14440
|
-
const list =
|
|
14441
|
-
for (const path2 of
|
|
15217
|
+
const list = asRecord4(listsByName[listName]);
|
|
15218
|
+
for (const path2 of asArray2(list?.paths).slice(0, 4)) {
|
|
14442
15219
|
entryPaths.push(path2);
|
|
14443
15220
|
}
|
|
14444
15221
|
}
|
|
14445
15222
|
if (variableNames.length === 0 && boundary.reason !== "request_start") {
|
|
14446
|
-
const recentVariables = Object.values(variablesByName).map((value) =>
|
|
15223
|
+
const recentVariables = Object.values(variablesByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, 3);
|
|
14447
15224
|
for (const variable of recentVariables) {
|
|
14448
15225
|
if (typeof variable.name === "string") {
|
|
14449
15226
|
variableNames.push(variable.name);
|
|
@@ -14526,12 +15303,12 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14526
15303
|
}
|
|
14527
15304
|
function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
14528
15305
|
if (pendingPrompts.length > 0) return true;
|
|
14529
|
-
const jobsById =
|
|
15306
|
+
const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
|
|
14530
15307
|
for (const job of Object.values(jobsById)) {
|
|
14531
|
-
const prompts =
|
|
15308
|
+
const prompts = asRecord4(asRecord4(job)?.prompts);
|
|
14532
15309
|
if (!prompts) continue;
|
|
14533
15310
|
for (const prompt of Object.values(prompts)) {
|
|
14534
|
-
const record =
|
|
15311
|
+
const record = asRecord4(prompt);
|
|
14535
15312
|
if (record?.status === "open") return true;
|
|
14536
15313
|
}
|
|
14537
15314
|
}
|
|
@@ -14539,7 +15316,7 @@ function hasOpenPrompt(liveDoc, pendingPrompts) {
|
|
|
14539
15316
|
}
|
|
14540
15317
|
function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
14541
15318
|
const lines = [];
|
|
14542
|
-
const loop =
|
|
15319
|
+
const loop = asRecord4(liveDoc?.loop);
|
|
14543
15320
|
const boundary = getWorkflowBoundary(liveDoc, options);
|
|
14544
15321
|
const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
|
|
14545
15322
|
const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
|
|
@@ -14599,8 +15376,8 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14599
15376
|
const title = typeof decision.title === "string" && decision.title.trim() ? decision.title.trim() : "Decision";
|
|
14600
15377
|
const decisionId = typeof decision.decisionId === "string" ? decision.decisionId : "unknown";
|
|
14601
15378
|
if (status === "open") {
|
|
14602
|
-
const candidatePreview =
|
|
14603
|
-
const record =
|
|
15379
|
+
const candidatePreview = asArray2(decision.candidates).slice(0, 3).map((candidate) => {
|
|
15380
|
+
const record = asRecord4(candidate);
|
|
14604
15381
|
if (!record) return null;
|
|
14605
15382
|
const candidateId = typeof record.id === "string" ? record.id : "unknown";
|
|
14606
15383
|
const candidateLabel = typeof record.label === "string" && record.label.trim() ? record.label.trim() : candidateId;
|
|
@@ -14610,7 +15387,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14610
15387
|
`- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`
|
|
14611
15388
|
);
|
|
14612
15389
|
} else {
|
|
14613
|
-
const selected =
|
|
15390
|
+
const selected = asRecord4(decision.selected);
|
|
14614
15391
|
const label = typeof selected?.label === "string" ? selected.label : typeof selected?.id === "string" ? selected.id : "unknown";
|
|
14615
15392
|
lines.push(`- [resolved] ${title} (${decisionId}) -> ${label}`);
|
|
14616
15393
|
}
|
|
@@ -14623,12 +15400,12 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14623
15400
|
title: prompt.title,
|
|
14624
15401
|
message: prompt.message
|
|
14625
15402
|
})),
|
|
14626
|
-
...Object.values(
|
|
15403
|
+
...Object.values(asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord4(asRecord4(job)?.prompts) || {})).map((prompt) => asRecord4(prompt)).filter(
|
|
14627
15404
|
(prompt) => Boolean(prompt && prompt.status === "open")
|
|
14628
15405
|
)
|
|
14629
15406
|
];
|
|
14630
15407
|
const visiblePrompts = boundary.reason === "request_start" ? openPrompts.filter((prompt) => {
|
|
14631
|
-
const promptRecord =
|
|
15408
|
+
const promptRecord = asRecord4(prompt);
|
|
14632
15409
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
14633
15410
|
const promptId = typeof promptRecord?.id === "string" ? promptRecord.id : typeof prompt.id === "string" ? prompt.id : null;
|
|
14634
15411
|
return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some(
|
|
@@ -14647,7 +15424,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14647
15424
|
}
|
|
14648
15425
|
}
|
|
14649
15426
|
const currentClosureId = getCurrentClosureId(liveDoc);
|
|
14650
|
-
const closureRecord = currentClosureId ?
|
|
15427
|
+
const closureRecord = currentClosureId ? asRecord4(asRecord4(loop?.closuresById)?.[currentClosureId]) : null;
|
|
14651
15428
|
const visibleClosure = closureRecord && (boundary.reason !== "request_start" || (Number(closureRecord.createdAt) || 0) >= boundary.timestamp) ? closureRecord : null;
|
|
14652
15429
|
lines.push("", "Loop Closure:");
|
|
14653
15430
|
if (visibleClosure) {
|
|
@@ -14660,10 +15437,10 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
|
|
|
14660
15437
|
return lines.join("\n");
|
|
14661
15438
|
}
|
|
14662
15439
|
function projectHeapSummary(heap, options) {
|
|
14663
|
-
const heapRecord =
|
|
14664
|
-
const entriesByPath =
|
|
14665
|
-
const listsByName =
|
|
14666
|
-
const variablesByName =
|
|
15440
|
+
const heapRecord = asRecord4(heap) || {};
|
|
15441
|
+
const entriesByPath = asRecord4(heapRecord.entriesByPath) || {};
|
|
15442
|
+
const listsByName = asRecord4(heapRecord.listsByName) || {};
|
|
15443
|
+
const variablesByName = asRecord4(heapRecord.variablesByName) || {};
|
|
14667
15444
|
const focusedVariableNames = new Set(
|
|
14668
15445
|
uniqueStrings(options?.focus?.variableNames || [])
|
|
14669
15446
|
);
|
|
@@ -14678,7 +15455,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14678
15455
|
const maxVariables = options?.maxVariables ?? (hasFocus ? 4 : 6);
|
|
14679
15456
|
const maxLists = options?.maxLists ?? (hasFocus ? 3 : 4);
|
|
14680
15457
|
const maxEntries = options?.maxEntries ?? (hasFocus ? 5 : 6);
|
|
14681
|
-
const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) =>
|
|
15458
|
+
const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).sort((left, right) => {
|
|
14682
15459
|
const leftFocused = left.name && focusedVariableNames.has(left.name) ? 1 : 0;
|
|
14683
15460
|
const rightFocused = right.name && focusedVariableNames.has(right.name) ? 1 : 0;
|
|
14684
15461
|
return rightFocused - leftFocused || (right.updatedAt || 0) - (left.updatedAt || 0);
|
|
@@ -14692,7 +15469,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14692
15469
|
for (const variable of variables) {
|
|
14693
15470
|
if (variable.entryPath) referencedPaths.add(variable.entryPath);
|
|
14694
15471
|
if (variable.listName) {
|
|
14695
|
-
const list =
|
|
15472
|
+
const list = asRecord4(
|
|
14696
15473
|
listsByName[variable.listName]
|
|
14697
15474
|
);
|
|
14698
15475
|
for (const path2 of list?.paths || []) referencedPaths.add(path2);
|
|
@@ -14701,10 +15478,10 @@ function projectHeapSummary(heap, options) {
|
|
|
14701
15478
|
for (const path2 of focusedEntryPaths) {
|
|
14702
15479
|
referencedPaths.add(path2);
|
|
14703
15480
|
}
|
|
14704
|
-
const visibleLists = Object.values(listsByName).map((value) =>
|
|
15481
|
+
const visibleLists = Object.values(listsByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter(
|
|
14705
15482
|
(list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))
|
|
14706
15483
|
).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
|
|
14707
|
-
const visibleEntries = Object.values(entriesByPath).map((value) =>
|
|
15484
|
+
const visibleEntries = Object.values(entriesByPath).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter((entry) => entry.path && referencedPaths.has(entry.path)).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxEntries);
|
|
14708
15485
|
const lines = [];
|
|
14709
15486
|
lines.push("Variables:");
|
|
14710
15487
|
if (variables.length === 0) {
|
|
@@ -14718,7 +15495,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14718
15495
|
continue;
|
|
14719
15496
|
}
|
|
14720
15497
|
if (variable.kind === "entry") {
|
|
14721
|
-
const entry = variable.entryPath ?
|
|
15498
|
+
const entry = variable.entryPath ? asRecord4(
|
|
14722
15499
|
entriesByPath[variable.entryPath]
|
|
14723
15500
|
) : null;
|
|
14724
15501
|
lines.push(
|
|
@@ -14726,7 +15503,7 @@ function projectHeapSummary(heap, options) {
|
|
|
14726
15503
|
);
|
|
14727
15504
|
continue;
|
|
14728
15505
|
}
|
|
14729
|
-
const list = variable.listName ?
|
|
15506
|
+
const list = variable.listName ? asRecord4(listsByName[variable.listName]) : null;
|
|
14730
15507
|
lines.push(
|
|
14731
15508
|
`- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`
|
|
14732
15509
|
);
|
|
@@ -14759,7 +15536,7 @@ function createHarnessVerifierSnapshot(input) {
|
|
|
14759
15536
|
input.projectionOptions
|
|
14760
15537
|
);
|
|
14761
15538
|
const heapDigest = hashString(
|
|
14762
|
-
projectHeapSummary(
|
|
15539
|
+
projectHeapSummary(asRecord4(input.liveDoc?.heap), {
|
|
14763
15540
|
focus: workflowFocus
|
|
14764
15541
|
})
|
|
14765
15542
|
) || "00000000";
|
|
@@ -15085,246 +15862,12 @@ ${loopBlock}
|
|
|
15085
15862
|
- Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
|
|
15086
15863
|
}
|
|
15087
15864
|
|
|
15088
|
-
// src/job-presentation.ts
|
|
15089
|
-
var RESPONSE_KEYS = [
|
|
15090
|
-
"reply",
|
|
15091
|
-
"response",
|
|
15092
|
-
"text",
|
|
15093
|
-
"message",
|
|
15094
|
-
"summary",
|
|
15095
|
-
"answer"
|
|
15096
|
-
];
|
|
15097
|
-
var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
|
|
15098
|
-
var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
|
|
15099
|
-
var LIST_KEY_CANDIDATES = ["listName"];
|
|
15100
|
-
var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
|
|
15101
|
-
var VARIABLE_KEY_CANDIDATES = ["variableName"];
|
|
15102
|
-
var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
|
|
15103
|
-
var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
|
|
15104
|
-
function asRecord3(value) {
|
|
15105
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15106
|
-
return value;
|
|
15107
|
-
}
|
|
15108
|
-
function normalizeText(value) {
|
|
15109
|
-
if (typeof value !== "string") return null;
|
|
15110
|
-
const trimmed = value.trim();
|
|
15111
|
-
if (!trimmed) return null;
|
|
15112
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
15113
|
-
return null;
|
|
15114
|
-
}
|
|
15115
|
-
return trimmed;
|
|
15116
|
-
}
|
|
15117
|
-
function humanTextFromStdout(stdout) {
|
|
15118
|
-
for (const line of [...stdout].reverse()) {
|
|
15119
|
-
const normalized = normalizeText(line);
|
|
15120
|
-
if (!normalized) continue;
|
|
15121
|
-
if (/^[A-Z_]+:/.test(normalized)) continue;
|
|
15122
|
-
return normalized;
|
|
15123
|
-
}
|
|
15124
|
-
return null;
|
|
15125
|
-
}
|
|
15126
|
-
function pushString(target, value) {
|
|
15127
|
-
if (typeof value === "string" && value.trim()) {
|
|
15128
|
-
target.add(value.trim());
|
|
15129
|
-
}
|
|
15130
|
-
}
|
|
15131
|
-
function pushStringArray(target, value) {
|
|
15132
|
-
if (!Array.isArray(value)) return;
|
|
15133
|
-
for (const item of value) {
|
|
15134
|
-
pushString(target, item);
|
|
15135
|
-
}
|
|
15136
|
-
}
|
|
15137
|
-
function collectReferencesFromRecord(record, refs) {
|
|
15138
|
-
for (const key of ENTRY_KEY_CANDIDATES)
|
|
15139
|
-
pushString(refs.entryPaths, record[key]);
|
|
15140
|
-
for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
|
|
15141
|
-
pushStringArray(refs.entryPaths, record[key]);
|
|
15142
|
-
for (const key of LIST_KEY_CANDIDATES)
|
|
15143
|
-
pushString(refs.listNames, record[key]);
|
|
15144
|
-
for (const key of LIST_ARRAY_KEY_CANDIDATES)
|
|
15145
|
-
pushStringArray(refs.listNames, record[key]);
|
|
15146
|
-
for (const key of VARIABLE_KEY_CANDIDATES)
|
|
15147
|
-
pushString(refs.variableNames, record[key]);
|
|
15148
|
-
for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
|
|
15149
|
-
pushStringArray(refs.variableNames, record[key]);
|
|
15150
|
-
}
|
|
15151
|
-
function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
15152
|
-
if (value === null || value === void 0 || depth > 4 || seen.has(value))
|
|
15153
|
-
return;
|
|
15154
|
-
if (typeof value === "string") {
|
|
15155
|
-
const trimmed = value.trim();
|
|
15156
|
-
if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
|
|
15157
|
-
if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
|
|
15158
|
-
if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
|
|
15159
|
-
return;
|
|
15160
|
-
}
|
|
15161
|
-
if (Array.isArray(value)) {
|
|
15162
|
-
seen.add(value);
|
|
15163
|
-
for (const item of value.slice(0, 24)) {
|
|
15164
|
-
scanForHeapReferences(item, heap, refs, depth + 1, seen);
|
|
15165
|
-
}
|
|
15166
|
-
return;
|
|
15167
|
-
}
|
|
15168
|
-
const record = asRecord3(value);
|
|
15169
|
-
if (!record) return;
|
|
15170
|
-
seen.add(value);
|
|
15171
|
-
collectReferencesFromRecord(record, refs);
|
|
15172
|
-
for (const key of UI_CONTAINER_KEYS) {
|
|
15173
|
-
const nested = asRecord3(record[key]);
|
|
15174
|
-
if (nested) collectReferencesFromRecord(nested, refs);
|
|
15175
|
-
}
|
|
15176
|
-
for (const nested of Object.values(record).slice(0, 24)) {
|
|
15177
|
-
scanForHeapReferences(nested, heap, refs, depth + 1, seen);
|
|
15178
|
-
}
|
|
15179
|
-
}
|
|
15180
|
-
function resolveVariablesToReferences(variableNames, heap, refs) {
|
|
15181
|
-
for (const variableName of variableNames) {
|
|
15182
|
-
const variable = heap.variablesByName?.[variableName];
|
|
15183
|
-
if (!variable) continue;
|
|
15184
|
-
if (variable.kind === "entry" && variable.entryPath) {
|
|
15185
|
-
refs.entryPaths.add(variable.entryPath);
|
|
15186
|
-
}
|
|
15187
|
-
if (variable.kind === "list" && variable.listName) {
|
|
15188
|
-
refs.listNames.add(variable.listName);
|
|
15189
|
-
}
|
|
15190
|
-
}
|
|
15191
|
-
}
|
|
15192
|
-
function sortEntries(entries) {
|
|
15193
|
-
return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
15194
|
-
}
|
|
15195
|
-
function sortLists(lists) {
|
|
15196
|
-
return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
15197
|
-
}
|
|
15198
|
-
function dedupeEntries(entries) {
|
|
15199
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15200
|
-
const result = [];
|
|
15201
|
-
for (const entry of entries) {
|
|
15202
|
-
if (!entry?.path || seen.has(entry.path)) continue;
|
|
15203
|
-
seen.add(entry.path);
|
|
15204
|
-
result.push(entry);
|
|
15205
|
-
}
|
|
15206
|
-
return result;
|
|
15207
|
-
}
|
|
15208
|
-
function dedupeLists(lists) {
|
|
15209
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15210
|
-
const result = [];
|
|
15211
|
-
for (const list of lists) {
|
|
15212
|
-
if (!list?.name || seen.has(list.name)) continue;
|
|
15213
|
-
seen.add(list.name);
|
|
15214
|
-
result.push(list);
|
|
15215
|
-
}
|
|
15216
|
-
return result;
|
|
15217
|
-
}
|
|
15218
|
-
function extractResponseText(result, stdout) {
|
|
15219
|
-
const directText = normalizeText(result);
|
|
15220
|
-
if (directText) return directText;
|
|
15221
|
-
const record = asRecord3(result);
|
|
15222
|
-
if (record) {
|
|
15223
|
-
for (const key of RESPONSE_KEYS) {
|
|
15224
|
-
const normalized = normalizeText(record[key]);
|
|
15225
|
-
if (normalized) return normalized;
|
|
15226
|
-
}
|
|
15227
|
-
for (const containerKey of UI_CONTAINER_KEYS) {
|
|
15228
|
-
const nested = asRecord3(record[containerKey]);
|
|
15229
|
-
if (!nested) continue;
|
|
15230
|
-
for (const key of RESPONSE_KEYS) {
|
|
15231
|
-
const normalized = normalizeText(nested[key]);
|
|
15232
|
-
if (normalized) return normalized;
|
|
15233
|
-
}
|
|
15234
|
-
}
|
|
15235
|
-
}
|
|
15236
|
-
return humanTextFromStdout(stdout);
|
|
15237
|
-
}
|
|
15238
|
-
function fallbackResponseText(entries, lists) {
|
|
15239
|
-
if (entries.length > 0) {
|
|
15240
|
-
return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
|
|
15241
|
-
}
|
|
15242
|
-
if (lists.length > 0) {
|
|
15243
|
-
const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
|
|
15244
|
-
if (emptyOnly) {
|
|
15245
|
-
return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
|
|
15246
|
-
}
|
|
15247
|
-
return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
|
|
15248
|
-
}
|
|
15249
|
-
return null;
|
|
15250
|
-
}
|
|
15251
|
-
function getJobRelatedEntries(heap, jobId) {
|
|
15252
|
-
return sortEntries(
|
|
15253
|
-
Object.values(heap.entriesByPath || {}).filter(
|
|
15254
|
-
(entry) => entry.relatedJobIds?.includes(jobId)
|
|
15255
|
-
)
|
|
15256
|
-
);
|
|
15257
|
-
}
|
|
15258
|
-
function getJobRelatedLists(heap, jobId) {
|
|
15259
|
-
return sortLists(
|
|
15260
|
-
Object.values(heap.listsByName || {}).filter(
|
|
15261
|
-
(list) => list.relatedJobIds?.includes(jobId)
|
|
15262
|
-
)
|
|
15263
|
-
);
|
|
15264
|
-
}
|
|
15265
|
-
function entriesFromLists(lists, heap) {
|
|
15266
|
-
const entries = [];
|
|
15267
|
-
for (const list of lists) {
|
|
15268
|
-
for (const path2 of list.paths || []) {
|
|
15269
|
-
const entry = heap.entriesByPath?.[path2];
|
|
15270
|
-
if (entry) entries.push(entry);
|
|
15271
|
-
}
|
|
15272
|
-
}
|
|
15273
|
-
return entries;
|
|
15274
|
-
}
|
|
15275
|
-
function resolveJobPresentation({
|
|
15276
|
-
jobId,
|
|
15277
|
-
result,
|
|
15278
|
-
stdout = [],
|
|
15279
|
-
sessionHeap,
|
|
15280
|
-
allowExplicitArtifacts = true
|
|
15281
|
-
}) {
|
|
15282
|
-
const refs = {
|
|
15283
|
-
entryPaths: /* @__PURE__ */ new Set(),
|
|
15284
|
-
listNames: /* @__PURE__ */ new Set(),
|
|
15285
|
-
variableNames: /* @__PURE__ */ new Set()
|
|
15286
|
-
};
|
|
15287
|
-
if (allowExplicitArtifacts) {
|
|
15288
|
-
scanForHeapReferences(result, sessionHeap, refs);
|
|
15289
|
-
resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
|
|
15290
|
-
}
|
|
15291
|
-
const referencedLists = sortLists(
|
|
15292
|
-
[...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
|
|
15293
|
-
);
|
|
15294
|
-
const referencedEntries = sortEntries(
|
|
15295
|
-
[...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
|
|
15296
|
-
);
|
|
15297
|
-
const jobLists = getJobRelatedLists(sessionHeap, jobId);
|
|
15298
|
-
const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
|
|
15299
|
-
const changedEntries = dedupeEntries([
|
|
15300
|
-
...jobEntries,
|
|
15301
|
-
...entriesFromLists(jobLists, sessionHeap)
|
|
15302
|
-
]);
|
|
15303
|
-
const explicitLists = dedupeLists(referencedLists);
|
|
15304
|
-
const explicitEntries = dedupeEntries([
|
|
15305
|
-
...referencedEntries,
|
|
15306
|
-
...entriesFromLists(referencedLists, sessionHeap)
|
|
15307
|
-
]);
|
|
15308
|
-
const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
|
|
15309
|
-
const lists = hasExplicitArtifacts ? explicitLists : jobLists;
|
|
15310
|
-
const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
|
|
15311
|
-
const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
|
|
15312
|
-
return {
|
|
15313
|
-
responseText,
|
|
15314
|
-
entries,
|
|
15315
|
-
lists,
|
|
15316
|
-
changedEntries,
|
|
15317
|
-
changedLists: jobLists,
|
|
15318
|
-
hasExplicitArtifacts
|
|
15319
|
-
};
|
|
15320
|
-
}
|
|
15321
|
-
|
|
15322
15865
|
// src/agent-evals.ts
|
|
15323
15866
|
var DEFAULT_CONTROLLER_BUDGETS = {
|
|
15324
15867
|
maxIterations: 6,
|
|
15325
15868
|
maxNoProgressIterations: 2
|
|
15326
15869
|
};
|
|
15327
|
-
function
|
|
15870
|
+
function asRecord5(value) {
|
|
15328
15871
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15329
15872
|
return value;
|
|
15330
15873
|
}
|
|
@@ -15372,7 +15915,7 @@ function buildArtifactDir(baseDir, suiteName = "granular-agent-evals") {
|
|
|
15372
15915
|
function createTimestampedArtifactDirectory(options) {
|
|
15373
15916
|
return buildArtifactDir(options?.baseDir, options?.suiteName);
|
|
15374
15917
|
}
|
|
15375
|
-
function
|
|
15918
|
+
function asArray3(value) {
|
|
15376
15919
|
if (!value) return [];
|
|
15377
15920
|
return Array.isArray(value) ? value : [value];
|
|
15378
15921
|
}
|
|
@@ -15390,7 +15933,7 @@ function buildScenarioSteps(scenario) {
|
|
|
15390
15933
|
request: scenario.request,
|
|
15391
15934
|
human: scenario.human,
|
|
15392
15935
|
expect: scenario.expect,
|
|
15393
|
-
inspect: [...
|
|
15936
|
+
inspect: [...asArray3(scenario.inspect), ...asArray3(scenario.verify)],
|
|
15394
15937
|
check: scenario.check,
|
|
15395
15938
|
maxIterations: scenario.maxIterations,
|
|
15396
15939
|
setup: {
|
|
@@ -15431,12 +15974,12 @@ function buildHistory(entries) {
|
|
|
15431
15974
|
);
|
|
15432
15975
|
}
|
|
15433
15976
|
function getOpenPromptsFromDoc(liveDoc) {
|
|
15434
|
-
const jobsById =
|
|
15977
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15435
15978
|
const prompts = [];
|
|
15436
15979
|
for (const job of Object.values(jobsById)) {
|
|
15437
|
-
const promptRecords =
|
|
15980
|
+
const promptRecords = asRecord5(asRecord5(job)?.prompts) || {};
|
|
15438
15981
|
for (const raw of Object.values(promptRecords)) {
|
|
15439
|
-
const record =
|
|
15982
|
+
const record = asRecord5(raw);
|
|
15440
15983
|
if (!record || record.status !== "open" || typeof record.promptId !== "string")
|
|
15441
15984
|
continue;
|
|
15442
15985
|
const prompt = normalizePrompt({
|
|
@@ -15457,11 +16000,11 @@ function getOpenPromptsFromDoc(liveDoc) {
|
|
|
15457
16000
|
return prompts;
|
|
15458
16001
|
}
|
|
15459
16002
|
function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
|
|
15460
|
-
const jobsById =
|
|
16003
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
15461
16004
|
return prompts.filter((prompt) => {
|
|
15462
16005
|
for (const jobRecord of Object.values(jobsById)) {
|
|
15463
|
-
const promptsById =
|
|
15464
|
-
const promptRecord =
|
|
16006
|
+
const promptsById = asRecord5(asRecord5(jobRecord)?.prompts) || {};
|
|
16007
|
+
const promptRecord = asRecord5(promptsById[prompt.id]);
|
|
15465
16008
|
const openedAt = Number(promptRecord?.openedAt) || 0;
|
|
15466
16009
|
if (openedAt >= boundaryTimestamp) return true;
|
|
15467
16010
|
}
|
|
@@ -15554,10 +16097,10 @@ ${modelOutputInstruction()}`
|
|
|
15554
16097
|
);
|
|
15555
16098
|
}
|
|
15556
16099
|
const raw = await response.json();
|
|
15557
|
-
const content =
|
|
15558
|
-
|
|
16100
|
+
const content = asRecord5(
|
|
16101
|
+
asRecord5(raw.choices?.[0])?.message
|
|
15559
16102
|
)?.content;
|
|
15560
|
-
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) =>
|
|
16103
|
+
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => asRecord5(part)?.text || "").join("") : "";
|
|
15561
16104
|
const parsed = extractJsonObject(text);
|
|
15562
16105
|
if (!parsed) {
|
|
15563
16106
|
if (attempt < 3) {
|
|
@@ -15609,17 +16152,17 @@ async function withTimeout(promise, ms, label) {
|
|
|
15609
16152
|
}
|
|
15610
16153
|
}
|
|
15611
16154
|
function getActionSummary(liveDoc, jobId) {
|
|
15612
|
-
const jobsById =
|
|
15613
|
-
const job =
|
|
16155
|
+
const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
|
|
16156
|
+
const job = asRecord5(jobsById[jobId]);
|
|
15614
16157
|
return Array.isArray(job?.actionSummary) ? job.actionSummary.filter(
|
|
15615
16158
|
(line) => typeof line === "string"
|
|
15616
16159
|
) : [];
|
|
15617
16160
|
}
|
|
15618
16161
|
function normalizeHeapSnapshot2(heap) {
|
|
15619
16162
|
return {
|
|
15620
|
-
entriesByPath:
|
|
15621
|
-
listsByName:
|
|
15622
|
-
variablesByName:
|
|
16163
|
+
entriesByPath: asRecord5(heap?.entriesByPath) || {},
|
|
16164
|
+
listsByName: asRecord5(heap?.listsByName) || {},
|
|
16165
|
+
variablesByName: asRecord5(heap?.variablesByName) || {},
|
|
15623
16166
|
updatedAt: typeof heap?.updatedAt === "number" ? heap.updatedAt : Date.now()
|
|
15624
16167
|
};
|
|
15625
16168
|
}
|
|
@@ -15805,8 +16348,8 @@ async function runAgentEvalSuite(options) {
|
|
|
15805
16348
|
);
|
|
15806
16349
|
}
|
|
15807
16350
|
const inspectionResults = [];
|
|
15808
|
-
const stepChecks =
|
|
15809
|
-
const stepInspections =
|
|
16351
|
+
const stepChecks = asArray3(step.check);
|
|
16352
|
+
const stepInspections = asArray3(step.inspect);
|
|
15810
16353
|
const context = {
|
|
15811
16354
|
conversation,
|
|
15812
16355
|
environment: conversation.environment,
|
|
@@ -15819,7 +16362,7 @@ async function runAgentEvalSuite(options) {
|
|
|
15819
16362
|
promptInteractions: completed.promptInteractions,
|
|
15820
16363
|
result: completed.result,
|
|
15821
16364
|
heap: normalizeHeapSnapshot2(
|
|
15822
|
-
|
|
16365
|
+
asRecord5(
|
|
15823
16366
|
cloneJson(conversation.environment.document)?.heap
|
|
15824
16367
|
)
|
|
15825
16368
|
),
|
|
@@ -16030,7 +16573,7 @@ function createAgentEvalHarness(options) {
|
|
|
16030
16573
|
}
|
|
16031
16574
|
function buildCheckContext(conversation, completed, turnDir) {
|
|
16032
16575
|
const liveDoc = cloneJson(conversation.environment.document);
|
|
16033
|
-
const heap = normalizeHeapSnapshot2(
|
|
16576
|
+
const heap = normalizeHeapSnapshot2(asRecord5(liveDoc?.heap));
|
|
16034
16577
|
return {
|
|
16035
16578
|
conversation,
|
|
16036
16579
|
environment: conversation.environment,
|
|
@@ -16106,7 +16649,7 @@ function createAgentEvalHarness(options) {
|
|
|
16106
16649
|
jobId: pending.job.id,
|
|
16107
16650
|
result: resumed.result,
|
|
16108
16651
|
stdout: [...pending.stdout, ...resumed.stdout],
|
|
16109
|
-
sessionHeap: normalizeHeapSnapshot2(
|
|
16652
|
+
sessionHeap: normalizeHeapSnapshot2(asRecord5(liveDoc?.heap))
|
|
16110
16653
|
});
|
|
16111
16654
|
const responseText = presentation.responseText || pending.finalReply || "Done.";
|
|
16112
16655
|
pending.conversation.history.push({
|
|
@@ -16316,7 +16859,7 @@ function createAgentEvalHarness(options) {
|
|
|
16316
16859
|
const settledLiveDoc = cloneJson(
|
|
16317
16860
|
conversation.environment.document
|
|
16318
16861
|
);
|
|
16319
|
-
const sessionHeap = normalizeHeapSnapshot2(
|
|
16862
|
+
const sessionHeap = normalizeHeapSnapshot2(asRecord5(settledLiveDoc?.heap));
|
|
16320
16863
|
const presentation = resolveJobPresentation({
|
|
16321
16864
|
jobId: job.id,
|
|
16322
16865
|
result: outcome.result,
|