@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.
@@ -3985,7 +3985,11 @@ var WSClient = class {
3985
3985
  return null;
3986
3986
  }
3987
3987
  try {
3988
- const payloadRaw = this.decodeBase64Url(parts[1]);
3988
+ const payloadSegment = parts[1];
3989
+ if (!payloadSegment) {
3990
+ return null;
3991
+ }
3992
+ const payloadRaw = this.decodeBase64Url(payloadSegment);
3989
3993
  const payload = JSON.parse(payloadRaw);
3990
3994
  if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) {
3991
3995
  return null;
@@ -5062,6 +5066,7 @@ import { ${allImports} } from "./sandbox-tools";
5062
5066
  this.eventListeners.set(event, []);
5063
5067
  }
5064
5068
  this.eventListeners.get(event).push(handler);
5069
+ return () => this.off(event, handler);
5065
5070
  }
5066
5071
  /**
5067
5072
  * Unsubscribe from session events
@@ -5518,6 +5523,16 @@ var JobImplementation = class {
5518
5523
  handler(message);
5519
5524
  }
5520
5525
  }
5526
+ return () => {
5527
+ const handlers = this.eventListeners.get(event);
5528
+ if (!handlers) {
5529
+ return;
5530
+ }
5531
+ this.eventListeners.set(
5532
+ event,
5533
+ handlers.filter((current) => current !== handler)
5534
+ );
5535
+ };
5521
5536
  }
5522
5537
  replayAgentMessage(message) {
5523
5538
  this.captureAgentMessage(message);
@@ -5604,6 +5619,540 @@ var JobImplementation = class {
5604
5619
  }
5605
5620
  };
5606
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
+
5607
6156
  // src/endpoints.ts
5608
6157
  var LOCAL_API_URL = "ws://localhost:8787/granular";
5609
6158
  var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
@@ -12064,7 +12613,9 @@ var Environment = class {
12064
12613
  }
12065
12614
  );
12066
12615
  if (result.errors?.length) {
12067
- throw new Error(`defineRelationship failed: ${result.errors[0].message}`);
12616
+ throw new Error(
12617
+ `defineRelationship failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
12618
+ );
12068
12619
  }
12069
12620
  return result.data.at.define_relationship;
12070
12621
  }
@@ -12100,7 +12651,9 @@ var Environment = class {
12100
12651
  { path: modelPath }
12101
12652
  );
12102
12653
  if (result.errors?.length) {
12103
- throw new Error(`getRelationships failed: ${result.errors[0].message}`);
12654
+ throw new Error(
12655
+ `getRelationships failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
12656
+ );
12104
12657
  }
12105
12658
  return result.data?.model?.relationships || [];
12106
12659
  }
@@ -12139,7 +12692,9 @@ var Environment = class {
12139
12692
  { target: targetPath }
12140
12693
  );
12141
12694
  if (result.errors?.length) {
12142
- throw new Error(`attach failed: ${result.errors[0].message}`);
12695
+ throw new Error(
12696
+ `attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
12697
+ );
12143
12698
  }
12144
12699
  }
12145
12700
  /**
@@ -12174,7 +12729,9 @@ var Environment = class {
12174
12729
  }`
12175
12730
  );
12176
12731
  if (result.errors?.length) {
12177
- throw new Error(`detach failed: ${result.errors[0].message}`);
12732
+ throw new Error(
12733
+ `detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
12734
+ );
12178
12735
  }
12179
12736
  }
12180
12737
  /**
@@ -12201,7 +12758,9 @@ var Environment = class {
12201
12758
  }`
12202
12759
  );
12203
12760
  if (result.errors?.length) {
12204
- throw new Error(`listRelated failed: ${result.errors[0].message}`);
12761
+ throw new Error(
12762
+ `listRelated failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
12763
+ );
12205
12764
  }
12206
12765
  return result.data?.at?.at?.list_related || [];
12207
12766
  }
@@ -12294,7 +12853,9 @@ var Environment = class {
12294
12853
  async _runGraphql(query, label) {
12295
12854
  const result = await this.graphql(query);
12296
12855
  if (result.errors?.length) {
12297
- throw new Error(`${label}: ${result.errors[0].message}`);
12856
+ throw new Error(
12857
+ `${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
12858
+ );
12298
12859
  }
12299
12860
  return result.data;
12300
12861
  }
@@ -12639,7 +13200,11 @@ var Environment = class {
12639
13200
  */
12640
13201
  async recordObject(options) {
12641
13202
  const results = await this.recordObjects([options]);
12642
- return results[0];
13203
+ const result = results[0];
13204
+ if (!result) {
13205
+ throw new Error("recordObject: no result returned for record");
13206
+ }
13207
+ return result;
12643
13208
  }
12644
13209
  /**
12645
13210
  * Batch version of `recordObject()`.
@@ -12691,7 +13256,13 @@ var Environment = class {
12691
13256
  );
12692
13257
  }
12693
13258
  for (let index = 0; index < items.length; index += 1) {
12694
- results[plan.offset + index] = items[index];
13259
+ const item = items[index];
13260
+ if (!item) {
13261
+ throw new Error(
13262
+ `recordObjects: chunk ${plan.chunkIndex + 1} returned an empty result at index ${index}`
13263
+ );
13264
+ }
13265
+ results[plan.offset + index] = item;
12695
13266
  }
12696
13267
  if (onChunk) {
12697
13268
  const info = {
@@ -12819,6 +13390,9 @@ var EnvironmentSession = class extends Session {
12819
13390
  get envName() {
12820
13391
  return this.environment.envName;
12821
13392
  }
13393
+ get tag() {
13394
+ return this.environment.tag;
13395
+ }
12822
13396
  get versionId() {
12823
13397
  return this.environment.versionId;
12824
13398
  }
@@ -12838,12 +13412,171 @@ var EnvironmentSession = class extends Session {
12838
13412
  return this.environment.feedback;
12839
13413
  }
12840
13414
  /**
12841
- * Return a plain JS snapshot of the synced session heap.
13415
+ * Return a plain JS copy of the synced session heap.
12842
13416
  */
