@granular-software/sdk 0.4.31 → 0.4.33

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