12843
13417
  getHeap() {
12844
13418
  const doc = this.document;
12845
13419
  return normalizeHeapSnapshot(doc?.heap);
12846
13420
  }
13421
+ async sessionDataRequest(path2, query) {
13422
+ const searchParams = new URLSearchParams();
13423
+ for (const [key, value] of Object.entries(query || {})) {
13424
+ if (value !== null && typeof value !== "undefined" && value !== "") {
13425
+ searchParams.set(key, String(value));
13426
+ }
13427
+ }
13428
+ const queryString = searchParams.toString();
13429
+ const response = await fetch(
13430
+ `${this.environment.runtimeBaseUrl}/orchestrator/ws/sessions/${encodeURIComponent(this.sessionId)}${path2}${queryString ? `?${queryString}` : ""}`,
13431
+ {
13432
+ method: "GET",
13433
+ headers: {
13434
+ Authorization: `Bearer ${this.environment.authToken}`,
13435
+ "Content-Type": "application/json"
13436
+ }
13437
+ }
13438
+ );
13439
+ if (!response.ok) {
13440
+ const errorText = await response.text();
13441
+ throw new Error(
13442
+ `Session data API Error (${response.status}): ${errorText}`
13443
+ );
13444
+ }
13445
+ return response.json();
13446
+ }
13447
+ async collectAllSessionItems(listPage) {
13448
+ const items = [];
13449
+ let cursor = null;
13450
+ do {
13451
+ const page = await listPage({ limit: 500, cursor });
13452
+ items.push(...page.items);
13453
+ cursor = page.nextCursor;
13454
+ } while (cursor);
13455
+ return items;
13456
+ }
13457
+ /**
13458
+ * Fetch the live session document from the runtime DO.
13459
+ *
13460
+ * For history and saved artifacts, prefer the collection APIs on
13461
+ * `messages`, `timeline`, `jobs`, and `heap`.
13462
+ */
13463
+ async getDocument() {
13464
+ return this.sessionDataRequest("/document");
13465
+ }
13466
+ get messages() {
13467
+ return {
13468
+ list: (options = {}) => this.sessionDataRequest(
13469
+ "/messages",
13470
+ options
13471
+ )
13472
+ };
13473
+ }
13474
+ get timeline() {
13475
+ return {
13476
+ list: (options = {}) => this.sessionDataRequest(
13477
+ "/timeline",
13478
+ options
13479
+ )
13480
+ };
13481
+ }
13482
+ get jobs() {
13483
+ return {
13484
+ list: (options = {}) => this.sessionDataRequest(
13485
+ "/jobs",
13486
+ options
13487
+ ),
13488
+ get: (jobId) => this.sessionDataRequest(
13489
+ `/jobs/${encodeURIComponent(jobId)}`
13490
+ )
13491
+ };
13492
+ }
13493
+ get heap() {
13494
+ return {
13495
+ entries: {
13496
+ list: (options = {}) => this.sessionDataRequest(
13497
+ "/heap/entries",
13498
+ options
13499
+ ),
13500
+ get: (path2) => this.sessionDataRequest(
13501
+ `/heap/entries/${encodeURIComponent(path2)}`
13502
+ )
13503
+ },
13504
+ lists: {
13505
+ list: (options = {}) => this.sessionDataRequest(
13506
+ "/heap/lists",
13507
+ options
13508
+ ),
13509
+ get: (name) => this.sessionDataRequest(
13510
+ `/heap/lists/${encodeURIComponent(name)}`
13511
+ )
13512
+ }
13513
+ };
13514
+ }
13515
+ get transcript() {
13516
+ return {
13517
+ list: async (options = {}) => {
13518
+ const [messages, jobs, entries, lists] = await Promise.all([
13519
+ this.collectAllSessionItems(this.messages.list),
13520
+ this.collectAllSessionItems(
13521
+ (pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
13522
+ ),
13523
+ this.collectAllSessionItems(this.heap.entries.list),
13524
+ this.collectAllSessionItems(this.heap.lists.list)
13525
+ ]);
13526
+ const liveDoc = {
13527
+ conversation: { messages },
13528
+ jobs: {
13529
+ byId: Object.fromEntries(
13530
+ jobs.map((job) => {
13531
+ const record = job && typeof job === "object" ? job : null;
13532
+ const id = typeof record?.jobId === "string" ? record.jobId : typeof record?.id === "string" ? record.id : null;
13533
+ return id ? [id, record] : null;
13534
+ }).filter(
13535
+ (entry) => Boolean(entry)
13536
+ )
13537
+ )
13538
+ }
13539
+ };
13540
+ const heap = normalizeHeapSnapshot({
13541
+ entriesByPath: Object.fromEntries(
13542
+ entries.map((entry) => {
13543
+ return entry?.path ? [
13544
+ entry.path,
13545
+ entry
13546
+ ] : null;
13547
+ }).filter(
13548
+ (entry) => Boolean(entry)
13549
+ )
13550
+ ),
13551
+ listsByName: Object.fromEntries(
13552
+ lists.map((list) => {
13553
+ return list?.name ? [list.name, list] : null;
13554
+ }).filter(
13555
+ (entry) => Boolean(entry)
13556
+ )
13557
+ ),
13558
+ variablesByName: this.getHeap().variablesByName,
13559
+ updatedAt: Date.now()
13560
+ });
13561
+ const allItems = buildSessionTranscript({
13562
+ liveDoc,
13563
+ sessionHeap: heap
13564
+ });
13565
+ const limit = Math.max(
13566
+ 1,
13567
+ Math.min(500, Math.floor(options.limit ?? 100))
13568
+ );
13569
+ const offset = typeof options.cursor === "string" ? Number.parseInt(options.cursor, 10) || 0 : 0;
13570
+ const items = allItems.slice(offset, offset + limit);
13571
+ const nextOffset = offset + items.length;
13572
+ return {
13573
+ items,
13574
+ nextCursor: nextOffset < allItems.length ? String(nextOffset) : null,
13575
+ totalCount: allItems.length
13576
+ };
13577
+ }
13578
+ };
13579
+ }
12847
13580
  async graphql(query, variables) {
12848
13581
  return this.environment.graphql(query, variables);
12849
13582
  }
@@ -14053,15 +14786,15 @@ var Granular = class _Granular {
14053
14786
  };
14054
14787
 
14055
14788
  // src/agent-harness.ts
14056
- function asRecord2(value) {
14789
+ function asRecord4(value) {
14057
14790
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
14058
14791
  return value;
14059
14792
  }
14060
- function asArray(value) {
14793
+ function asArray2(value) {
14061
14794
  return Array.isArray(value) ? value : [];
14062
14795
  }
14063
14796
  function toSortedRecords(value) {
14064
- return Object.values(asRecord2(value) || {}).map((entry) => asRecord2(entry)).filter((entry) => Boolean(entry));
14797
+ return Object.values(asRecord4(value) || {}).map((entry) => asRecord4(entry)).filter((entry) => Boolean(entry));
14065
14798
  }
14066
14799
  function uniqueStrings(values, maxCount) {
14067
14800
  const seen = /* @__PURE__ */ new Set();
@@ -14087,7 +14820,7 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
14087
14820
  const headline = entry.label || entry.id || entry.path || "Unknown";
14088
14821
  const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
14089
14822
  const classLabel = entry.className || "unknown";
14090
- const preview = asArray(entry.fields).filter(
14823
+ const preview = asArray2(entry.fields).filter(
14091
14824
  (field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
14092
14825
  ).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
14093
14826
  return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
@@ -14224,14 +14957,14 @@ function normalizeActionSummaryForPrompt(line) {
14224
14957
  return line.replace(/\blimit=/g, "perPage=").replace(/\blimit:/g, "perPage:");
14225
14958
  }
14226
14959
  function getCurrentClosureId(liveDoc) {
14227
- const loop = asRecord2(liveDoc?.loop);
14960
+ const loop = asRecord4(liveDoc?.loop);
14228
14961
  return typeof loop?.currentClosureId === "string" ? loop.currentClosureId : null;
14229
14962
  }
14230
14963
  function getLatestClosure(liveDoc) {
14231
- const loop = asRecord2(liveDoc?.loop);
14964
+ const loop = asRecord4(liveDoc?.loop);
14232
14965
  const currentClosureId = getCurrentClosureId(liveDoc);
14233
- const closuresById = asRecord2(loop?.closuresById) || {};
14234
- const currentClosure = currentClosureId ? asRecord2(closuresById[currentClosureId]) : null;
14966
+ const closuresById = asRecord4(loop?.closuresById) || {};
14967
+ const currentClosure = currentClosureId ? asRecord4(closuresById[currentClosureId]) : null;
14235
14968
  if (currentClosure) {
14236
14969
  return {
14237
14970
  ...currentClosure,
@@ -14240,7 +14973,7 @@ function getLatestClosure(liveDoc) {
14240
14973
  }
14241
14974
  const closures = [];
14242
14975
  for (const [closureId, value] of Object.entries(closuresById)) {
14243
- const record = asRecord2(value);
14976
+ const record = asRecord4(value);
14244
14977
  if (!record) continue;
14245
14978
  closures.push({ ...record, closureId });
14246
14979
  }
@@ -14277,10 +15010,10 @@ function getJobTimestamp(job) {
14277
15010
  return Number(job.finishedAt) || Number(job.startedAt) || Number(job.submittedAt) || 0;
14278
15011
  }
14279
15012
  function getJobRecords(liveDoc) {
14280
- const jobsById = asRecord2(asRecord2(liveDoc?.jobs)?.byId) || {};
15013
+ const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
14281
15014
  const jobs = [];
14282
15015
  for (const [jobId, value] of Object.entries(jobsById)) {
14283
- const record = asRecord2(value);
15016
+ const record = asRecord4(value);
14284
15017
  if (!record) continue;
14285
15018
  jobs.push({ ...record, jobId });
14286
15019
  }
@@ -14290,7 +15023,7 @@ function getJobRecords(liveDoc) {
14290
15023
  return jobs;
14291
15024
  }
14292
15025
  function getPromptRecordsFromJobs(liveDoc) {
14293
- return getJobRecords(liveDoc).flatMap((job) => Object.values(asRecord2(job.prompts) || {})).map((prompt) => asRecord2(prompt)).filter((prompt) => Boolean(prompt));
15026
+ return getJobRecords(liveDoc).flatMap((job) => Object.values(asRecord4(job.prompts) || {})).map((prompt) => asRecord4(prompt)).filter((prompt) => Boolean(prompt));
14294
15027
  }
14295
15028
  function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
14296
15029
  const boundary = getWorkflowBoundary(liveDoc, options);
@@ -14305,15 +15038,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
14305
15038
  const openDecisionIds = [];
14306
15039
  const openPromptIds = [];
14307
15040
  for (const job of jobs) {
14308
- for (const line of asArray(job.actionSummary)) {
15041
+ for (const line of asArray2(job.actionSummary)) {
14309
15042
  if (typeof line === "string" && line.trim()) {
14310
15043
  actionSummaryLines.push(line.trim());
14311
15044
  }
14312
15045
  }
14313
- for (const rawEvent of asArray(job.actionTrace)) {
14314
- const event = asRecord2(rawEvent);
14315
- const details = asRecord2(event?.details);
14316
- const outcome = asRecord2(event?.outcome);
15046
+ for (const rawEvent of asArray2(job.actionTrace)) {
15047
+ const event = asRecord4(rawEvent);
15048
+ const details = asRecord4(event?.details);
15049
+ const outcome = asRecord4(event?.outcome);
14317
15050
  if (typeof details?.name === "string") {
14318
15051
  variableNames.push(details.name);
14319
15052
  }
@@ -14340,7 +15073,7 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
14340
15073
  }
14341
15074
  }
14342
15075
  }
14343
- const loop = asRecord2(liveDoc?.loop);
15076
+ const loop = asRecord4(liveDoc?.loop);
14344
15077
  const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
14345
15078
  const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
14346
15079
  const status = typeof task.status === "string" ? task.status : "pending";
@@ -14388,15 +15121,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
14388
15121
  openPromptIds.push(prompt.id);
14389
15122
  }
14390
15123
  }
14391
- const heap = asRecord2(liveDoc?.heap);
14392
- const variablesByName = asRecord2(heap?.variablesByName) || {};
14393
- const listsByName = asRecord2(heap?.listsByName) || {};
15124
+ const heap = asRecord4(liveDoc?.heap);
15125
+ const variablesByName = asRecord4(heap?.variablesByName) || {};
15126
+ const listsByName = asRecord4(heap?.listsByName) || {};
14394
15127
  const recentHints = extractFocusHintsFromActionSummary(actionSummaryLines);
14395
15128
  variableNames.push(...recentHints.variableNames);
14396
15129
  listNames.push(...recentHints.listNames);
14397
15130
  entryPaths.push(...recentHints.entryPaths);
14398
15131
  for (const variableName of uniqueStrings(variableNames)) {
14399
- const variable = asRecord2(variablesByName[variableName]);
15132
+ const variable = asRecord4(variablesByName[variableName]);
14400
15133
  if (!variable) continue;
14401
15134
  if (typeof variable.listName === "string") {
14402
15135
  listNames.push(variable.listName);
@@ -14406,13 +15139,13 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
14406
15139
  }
14407
15140
  }
14408
15141
  for (const listName of uniqueStrings(listNames)) {
14409
- const list = asRecord2(listsByName[listName]);
14410
- for (const path2 of asArray(list?.paths).slice(0, 4)) {
15142
+ const list = asRecord4(listsByName[listName]);
15143
+ for (const path2 of asArray2(list?.paths).slice(0, 4)) {
14411
15144
  entryPaths.push(path2);
14412
15145
  }
14413
15146
  }
14414
15147
  if (variableNames.length === 0 && boundary.reason !== "request_start") {
14415
- 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);
15148
+ const recentVariables = Object.values(variablesByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, 3);
14416
15149
  for (const variable of recentVariables) {
14417
15150
  if (typeof variable.name === "string") {
14418
15151
  variableNames.push(variable.name);
@@ -14495,12 +15228,12 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
14495
15228
  }
14496
15229
  function hasOpenPrompt(liveDoc, pendingPrompts) {
14497
15230
  if (pendingPrompts.length > 0) return true;
14498
- const jobsById = asRecord2(asRecord2(liveDoc?.jobs)?.byId) || {};
15231
+ const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
14499
15232
  for (const job of Object.values(jobsById)) {
14500
- const prompts = asRecord2(asRecord2(job)?.prompts);
15233
+ const prompts = asRecord4(asRecord4(job)?.prompts);
14501
15234
  if (!prompts) continue;
14502
15235
  for (const prompt of Object.values(prompts)) {
14503
- const record = asRecord2(prompt);
15236
+ const record = asRecord4(prompt);
14504
15237
  if (record?.status === "open") return true;
14505
15238
  }
14506
15239
  }
@@ -14508,7 +15241,7 @@ function hasOpenPrompt(liveDoc, pendingPrompts) {
14508
15241
  }
14509
15242
  function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14510
15243
  const lines = [];
14511
- const loop = asRecord2(liveDoc?.loop);
15244
+ const loop = asRecord4(liveDoc?.loop);
14512
15245
  const boundary = getWorkflowBoundary(liveDoc, options);
14513
15246
  const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
14514
15247
  const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
@@ -14568,8 +15301,8 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14568
15301
  const title = typeof decision.title === "string" && decision.title.trim() ? decision.title.trim() : "Decision";
14569
15302
  const decisionId = typeof decision.decisionId === "string" ? decision.decisionId : "unknown";
14570
15303
  if (status === "open") {
14571
- const candidatePreview = asArray(decision.candidates).slice(0, 3).map((candidate) => {
14572
- const record = asRecord2(candidate);
15304
+ const candidatePreview = asArray2(decision.candidates).slice(0, 3).map((candidate) => {
15305
+ const record = asRecord4(candidate);
14573
15306
  if (!record) return null;
14574
15307
  const candidateId = typeof record.id === "string" ? record.id : "unknown";
14575
15308
  const candidateLabel = typeof record.label === "string" && record.label.trim() ? record.label.trim() : candidateId;
@@ -14579,7 +15312,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14579
15312
  `- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`
14580
15313
  );
14581
15314
  } else {
14582
- const selected = asRecord2(decision.selected);
15315
+ const selected = asRecord4(decision.selected);
14583
15316
  const label = typeof selected?.label === "string" ? selected.label : typeof selected?.id === "string" ? selected.id : "unknown";
14584
15317
  lines.push(`- [resolved] ${title} (${decisionId}) -> ${label}`);
14585
15318
  }
@@ -14592,12 +15325,12 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14592
15325
  title: prompt.title,
14593
15326
  message: prompt.message
14594
15327
  })),
14595
- ...Object.values(asRecord2(asRecord2(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord2(asRecord2(job)?.prompts) || {})).map((prompt) => asRecord2(prompt)).filter(
15328
+ ...Object.values(asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord4(asRecord4(job)?.prompts) || {})).map((prompt) => asRecord4(prompt)).filter(
14596
15329
  (prompt) => Boolean(prompt && prompt.status === "open")
14597
15330
  )
14598
15331
  ];
14599
15332
  const visiblePrompts = boundary.reason === "request_start" ? openPrompts.filter((prompt) => {
14600
- const promptRecord = asRecord2(prompt);
15333
+ const promptRecord = asRecord4(prompt);
14601
15334
  const openedAt = Number(promptRecord?.openedAt) || 0;
14602
15335
  const promptId = typeof promptRecord?.id === "string" ? promptRecord.id : typeof prompt.id === "string" ? prompt.id : null;
14603
15336
  return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some(
@@ -14616,7 +15349,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14616
15349
  }
14617
15350
  }
14618
15351
  const currentClosureId = getCurrentClosureId(liveDoc);
14619
- const closureRecord = currentClosureId ? asRecord2(asRecord2(loop?.closuresById)?.[currentClosureId]) : null;
15352
+ const closureRecord = currentClosureId ? asRecord4(asRecord4(loop?.closuresById)?.[currentClosureId]) : null;
14620
15353
  const visibleClosure = closureRecord && (boundary.reason !== "request_start" || (Number(closureRecord.createdAt) || 0) >= boundary.timestamp) ? closureRecord : null;
14621
15354
  lines.push("", "Loop Closure:");
14622
15355
  if (visibleClosure) {
@@ -14629,10 +15362,10 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14629
15362
  return lines.join("\n");
14630
15363
  }
14631
15364
  function projectHeapSummary(heap, options) {
14632
- const heapRecord = asRecord2(heap) || {};
14633
- const entriesByPath = asRecord2(heapRecord.entriesByPath) || {};
14634
- const listsByName = asRecord2(heapRecord.listsByName) || {};
14635
- const variablesByName = asRecord2(heapRecord.variablesByName) || {};
15365
+ const heapRecord = asRecord4(heap) || {};
15366
+ const entriesByPath = asRecord4(heapRecord.entriesByPath) || {};
15367
+ const listsByName = asRecord4(heapRecord.listsByName) || {};
15368
+ const variablesByName = asRecord4(heapRecord.variablesByName) || {};
14636
15369
  const focusedVariableNames = new Set(
14637
15370
  uniqueStrings(options?.focus?.variableNames || [])
14638
15371
  );
@@ -14647,7 +15380,7 @@ function projectHeapSummary(heap, options) {
14647
15380
  const maxVariables = options?.maxVariables ?? (hasFocus ? 4 : 6);
14648
15381
  const maxLists = options?.maxLists ?? (hasFocus ? 3 : 4);
14649
15382
  const maxEntries = options?.maxEntries ?? (hasFocus ? 5 : 6);
14650
- const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) => asRecord2(value)).filter((value) => Boolean(value)).sort((left, right) => {
15383
+ const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).sort((left, right) => {
14651
15384
  const leftFocused = left.name && focusedVariableNames.has(left.name) ? 1 : 0;
14652
15385
  const rightFocused = right.name && focusedVariableNames.has(right.name) ? 1 : 0;
14653
15386
  return rightFocused - leftFocused || (right.updatedAt || 0) - (left.updatedAt || 0);
@@ -14661,7 +15394,7 @@ function projectHeapSummary(heap, options) {
14661
15394
  for (const variable of variables) {
14662
15395
  if (variable.entryPath) referencedPaths.add(variable.entryPath);
14663
15396
  if (variable.listName) {
14664
- const list = asRecord2(
15397
+ const list = asRecord4(
14665
15398
  listsByName[variable.listName]
14666
15399
  );
14667
15400
  for (const path2 of list?.paths || []) referencedPaths.add(path2);
@@ -14670,10 +15403,10 @@ function projectHeapSummary(heap, options) {
14670
15403
  for (const path2 of focusedEntryPaths) {
14671
15404
  referencedPaths.add(path2);
14672
15405
  }
14673
- const visibleLists = Object.values(listsByName).map((value) => asRecord2(value)).filter((value) => Boolean(value)).filter(
15406
+ const visibleLists = Object.values(listsByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter(
14674
15407
  (list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))
14675
15408
  ).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
14676
- 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);
15409
+ const visibleEntries = Object.values(entriesByPath).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter((entry) => entry.path && referencedPaths.has(entry.path)).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxEntries);
14677
15410
  const lines = [];
14678
15411
  lines.push("Variables:");
14679
15412
  if (variables.length === 0) {
@@ -14687,7 +15420,7 @@ function projectHeapSummary(heap, options) {
14687
15420
  continue;
14688
15421
  }
14689
15422
  if (variable.kind === "entry") {
14690
- const entry = variable.entryPath ? asRecord2(
15423
+ const entry = variable.entryPath ? asRecord4(
14691
15424
  entriesByPath[variable.entryPath]
14692
15425
  ) : null;
14693
15426
  lines.push(
@@ -14695,7 +15428,7 @@ function projectHeapSummary(heap, options) {
14695
15428
  );
14696
15429
  continue;
14697
15430
  }
14698
- const list = variable.listName ? asRecord2(listsByName[variable.listName]) : null;
15431
+ const list = variable.listName ? asRecord4(listsByName[variable.listName]) : null;
14699
15432
  lines.push(
14700
15433
  `- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`
14701
15434
  );
@@ -14728,7 +15461,7 @@ function createHarnessVerifierSnapshot(input) {
14728
15461
  input.projectionOptions
14729
15462
  );
14730
15463
  const heapDigest = hashString(
14731
- projectHeapSummary(asRecord2(input.liveDoc?.heap), {
15464
+ projectHeapSummary(asRecord4(input.liveDoc?.heap), {
14732
15465
  focus: workflowFocus
14733
15466
  })
14734
15467
  ) || "00000000";
@@ -15054,246 +15787,12 @@ ${loopBlock}
15054
15787
  - Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
15055
15788
  }
15056
15789
 
15057
- // src/job-presentation.ts
15058
- var RESPONSE_KEYS = [
15059
- "reply",
15060
- "response",
15061
- "text",
15062
- "message",
15063
- "summary",
15064
- "answer"
15065
- ];
15066
- var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
15067
- var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
15068
- var LIST_KEY_CANDIDATES = ["listName"];
15069
- var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
15070
- var VARIABLE_KEY_CANDIDATES = ["variableName"];
15071
- var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
15072
- var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
15073
- function asRecord3(value) {
15074
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
15075
- return value;
15076
- }
15077
- function normalizeText(value) {
15078
- if (typeof value !== "string") return null;
15079
- const trimmed = value.trim();
15080
- if (!trimmed) return null;
15081
- if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
15082
- return null;
15083
- }
15084
- return trimmed;
15085
- }
15086
- function humanTextFromStdout(stdout) {
15087
- for (const line of [...stdout].reverse()) {
15088
- const normalized = normalizeText(line);
15089
- if (!normalized) continue;
15090
- if (/^[A-Z_]+:/.test(normalized)) continue;
15091
- return normalized;
15092
- }
15093
- return null;
15094
- }
15095
- function pushString(target, value) {
15096
- if (typeof value === "string" && value.trim()) {
15097
- target.add(value.trim());
15098
- }
15099
- }
15100
- function pushStringArray(target, value) {
15101
- if (!Array.isArray(value)) return;
15102
- for (const item of value) {
15103
- pushString(target, item);
15104
- }
15105
- }
15106
- function collectReferencesFromRecord(record, refs) {
15107
- for (const key of ENTRY_KEY_CANDIDATES)
15108
- pushString(refs.entryPaths, record[key]);
15109
- for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
15110
- pushStringArray(refs.entryPaths, record[key]);
15111
- for (const key of LIST_KEY_CANDIDATES)
15112
- pushString(refs.listNames, record[key]);
15113
- for (const key of LIST_ARRAY_KEY_CANDIDATES)
15114
- pushStringArray(refs.listNames, record[key]);
15115
- for (const key of VARIABLE_KEY_CANDIDATES)
15116
- pushString(refs.variableNames, record[key]);
15117
- for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
15118
- pushStringArray(refs.variableNames, record[key]);
15119
- }
15120
- function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
15121
- if (value === null || value === void 0 || depth > 4 || seen.has(value))
15122
- return;
15123
- if (typeof value === "string") {
15124
- const trimmed = value.trim();
15125
- if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
15126
- if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
15127
- if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
15128
- return;
15129
- }
15130
- if (Array.isArray(value)) {
15131
- seen.add(value);
15132
- for (const item of value.slice(0, 24)) {
15133
- scanForHeapReferences(item, heap, refs, depth + 1, seen);
15134
- }
15135
- return;
15136
- }
15137
- const record = asRecord3(value);
15138
- if (!record) return;
15139
- seen.add(value);
15140
- collectReferencesFromRecord(record, refs);
15141
- for (const key of UI_CONTAINER_KEYS) {
15142
- const nested = asRecord3(record[key]);
15143
- if (nested) collectReferencesFromRecord(nested, refs);
15144
- }
15145
- for (const nested of Object.values(record).slice(0, 24)) {
15146
- scanForHeapReferences(nested, heap, refs, depth + 1, seen);
15147
- }
15148
- }
15149
- function resolveVariablesToReferences(variableNames, heap, refs) {
15150
- for (const variableName of variableNames) {
15151
- const variable = heap.variablesByName?.[variableName];
15152
- if (!variable) continue;
15153
- if (variable.kind === "entry" && variable.entryPath) {
15154
- refs.entryPaths.add(variable.entryPath);
15155
- }
15156
- if (variable.kind === "list" && variable.listName) {
15157
- refs.listNames.add(variable.listName);
15158
- }
15159
- }
15160
- }
15161
- function sortEntries(entries) {
15162
- return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
15163
- }
15164
- function sortLists(lists) {
15165
- return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
15166
- }
15167
- function dedupeEntries(entries) {
15168
- const seen = /* @__PURE__ */ new Set();
15169
- const result = [];
15170
- for (const entry of entries) {
15171
- if (!entry?.path || seen.has(entry.path)) continue;
15172
- seen.add(entry.path);
15173
- result.push(entry);
15174
- }
15175
- return result;
15176
- }
15177
- function dedupeLists(lists) {
15178
- const seen = /* @__PURE__ */ new Set();
15179
- const result = [];
15180
- for (const list of lists) {
15181
- if (!list?.name || seen.has(list.name)) continue;
15182
- seen.add(list.name);
15183
- result.push(list);
15184
- }
15185
- return result;
15186
- }
15187
- function extractResponseText(result, stdout) {
15188
- const directText = normalizeText(result);
15189
- if (directText) return directText;
15190
- const record = asRecord3(result);
15191
- if (record) {
15192
- for (const key of RESPONSE_KEYS) {
15193
- const normalized = normalizeText(record[key]);
15194
- if (normalized) return normalized;
15195
- }
15196
- for (const containerKey of UI_CONTAINER_KEYS) {
15197
- const nested = asRecord3(record[containerKey]);
15198
- if (!nested) continue;
15199
- for (const key of RESPONSE_KEYS) {
15200
- const normalized = normalizeText(nested[key]);
15201
- if (normalized) return normalized;
15202
- }
15203
- }
15204
- }
15205
- return humanTextFromStdout(stdout);
15206
- }
15207
- function fallbackResponseText(entries, lists) {
15208
- if (entries.length > 0) {
15209
- return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
15210
- }
15211
- if (lists.length > 0) {
15212
- const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
15213
- if (emptyOnly) {
15214
- return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
15215
- }
15216
- return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
15217
- }
15218
- return null;
15219
- }
15220
- function getJobRelatedEntries(heap, jobId) {
15221
- return sortEntries(
15222
- Object.values(heap.entriesByPath || {}).filter(
15223
- (entry) => entry.relatedJobIds?.includes(jobId)
15224
- )
15225
- );
15226
- }
15227
- function getJobRelatedLists(heap, jobId) {
15228
- return sortLists(
15229
- Object.values(heap.listsByName || {}).filter(
15230
- (list) => list.relatedJobIds?.includes(jobId)
15231
- )
15232
- );
15233
- }
15234
- function entriesFromLists(lists, heap) {
15235
- const entries = [];
15236
- for (const list of lists) {
15237
- for (const path2 of list.paths || []) {
15238
- const entry = heap.entriesByPath?.[path2];
15239
- if (entry) entries.push(entry);
15240
- }
15241
- }
15242
- return entries;
15243
- }
15244
- function resolveJobPresentation({
15245
- jobId,
15246
- result,
15247
- stdout = [],
15248
- sessionHeap,
15249
- allowExplicitArtifacts = true
15250
- }) {
15251
- const refs = {
15252
- entryPaths: /* @__PURE__ */ new Set(),
15253
- listNames: /* @__PURE__ */ new Set(),
15254
- variableNames: /* @__PURE__ */ new Set()
15255
- };
15256
- if (allowExplicitArtifacts) {
15257
- scanForHeapReferences(result, sessionHeap, refs);
15258
- resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
15259
- }
15260
- const referencedLists = sortLists(
15261
- [...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
15262
- );
15263
- const referencedEntries = sortEntries(
15264
- [...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
15265
- );
15266
- const jobLists = getJobRelatedLists(sessionHeap, jobId);
15267
- const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
15268
- const changedEntries = dedupeEntries([
15269
- ...jobEntries,
15270
- ...entriesFromLists(jobLists, sessionHeap)
15271
- ]);
15272
- const explicitLists = dedupeLists(referencedLists);
15273
- const explicitEntries = dedupeEntries([
15274
- ...referencedEntries,
15275
- ...entriesFromLists(referencedLists, sessionHeap)
15276
- ]);
15277
- const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
15278
- const lists = hasExplicitArtifacts ? explicitLists : jobLists;
15279
- const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
15280
- const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
15281
- return {
15282
- responseText,
15283
- entries,
15284
- lists,
15285
- changedEntries,
15286
- changedLists: jobLists,
15287
- hasExplicitArtifacts
15288
- };
15289
- }
15290
-
15291
15790
  // src/agent-evals.ts
15292
15791
  var DEFAULT_CONTROLLER_BUDGETS = {
15293
15792
  maxIterations: 6,
15294
15793
  maxNoProgressIterations: 2
15295
15794
  };
15296
- function asRecord4(value) {
15795
+ function asRecord5(value) {
15297
15796
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
15298
15797
  return value;
15299
15798
  }
@@ -15341,7 +15840,7 @@ function buildArtifactDir(baseDir, suiteName = "granular-agent-evals") {
15341
15840
  function createTimestampedArtifactDirectory(options) {
15342
15841
  return buildArtifactDir(options?.baseDir, options?.suiteName);
15343
15842
  }
15344
- function asArray2(value) {
15843
+ function asArray3(value) {
15345
15844
  if (!value) return [];
15346
15845
  return Array.isArray(value) ? value : [value];
15347
15846
  }
@@ -15359,7 +15858,7 @@ function buildScenarioSteps(scenario) {
15359
15858
  request: scenario.request,
15360
15859
  human: scenario.human,
15361
15860
  expect: scenario.expect,
15362
- inspect: [...asArray2(scenario.inspect), ...asArray2(scenario.verify)],
15861
+ inspect: [...asArray3(scenario.inspect), ...asArray3(scenario.verify)],
15363
15862
  check: scenario.check,
15364
15863
  maxIterations: scenario.maxIterations,
15365
15864
  setup: {
@@ -15400,12 +15899,12 @@ function buildHistory(entries) {
15400
15899
  );
15401
15900
  }
15402
15901
  function getOpenPromptsFromDoc(liveDoc) {
15403
- const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
15902
+ const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
15404
15903
  const prompts = [];
15405
15904
  for (const job of Object.values(jobsById)) {
15406
- const promptRecords = asRecord4(asRecord4(job)?.prompts) || {};
15905
+ const promptRecords = asRecord5(asRecord5(job)?.prompts) || {};
15407
15906
  for (const raw of Object.values(promptRecords)) {
15408
- const record = asRecord4(raw);
15907
+ const record = asRecord5(raw);
15409
15908
  if (!record || record.status !== "open" || typeof record.promptId !== "string")
15410
15909
  continue;
15411
15910
  const prompt = normalizePrompt({
@@ -15426,11 +15925,11 @@ function getOpenPromptsFromDoc(liveDoc) {
15426
15925
  return prompts;
15427
15926
  }
15428
15927
  function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
15429
- const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
15928
+ const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
15430
15929
  return prompts.filter((prompt) => {
15431
15930
  for (const jobRecord of Object.values(jobsById)) {
15432
- const promptsById = asRecord4(asRecord4(jobRecord)?.prompts) || {};
15433
- const promptRecord = asRecord4(promptsById[prompt.id]);
15931
+ const promptsById = asRecord5(asRecord5(jobRecord)?.prompts) || {};
15932
+ const promptRecord = asRecord5(promptsById[prompt.id]);
15434
15933
  const openedAt = Number(promptRecord?.openedAt) || 0;
15435
15934
  if (openedAt >= boundaryTimestamp) return true;
15436
15935
  }
@@ -15523,10 +16022,10 @@ ${modelOutputInstruction()}`
15523
16022
  );
15524
16023
  }
15525
16024
  const raw = await response.json();
15526
- const content = asRecord4(
15527
- asRecord4(raw.choices?.[0])?.message
16025
+ const content = asRecord5(
16026
+ asRecord5(raw.choices?.[0])?.message
15528
16027
  )?.content;
15529
- const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => asRecord4(part)?.text || "").join("") : "";
16028
+ const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => asRecord5(part)?.text || "").join("") : "";
15530
16029
  const parsed = extractJsonObject(text);
15531
16030
  if (!parsed) {
15532
16031
  if (attempt < 3) {
@@ -15578,17 +16077,17 @@ async function withTimeout(promise, ms, label) {
15578
16077
  }
15579
16078
  }
15580
16079
  function getActionSummary(liveDoc, jobId) {
15581
- const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
15582
- const job = asRecord4(jobsById[jobId]);
16080
+ const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
16081
+ const job = asRecord5(jobsById[jobId]);
15583
16082
  return Array.isArray(job?.actionSummary) ? job.actionSummary.filter(
15584
16083
  (line) => typeof line === "string"
15585
16084
  ) : [];
15586
16085
  }
15587
16086
  function normalizeHeapSnapshot2(heap) {
15588
16087
  return {
15589
- entriesByPath: asRecord4(heap?.entriesByPath) || {},
15590
- listsByName: asRecord4(heap?.listsByName) || {},
15591
- variablesByName: asRecord4(heap?.variablesByName) || {},
16088
+ entriesByPath: asRecord5(heap?.entriesByPath) || {},
16089
+ listsByName: asRecord5(heap?.listsByName) || {},
16090
+ variablesByName: asRecord5(heap?.variablesByName) || {},
15592
16091
  updatedAt: typeof heap?.updatedAt === "number" ? heap.updatedAt : Date.now()
15593
16092
  };
15594
16093
  }
@@ -15774,8 +16273,8 @@ async function runAgentEvalSuite(options) {
15774
16273
  );
15775
16274
  }
15776
16275
  const inspectionResults = [];
15777
- const stepChecks = asArray2(step.check);
15778
- const stepInspections = asArray2(step.inspect);
16276
+ const stepChecks = asArray3(step.check);
16277
+ const stepInspections = asArray3(step.inspect);
15779
16278
  const context = {
15780
16279
  conversation,
15781
16280
  environment: conversation.environment,
@@ -15788,7 +16287,7 @@ async function runAgentEvalSuite(options) {
15788
16287
  promptInteractions: completed.promptInteractions,
15789
16288
  result: completed.result,
15790
16289
  heap: normalizeHeapSnapshot2(
15791
- asRecord4(
16290
+ asRecord5(
15792
16291
  cloneJson(conversation.environment.document)?.heap
15793
16292
  )
15794
16293
  ),
@@ -15999,7 +16498,7 @@ function createAgentEvalHarness(options) {
15999
16498
  }
16000
16499
  function buildCheckContext(conversation, completed, turnDir) {
16001
16500
  const liveDoc = cloneJson(conversation.environment.document);
16002
- const heap = normalizeHeapSnapshot2(asRecord4(liveDoc?.heap));
16501
+ const heap = normalizeHeapSnapshot2(asRecord5(liveDoc?.heap));
16003
16502
  return {
16004
16503
  conversation,
16005
16504
  environment: conversation.environment,
@@ -16075,7 +16574,7 @@ function createAgentEvalHarness(options) {
16075
16574
  jobId: pending.job.id,
16076
16575
  result: resumed.result,
16077
16576
  stdout: [...pending.stdout, ...resumed.stdout],
16078
- sessionHeap: normalizeHeapSnapshot2(asRecord4(liveDoc?.heap))
16577
+ sessionHeap: normalizeHeapSnapshot2(asRecord5(liveDoc?.heap))
16079
16578
  });
16080
16579
  const responseText = presentation.responseText || pending.finalReply || "Done.";
16081
16580
  pending.conversation.history.push({
@@ -16285,7 +16784,7 @@ function createAgentEvalHarness(options) {
16285
16784
  const settledLiveDoc = cloneJson(
16286
16785
  conversation.environment.document
16287
16786
  );
16288
- const sessionHeap = normalizeHeapSnapshot2(asRecord4(settledLiveDoc?.heap));
16787
+ const sessionHeap = normalizeHeapSnapshot2(asRecord5(settledLiveDoc?.heap));
16289
16788
  const presentation = resolveJobPresentation({
16290
16789
  jobId: job.id,
16291
16790
  result: outcome.result,