@granular-software/sdk 0.4.32 → 0.4.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5644,6 +5644,540 @@ var JobImplementation = class {
5644
5644
  }
5645
5645
  };
5646
5646
 
5647
+ // src/job-presentation.ts
5648
+ var RESPONSE_KEYS = [
5649
+ "reply",
5650
+ "response",
5651
+ "text",
5652
+ "message",
5653
+ "summary",
5654
+ "answer"
5655
+ ];
5656
+ var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
5657
+ var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
5658
+ var LIST_KEY_CANDIDATES = ["listName"];
5659
+ var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
5660
+ var VARIABLE_KEY_CANDIDATES = ["variableName"];
5661
+ var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
5662
+ var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
5663
+ function asRecord2(value) {
5664
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
5665
+ return value;
5666
+ }
5667
+ function normalizeText(value) {
5668
+ if (typeof value !== "string") return null;
5669
+ const trimmed = value.trim();
5670
+ if (!trimmed) return null;
5671
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
5672
+ return null;
5673
+ }
5674
+ return trimmed;
5675
+ }
5676
+ function humanTextFromStdout(stdout) {
5677
+ for (const line of [...stdout].reverse()) {
5678
+ const normalized = normalizeText(line);
5679
+ if (!normalized) continue;
5680
+ if (/^[A-Z_]+:/.test(normalized)) continue;
5681
+ return normalized;
5682
+ }
5683
+ return null;
5684
+ }
5685
+ function pushString(target, value) {
5686
+ if (typeof value === "string" && value.trim()) {
5687
+ target.add(value.trim());
5688
+ }
5689
+ }
5690
+ function pushStringArray(target, value) {
5691
+ if (!Array.isArray(value)) return;
5692
+ for (const item of value) {
5693
+ pushString(target, item);
5694
+ }
5695
+ }
5696
+ function collectReferencesFromRecord(record, refs) {
5697
+ for (const key of ENTRY_KEY_CANDIDATES)
5698
+ pushString(refs.entryPaths, record[key]);
5699
+ for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
5700
+ pushStringArray(refs.entryPaths, record[key]);
5701
+ for (const key of LIST_KEY_CANDIDATES)
5702
+ pushString(refs.listNames, record[key]);
5703
+ for (const key of LIST_ARRAY_KEY_CANDIDATES)
5704
+ pushStringArray(refs.listNames, record[key]);
5705
+ for (const key of VARIABLE_KEY_CANDIDATES)
5706
+ pushString(refs.variableNames, record[key]);
5707
+ for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
5708
+ pushStringArray(refs.variableNames, record[key]);
5709
+ }
5710
+ function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
5711
+ if (value === null || value === void 0 || depth > 4 || seen.has(value))
5712
+ return;
5713
+ if (typeof value === "string") {
5714
+ const trimmed = value.trim();
5715
+ if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
5716
+ if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
5717
+ if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
5718
+ return;
5719
+ }
5720
+ if (Array.isArray(value)) {
5721
+ seen.add(value);
5722
+ for (const item of value.slice(0, 24)) {
5723
+ scanForHeapReferences(item, heap, refs, depth + 1, seen);
5724
+ }
5725
+ return;
5726
+ }
5727
+ const record = asRecord2(value);
5728
+ if (!record) return;
5729
+ seen.add(value);
5730
+ collectReferencesFromRecord(record, refs);
5731
+ for (const key of UI_CONTAINER_KEYS) {
5732
+ const nested = asRecord2(record[key]);
5733
+ if (nested) collectReferencesFromRecord(nested, refs);
5734
+ }
5735
+ for (const nested of Object.values(record).slice(0, 24)) {
5736
+ scanForHeapReferences(nested, heap, refs, depth + 1, seen);
5737
+ }
5738
+ }
5739
+ function resolveVariablesToReferences(variableNames, heap, refs) {
5740
+ for (const variableName of variableNames) {
5741
+ const variable = heap.variablesByName?.[variableName];
5742
+ if (!variable) continue;
5743
+ if (variable.kind === "entry" && variable.entryPath) {
5744
+ refs.entryPaths.add(variable.entryPath);
5745
+ }
5746
+ if (variable.kind === "list" && variable.listName) {
5747
+ refs.listNames.add(variable.listName);
5748
+ }
5749
+ }
5750
+ }
5751
+ function sortEntries(entries) {
5752
+ return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
5753
+ }
5754
+ function sortLists(lists) {
5755
+ return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
5756
+ }
5757
+ function dedupeEntries(entries) {
5758
+ const seen = /* @__PURE__ */ new Set();
5759
+ const result = [];
5760
+ for (const entry of entries) {
5761
+ if (!entry?.path || seen.has(entry.path)) continue;
5762
+ seen.add(entry.path);
5763
+ result.push(entry);
5764
+ }
5765
+ return result;
5766
+ }
5767
+ function dedupeLists(lists) {
5768
+ const seen = /* @__PURE__ */ new Set();
5769
+ const result = [];
5770
+ for (const list of lists) {
5771
+ if (!list?.name || seen.has(list.name)) continue;
5772
+ seen.add(list.name);
5773
+ result.push(list);
5774
+ }
5775
+ return result;
5776
+ }
5777
+ function extractResponseText(result, stdout) {
5778
+ const directText = normalizeText(result);
5779
+ if (directText) return directText;
5780
+ const record = asRecord2(result);
5781
+ if (record) {
5782
+ for (const key of RESPONSE_KEYS) {
5783
+ const normalized = normalizeText(record[key]);
5784
+ if (normalized) return normalized;
5785
+ }
5786
+ for (const containerKey of UI_CONTAINER_KEYS) {
5787
+ const nested = asRecord2(record[containerKey]);
5788
+ if (!nested) continue;
5789
+ for (const key of RESPONSE_KEYS) {
5790
+ const normalized = normalizeText(nested[key]);
5791
+ if (normalized) return normalized;
5792
+ }
5793
+ }
5794
+ }
5795
+ return humanTextFromStdout(stdout);
5796
+ }
5797
+ function fallbackResponseText(entries, lists) {
5798
+ if (entries.length > 0) {
5799
+ return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
5800
+ }
5801
+ if (lists.length > 0) {
5802
+ const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
5803
+ if (emptyOnly) {
5804
+ return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
5805
+ }
5806
+ return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
5807
+ }
5808
+ return null;
5809
+ }
5810
+ function getJobRelatedEntries(heap, jobId) {
5811
+ return sortEntries(
5812
+ Object.values(heap.entriesByPath || {}).filter(
5813
+ (entry) => entry.relatedJobIds?.includes(jobId)
5814
+ )
5815
+ );
5816
+ }
5817
+ function getJobRelatedLists(heap, jobId) {
5818
+ return sortLists(
5819
+ Object.values(heap.listsByName || {}).filter(
5820
+ (list) => list.relatedJobIds?.includes(jobId)
5821
+ )
5822
+ );
5823
+ }
5824
+ function entriesFromLists(lists, heap) {
5825
+ const entries = [];
5826
+ for (const list of lists) {
5827
+ for (const path2 of list.paths || []) {
5828
+ const entry = heap.entriesByPath?.[path2];
5829
+ if (entry) entries.push(entry);
5830
+ }
5831
+ }
5832
+ return entries;
5833
+ }
5834
+ function resolveJobPresentation({
5835
+ jobId,
5836
+ result,
5837
+ stdout = [],
5838
+ sessionHeap,
5839
+ allowExplicitArtifacts = true
5840
+ }) {
5841
+ const refs = {
5842
+ entryPaths: /* @__PURE__ */ new Set(),
5843
+ listNames: /* @__PURE__ */ new Set(),
5844
+ variableNames: /* @__PURE__ */ new Set()
5845
+ };
5846
+ if (allowExplicitArtifacts) {
5847
+ scanForHeapReferences(result, sessionHeap, refs);
5848
+ resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
5849
+ }
5850
+ const referencedLists = sortLists(
5851
+ [...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
5852
+ );
5853
+ const referencedEntries = sortEntries(
5854
+ [...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
5855
+ );
5856
+ const jobLists = getJobRelatedLists(sessionHeap, jobId);
5857
+ const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
5858
+ const changedEntries = dedupeEntries([
5859
+ ...jobEntries,
5860
+ ...entriesFromLists(jobLists, sessionHeap)
5861
+ ]);
5862
+ const explicitLists = dedupeLists(referencedLists);
5863
+ const explicitEntries = dedupeEntries([
5864
+ ...referencedEntries,
5865
+ ...entriesFromLists(referencedLists, sessionHeap)
5866
+ ]);
5867
+ const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
5868
+ const lists = hasExplicitArtifacts ? explicitLists : jobLists;
5869
+ const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
5870
+ const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
5871
+ return {
5872
+ responseText,
5873
+ entries,
5874
+ lists,
5875
+ changedEntries,
5876
+ changedLists: jobLists,
5877
+ hasExplicitArtifacts
5878
+ };
5879
+ }
5880
+
5881
+ // src/session-transcript.ts
5882
+ var EMPTY_HEAP = {
5883
+ entriesByPath: {},
5884
+ listsByName: {},
5885
+ variablesByName: {}};
5886
+ function asRecord3(value) {
5887
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
5888
+ return value;
5889
+ }
5890
+ function asArray(value) {
5891
+ return Array.isArray(value) ? value : [];
5892
+ }
5893
+ function asNumber(value) {
5894
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
5895
+ }
5896
+ function asString(value) {
5897
+ return typeof value === "string" ? value : void 0;
5898
+ }
5899
+ function trimString(value) {
5900
+ return typeof value === "string" ? value.trim() : "";
5901
+ }
5902
+ function normalizeShowRefs(value) {
5903
+ const record = asRecord3(value);
5904
+ if (!record) return void 0;
5905
+ const normalizeRefs = (input) => {
5906
+ if (!Array.isArray(input)) return void 0;
5907
+ const refs = Array.from(
5908
+ new Set(
5909
+ input.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean)
5910
+ )
5911
+ );
5912
+ return refs.length > 0 ? refs : void 0;
5913
+ };
5914
+ const show = {
5915
+ entryPaths: normalizeRefs(record.entryPaths),
5916
+ listNames: normalizeRefs(record.listNames),
5917
+ variableNames: normalizeRefs(record.variableNames)
5918
+ };
5919
+ return show.entryPaths || show.listNames || show.variableNames ? show : void 0;
5920
+ }
5921
+ function stringifyTranscriptValue(value, fallback = "") {
5922
+ if (typeof value === "string") {
5923
+ return value.trim() || fallback;
5924
+ }
5925
+ if (typeof value === "boolean") {
5926
+ return value ? "Confirmed" : "Canceled";
5927
+ }
5928
+ if (value === void 0) {
5929
+ return fallback;
5930
+ }
5931
+ try {
5932
+ const json = JSON.stringify(value, null, 2);
5933
+ if (!json || json === "undefined") return fallback;
5934
+ return json.length > 2e3 ? `${json.slice(0, 2e3)}...` : json;
5935
+ } catch {
5936
+ return String(value);
5937
+ }
5938
+ }
5939
+ function buildArtifactHistory(show) {
5940
+ if (!show) return void 0;
5941
+ return `[Agent message]
5942
+ ${stringifyTranscriptValue({ show }, "")}`;
5943
+ }
5944
+ function normalizeConversationMessage(raw) {
5945
+ const record = asRecord3(raw);
5946
+ if (!record) return null;
5947
+ const role = record.role === "user" ? "user" : record.role === "assistant" ? "assistant" : null;
5948
+ if (!role) return null;
5949
+ const content = trimString(
5950
+ record.content ?? record.reply ?? record.message ?? record.text
5951
+ );
5952
+ const show = normalizeShowRefs(record.show);
5953
+ const id = asString(record.id) || crypto.randomUUID();
5954
+ const timestamp = asNumber(record.timestamp) || asNumber(record.ts) || 0;
5955
+ if (!content && !show) return null;
5956
+ return {
5957
+ id,
5958
+ role,
5959
+ content,
5960
+ timestamp,
5961
+ jobId: asString(record.jobId),
5962
+ promptId: asString(record.promptId),
5963
+ show,
5964
+ historyContent: role === "assistant" ? content ? `[Assistant reply]
5965
+ ${content}` : buildArtifactHistory(show) : void 0,
5966
+ source: "conversation"
5967
+ };
5968
+ }
5969
+ function normalizePromptEntries(jobId, rawPrompts, conversationPromptIds) {
5970
+ const promptsById = asRecord3(rawPrompts) || {};
5971
+ return Object.values(promptsById).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
5972
+ (left, right) => (asNumber(left.openedAt) || asNumber(left.answeredAt) || 0) - (asNumber(right.openedAt) || asNumber(right.answeredAt) || 0)
5973
+ ).flatMap((prompt) => {
5974
+ const promptId = asString(prompt.promptId);
5975
+ if (!promptId || conversationPromptIds.has(promptId)) return [];
5976
+ const title = trimString(prompt.title);
5977
+ const message = trimString(prompt.message);
5978
+ const assistantContent = message || title || "Input required";
5979
+ const openedAt = asNumber(prompt.openedAt) || 0;
5980
+ const answeredAt = asNumber(prompt.answeredAt) || openedAt;
5981
+ const entries = [
5982
+ {
5983
+ id: `prompt:${promptId}:assistant`,
5984
+ role: "assistant",
5985
+ content: assistantContent,
5986
+ timestamp: openedAt,
5987
+ jobId,
5988
+ promptId,
5989
+ historyContent: `[Assistant reply]
5990
+ ${assistantContent}`,
5991
+ source: "job_prompt"
5992
+ }
5993
+ ];
5994
+ if (Object.prototype.hasOwnProperty.call(prompt, "answer")) {
5995
+ entries.push({
5996
+ id: `prompt:${promptId}:user`,
5997
+ role: "user",
5998
+ content: stringifyTranscriptValue(prompt.answer, ""),
5999
+ timestamp: answeredAt,
6000
+ jobId,
6001
+ promptId,
6002
+ source: "job_prompt"
6003
+ });
6004
+ }
6005
+ return entries;
6006
+ });
6007
+ }
6008
+ function normalizeAgentMessageEntries(jobId, rawMessages) {
6009
+ return asArray(rawMessages).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
6010
+ (left, right) => (asNumber(left.timestamp) || asNumber(left.ts) || 0) - (asNumber(right.timestamp) || asNumber(right.ts) || 0)
6011
+ ).flatMap((message) => {
6012
+ const messageId = asString(message.messageId) || asString(message.id) || crypto.randomUUID();
6013
+ const timestamp = asNumber(message.timestamp) || asNumber(message.ts) || 0;
6014
+ const reply = trimString(
6015
+ message.reply ?? message.message ?? message.text ?? message.content
6016
+ );
6017
+ const show = normalizeShowRefs(message.show);
6018
+ const entries = [];
6019
+ if (reply) {
6020
+ entries.push({
6021
+ id: `agent:${messageId}:text`,
6022
+ role: "assistant",
6023
+ content: reply,
6024
+ timestamp,
6025
+ jobId,
6026
+ historyContent: `[Assistant reply]
6027
+ ${reply}`,
6028
+ source: "job_agent_message"
6029
+ });
6030
+ }
6031
+ if (show) {
6032
+ entries.push({
6033
+ id: `agent:${messageId}:artifacts`,
6034
+ role: "assistant",
6035
+ content: "",
6036
+ timestamp,
6037
+ jobId,
6038
+ show,
6039
+ historyContent: buildArtifactHistory(show),
6040
+ source: "job_agent_message"
6041
+ });
6042
+ }
6043
+ return entries;
6044
+ });
6045
+ }
6046
+ function buildJobFallbackEntries(jobId, job, sessionHeap) {
6047
+ const timestamp = asNumber(job.finishedAt) || asNumber(job.startedAt) || asNumber(job.submittedAt) || 0;
6048
+ const resultPreview = stringifyTranscriptValue(
6049
+ job.result,
6050
+ "No job result recorded."
6051
+ );
6052
+ const presentation = resolveJobPresentation({
6053
+ jobId,
6054
+ result: job.result,
6055
+ stdout: [],
6056
+ sessionHeap
6057
+ });
6058
+ const entries = [];
6059
+ const responseText = presentation.responseText || "";
6060
+ if (responseText) {
6061
+ entries.push({
6062
+ id: `job:${jobId}:result-text`,
6063
+ role: "assistant",
6064
+ content: responseText,
6065
+ timestamp,
6066
+ jobId,
6067
+ historyContent: `[Assistant reply]
6068
+ ${responseText}`,
6069
+ source: "job_result"
6070
+ });
6071
+ }
6072
+ const show = {
6073
+ entryPaths: presentation.entries.map((entry) => entry.path),
6074
+ listNames: presentation.lists.map((list) => list.name)
6075
+ };
6076
+ if (show.entryPaths && show.entryPaths.length > 0 || show.listNames && show.listNames.length > 0) {
6077
+ entries.push({
6078
+ id: `job:${jobId}:result-artifacts`,
6079
+ role: "assistant",
6080
+ content: "",
6081
+ timestamp,
6082
+ jobId,
6083
+ show,
6084
+ historyContent: buildArtifactHistory(show),
6085
+ source: "job_result"
6086
+ });
6087
+ }
6088
+ if (entries.length === 0 && trimString(job.error)) {
6089
+ entries.push({
6090
+ id: `job:${jobId}:result-error`,
6091
+ role: "assistant",
6092
+ content: trimString(job.error),
6093
+ timestamp,
6094
+ jobId,
6095
+ historyContent: `[Assistant reply]
6096
+ ${trimString(job.error)}`,
6097
+ source: "job_result"
6098
+ });
6099
+ }
6100
+ if (entries.length === 0 && resultPreview && resultPreview !== "No job result recorded.") {
6101
+ entries.push({
6102
+ id: `job:${jobId}:result-preview`,
6103
+ role: "assistant",
6104
+ content: resultPreview,
6105
+ timestamp,
6106
+ jobId,
6107
+ historyContent: `[Assistant reply]
6108
+ ${resultPreview}`,
6109
+ source: "job_result"
6110
+ });
6111
+ }
6112
+ return entries;
6113
+ }
6114
+ function buildJobCodeEntry(jobId, job) {
6115
+ const code = trimString(job.source);
6116
+ if (!code) return null;
6117
+ const jobStatus = asString(job.status);
6118
+ const error = jobStatus === "failed" || jobStatus === "canceled" || jobStatus === "timeout" ? trimString(job.error) || `Job ${jobStatus}` : void 0;
6119
+ return {
6120
+ id: `job:${jobId}:code`,
6121
+ role: "assistant",
6122
+ content: "",
6123
+ timestamp: asNumber(job.submittedAt) || asNumber(job.startedAt) || asNumber(job.finishedAt) || 0,
6124
+ jobId,
6125
+ code,
6126
+ jobStatus,
6127
+ jobResultPreview: stringifyTranscriptValue(job.result, "No job result recorded."),
6128
+ error,
6129
+ source: "job_code"
6130
+ };
6131
+ }
6132
+ function buildSessionTranscript(input) {
6133
+ const liveDoc = input.liveDoc || null;
6134
+ const sessionHeap = input.sessionHeap || EMPTY_HEAP;
6135
+ const transcript = [];
6136
+ const conversationMessages = asArray(asRecord3(liveDoc?.conversation)?.messages).map((message) => normalizeConversationMessage(message)).filter((message) => Boolean(message));
6137
+ const conversationPromptIds = new Set(
6138
+ conversationMessages.map((message) => message.promptId).filter((promptId) => Boolean(promptId))
6139
+ );
6140
+ const assistantConversationJobIds = new Set(
6141
+ conversationMessages.filter((message) => message.role === "assistant" && Boolean(message.jobId)).map((message) => message.jobId)
6142
+ );
6143
+ transcript.push(...conversationMessages);
6144
+ const jobsById = asRecord3(asRecord3(liveDoc?.jobs)?.byId) || {};
6145
+ const jobs = Object.values(jobsById).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
6146
+ (left, right) => (asNumber(left.submittedAt) || asNumber(left.startedAt) || asNumber(left.finishedAt) || 0) - (asNumber(right.submittedAt) || asNumber(right.startedAt) || asNumber(right.finishedAt) || 0)
6147
+ );
6148
+ for (const job of jobs) {
6149
+ const jobId = asString(job.jobId);
6150
+ if (!jobId) continue;
6151
+ const codeEntry = buildJobCodeEntry(jobId, job);
6152
+ if (codeEntry) {
6153
+ transcript.push(codeEntry);
6154
+ }
6155
+ transcript.push(
6156
+ ...normalizePromptEntries(jobId, job.prompts, conversationPromptIds)
6157
+ );
6158
+ if (!assistantConversationJobIds.has(jobId)) {
6159
+ const agentEntries = normalizeAgentMessageEntries(jobId, job.agentMessages);
6160
+ if (agentEntries.length > 0) {
6161
+ transcript.push(...agentEntries);
6162
+ } else {
6163
+ transcript.push(
6164
+ ...buildJobFallbackEntries(
6165
+ jobId,
6166
+ job,
6167
+ sessionHeap
6168
+ )
6169
+ );
6170
+ }
6171
+ }
6172
+ }
6173
+ return transcript.sort((left, right) => {
6174
+ if (left.timestamp !== right.timestamp) {
6175
+ return left.timestamp - right.timestamp;
6176
+ }
6177
+ return left.id.localeCompare(right.id);
6178
+ });
6179
+ }
6180
+
5647
6181
  // src/endpoints.ts
5648
6182
  var LOCAL_API_URL = "ws://localhost:8787/granular";
5649
6183
  var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
@@ -10357,6 +10891,50 @@ function computeEffectKey(effect) {
10357
10891
  }
10358
10892
  return effect.static ? `class:${attachedClass}:static:${effect.name}` : `class:${attachedClass}:instance:${effect.name}`;
10359
10893
  }
10894
+ function computeEffectVersionSelectorSpecificity(selector) {
10895
+ if (!selector || selector.mode === "all") {
10896
+ return 0;
10897
+ }
10898
+ if (selector.mode === "exact") {
10899
+ return 2;
10900
+ }
10901
+ return 1;
10902
+ }
10903
+ function matchesEffectVersionSelector(selector, buildVersionNumber) {
10904
+ if (!selector || selector.mode === "all") {
10905
+ return true;
10906
+ }
10907
+ if (typeof buildVersionNumber !== "number" || !Number.isFinite(buildVersionNumber)) {
10908
+ return false;
10909
+ }
10910
+ if (selector.mode === "exact") {
10911
+ return buildVersionNumber === selector.versionNumber;
10912
+ }
10913
+ if (selector.mode === "before") {
10914
+ return buildVersionNumber < selector.versionNumber;
10915
+ }
10916
+ return buildVersionNumber > selector.versionNumber;
10917
+ }
10918
+ function selectRegisteredEffect(effectMap, effectKey, buildVersionNumber) {
10919
+ let bestEffect;
10920
+ let bestSpecificity = Number.NEGATIVE_INFINITY;
10921
+ for (const effect of effectMap.values()) {
10922
+ if (computeEffectKey(effect) !== effectKey) {
10923
+ continue;
10924
+ }
10925
+ if (!matchesEffectVersionSelector(effect.versionSelector, buildVersionNumber)) {
10926
+ continue;
10927
+ }
10928
+ const specificity = computeEffectVersionSelectorSpecificity(
10929
+ effect.versionSelector
10930
+ );
10931
+ if (!bestEffect || specificity > bestSpecificity) {
10932
+ bestEffect = effect;
10933
+ bestSpecificity = specificity;
10934
+ }
10935
+ }
10936
+ return bestEffect;
10937
+ }
10360
10938
  function normalizeEffectBehaviors(value) {
10361
10939
  return normalizeEffectBehaviorSummary(
10362
10940
  value
@@ -10377,9 +10955,17 @@ function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
10377
10955
  return void 0;
10378
10956
  }
10379
10957
  if (reverseHandler.includes(":")) {
10380
- return effectMap.get(reverseHandler);
10958
+ return selectRegisteredEffect(
10959
+ effectMap,
10960
+ reverseHandler,
10961
+ request.context?.buildVersionNumber
10962
+ );
10381
10963
  }
10382
- const directMatch = effectMap.get(reverseHandler);
10964
+ const directMatch = selectRegisteredEffect(
10965
+ effectMap,
10966
+ reverseHandler,
10967
+ request.context?.buildVersionNumber
10968
+ );
10383
10969
  if (directMatch) {
10384
10970
  return directMatch;
10385
10971
  }
@@ -10394,7 +10980,11 @@ function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
10394
10980
  })
10395
10981
  ];
10396
10982
  for (const candidateKey of candidateKeys) {
10397
- const candidate = effectMap.get(candidateKey);
10983
+ const candidate = selectRegisteredEffect(
10984
+ effectMap,
10985
+ candidateKey,
10986
+ request.context?.buildVersionNumber
10987
+ );
10398
10988
  if (candidate) {
10399
10989
  return candidate;
10400
10990
  }
@@ -10430,7 +11020,11 @@ function resolveHandlerForMode(effectMap, effect, request) {
10430
11020
  return { effect, mode, handler: effect.handler };
10431
11021
  }
10432
11022
  async function invokeRegisteredEffect(effectMap, request) {
10433
- const effect = effectMap.get(request.effectKey);
11023
+ const effect = selectRegisteredEffect(
11024
+ effectMap,
11025
+ request.effectKey,
11026
+ request.context?.buildVersionNumber
11027
+ );
10434
11028
  if (!effect) {
10435
11029
  throw new Error(`Effect handler not found: ${request.effectKey}`);
10436
11030
  }
@@ -11689,6 +12283,17 @@ function computeEffectKey2(effect) {
11689
12283
  }
11690
12284
  return effect.static ? `class:${attachedClass}:static:${effect.name}` : `class:${attachedClass}:instance:${effect.name}`;
11691
12285
  }
12286
+ function computeEffectVersionSelectorKey(selector) {
12287
+ if (!selector || selector.mode === "all") {
12288
+ return "all";
12289
+ }
12290
+ return `${selector.mode}:${selector.versionNumber}`;
12291
+ }
12292
+ function computeEffectRegistrationKey(effect) {
12293
+ return `${computeEffectKey2(effect)}@${computeEffectVersionSelectorKey(
12294
+ effect.versionSelector
12295
+ )}`;
12296
+ }
11692
12297
  function buildEffectHostUrl(apiUrl, sandboxId, effectClientId, clientId) {
11693
12298
  const url = new URL(apiUrl);
11694
12299
  if (url.pathname.endsWith("/granular/ws/connect")) {
@@ -12183,7 +12788,9 @@ var Environment = class {
12183
12788
  { target: targetPath }
12184
12789
  );
12185
12790
  if (result.errors?.length) {
12186
- throw new Error(`attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`);
12791
+ throw new Error(
12792
+ `attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
12793
+ );
12187
12794
  }
12188
12795
  }
12189
12796
  /**
@@ -12218,7 +12825,9 @@ var Environment = class {
12218
12825
  }`
12219
12826
  );
12220
12827
  if (result.errors?.length) {
12221
- throw new Error(`detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`);
12828
+ throw new Error(
12829
+ `detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
12830
+ );
12222
12831
  }
12223
12832
  }
12224
12833
  /**
@@ -12340,7 +12949,9 @@ var Environment = class {
12340
12949
  async _runGraphql(query, label) {
12341
12950
  const result = await this.graphql(query);
12342
12951
  if (result.errors?.length) {
12343
- throw new Error(`${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`);
12952
+ throw new Error(
12953
+ `${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
12954
+ );
12344
12955
  }
12345
12956
  return result.data;
12346
12957
  }
@@ -12875,6 +13486,9 @@ var EnvironmentSession = class extends Session {
12875
13486
  get envName() {
12876
13487
  return this.environment.envName;
12877
13488
  }
13489
+ get tag() {
13490
+ return this.environment.tag;
13491
+ }
12878
13492
  get versionId() {
12879
13493
  return this.environment.versionId;
12880
13494
  }
@@ -12894,12 +13508,171 @@ var EnvironmentSession = class extends Session {
12894
13508
  return this.environment.feedback;
12895
13509
  }
12896
13510
  /**
12897
- * Return a plain JS snapshot of the synced session heap.
13511
+ * Return a plain JS copy of the synced session heap.
12898
13512
  */
12899
13513
  getHeap() {
12900
13514
  const doc = this.document;
12901
13515
  return normalizeHeapSnapshot(doc?.heap);
12902
13516
  }
13517
+ async sessionDataRequest(path2, query) {
13518
+ const searchParams = new URLSearchParams();
13519
+ for (const [key, value] of Object.entries(query || {})) {
13520
+ if (value !== null && typeof value !== "undefined" && value !== "") {
13521
+ searchParams.set(key, String(value));
13522
+ }
13523
+ }
13524
+ const queryString = searchParams.toString();
13525
+ const response = await fetch(
13526
+ `${this.environment.runtimeBaseUrl}/orchestrator/ws/sessions/${encodeURIComponent(this.sessionId)}${path2}${queryString ? `?${queryString}` : ""}`,
13527
+ {
13528
+ method: "GET",
13529
+ headers: {
13530
+ Authorization: `Bearer ${this.environment.authToken}`,
13531
+ "Content-Type": "application/json"
13532
+ }
13533
+ }
13534
+ );
13535
+ if (!response.ok) {
13536
+ const errorText = await response.text();
13537
+ throw new Error(
13538
+ `Session data API Error (${response.status}): ${errorText}`
13539
+ );
13540
+ }
13541
+ return response.json();
13542
+ }
13543
+ async collectAllSessionItems(listPage) {
13544
+ const items = [];
13545
+ let cursor = null;
13546
+ do {
13547
+ const page = await listPage({ limit: 500, cursor });
13548
+ items.push(...page.items);
13549
+ cursor = page.nextCursor;
13550
+ } while (cursor);
13551
+ return items;
13552
+ }
13553
+ /**
13554
+ * Fetch the live session document from the runtime DO.
13555
+ *
13556
+ * For history and saved artifacts, prefer the collection APIs on
13557
+ * `messages`, `timeline`, `jobs`, and `heap`.
13558
+ */
13559
+ async getDocument() {
13560
+ return this.sessionDataRequest("/document");
13561
+ }
13562
+ get messages() {
13563
+ return {
13564
+ list: (options = {}) => this.sessionDataRequest(
13565
+ "/messages",
13566
+ options
13567
+ )
13568
+ };
13569
+ }
13570
+ get timeline() {
13571
+ return {
13572
+ list: (options = {}) => this.sessionDataRequest(
13573
+ "/timeline",
13574
+ options
13575
+ )
13576
+ };
13577
+ }
13578
+ get jobs() {
13579
+ return {
13580
+ list: (options = {}) => this.sessionDataRequest(
13581
+ "/jobs",
13582
+ options
13583
+ ),
13584
+ get: (jobId) => this.sessionDataRequest(
13585
+ `/jobs/${encodeURIComponent(jobId)}`
13586
+ )
13587
+ };
13588
+ }
13589
+ get heap() {
13590
+ return {
13591
+ entries: {
13592
+ list: (options = {}) => this.sessionDataRequest(
13593
+ "/heap/entries",
13594
+ options
13595
+ ),
13596
+ get: (path2) => this.sessionDataRequest(
13597
+ `/heap/entries/${encodeURIComponent(path2)}`
13598
+ )
13599
+ },
13600
+ lists: {
13601
+ list: (options = {}) => this.sessionDataRequest(
13602
+ "/heap/lists",
13603
+ options
13604
+ ),
13605
+ get: (name) => this.sessionDataRequest(
13606
+ `/heap/lists/${encodeURIComponent(name)}`
13607
+ )
13608
+ }
13609
+ };
13610
+ }
13611
+ get transcript() {
13612
+ return {
13613
+ list: async (options = {}) => {
13614
+ const [messages, jobs, entries, lists] = await Promise.all([
13615
+ this.collectAllSessionItems(this.messages.list),
13616
+ this.collectAllSessionItems(
13617
+ (pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
13618
+ ),
13619
+ this.collectAllSessionItems(this.heap.entries.list),
13620
+ this.collectAllSessionItems(this.heap.lists.list)
13621
+ ]);
13622
+ const liveDoc = {
13623
+ conversation: { messages },
13624
+ jobs: {
13625
+ byId: Object.fromEntries(
13626
+ jobs.map((job) => {
13627
+ const record = job && typeof job === "object" ? job : null;
13628
+ const id = typeof record?.jobId === "string" ? record.jobId : typeof record?.id === "string" ? record.id : null;
13629
+ return id ? [id, record] : null;
13630
+ }).filter(
13631
+ (entry) => Boolean(entry)
13632
+ )
13633
+ )
13634
+ }
13635
+ };
13636
+ const heap = normalizeHeapSnapshot({
13637
+ entriesByPath: Object.fromEntries(
13638
+ entries.map((entry) => {
13639
+ return entry?.path ? [
13640
+ entry.path,
13641
+ entry
13642
+ ] : null;
13643
+ }).filter(
13644
+ (entry) => Boolean(entry)
13645
+ )
13646
+ ),
13647
+ listsByName: Object.fromEntries(
13648
+ lists.map((list) => {
13649
+ return list?.name ? [list.name, list] : null;
13650
+ }).filter(
13651
+ (entry) => Boolean(entry)
13652
+ )
13653
+ ),
13654
+ variablesByName: this.getHeap().variablesByName,
13655
+ updatedAt: Date.now()
13656
+ });
13657
+ const allItems = buildSessionTranscript({
13658
+ liveDoc,
13659
+ sessionHeap: heap
13660
+ });
13661
+ const limit = Math.max(
13662
+ 1,
13663
+ Math.min(500, Math.floor(options.limit ?? 100))
13664
+ );
13665
+ const offset = typeof options.cursor === "string" ? Number.parseInt(options.cursor, 10) || 0 : 0;
13666
+ const items = allItems.slice(offset, offset + limit);
13667
+ const nextOffset = offset + items.length;
13668
+ return {
13669
+ items,
13670
+ nextCursor: nextOffset < allItems.length ? String(nextOffset) : null,
13671
+ totalCount: allItems.length
13672
+ };
13673
+ }
13674
+ };
13675
+ }
12903
13676
  async graphql(query, variables) {
12904
13677
  return this.environment.graphql(query, variables);
12905
13678
  }
@@ -13042,7 +13815,7 @@ var Granular = class _Granular {
13042
13815
  onUnexpectedClose;
13043
13816
  onReconnectError;
13044
13817
  debugHttp = process.env.GRANULAR_DEBUG_HTTP === "1";
13045
- /** Sandbox-level effect registry: sandboxId → (effectKey → ToolWithHandler) */
13818
+ /** Sandbox-level effect registry: sandboxId → (effectKey@selector → ToolWithHandler) */
13046
13819
  sandboxEffects = /* @__PURE__ */ new Map();
13047
13820
  /** Live sandbox-scoped effect hosts keyed by sandboxId */
13048
13821
  sandboxEffectHosts = /* @__PURE__ */ new Map();
@@ -13461,7 +14234,8 @@ var Granular = class _Granular {
13461
14234
  provenance: effect.provenance || { source: "custom" },
13462
14235
  tags: effect.tags,
13463
14236
  className: effect.className,
13464
- static: effect.static
14237
+ static: effect.static,
14238
+ versionSelector: effect.versionSelector
13465
14239
  };
13466
14240
  }
13467
14241
  async publishSandboxEffectCatalog(host) {
@@ -13649,7 +14423,10 @@ var Granular = class _Granular {
13649
14423
  async registerEffect(sandboxNameOrId, effect) {
13650
14424
  const sandbox = await this.findOrCreateSandbox(sandboxNameOrId);
13651
14425
  const sandboxId = sandbox.sandboxId;
13652
- this.getSandboxEffectMap(sandboxId).set(computeEffectKey2(effect), effect);
14426
+ this.getSandboxEffectMap(sandboxId).set(
14427
+ computeEffectRegistrationKey(effect),
14428
+ effect
14429
+ );
13653
14430
  await this.syncSandboxEffectCatalog(sandboxId);
13654
14431
  }
13655
14432
  /**
@@ -13662,7 +14439,7 @@ var Granular = class _Granular {
13662
14439
  const sandboxId = sandbox.sandboxId;
13663
14440
  const map = this.getSandboxEffectMap(sandboxId);
13664
14441
  for (const effect of effects) {
13665
- map.set(computeEffectKey2(effect), effect);
14442
+ map.set(computeEffectRegistrationKey(effect), effect);
13666
14443
  }
13667
14444
  await this.syncSandboxEffectCatalog(sandboxId);
13668
14445
  }
@@ -13680,7 +14457,7 @@ var Granular = class _Granular {
13680
14457
  return;
13681
14458
  }
13682
14459
  const nextEntries = Array.from(currentMap.entries()).filter(
13683
- ([effectKey, effect]) => effectKey !== name && effect.name !== name
14460
+ ([, effect]) => computeEffectKey2(effect) !== name && effect.name !== name
13684
14461
  );
13685
14462
  if (nextEntries.length === currentMap.size) {
13686
14463
  return;
@@ -14109,15 +14886,15 @@ var Granular = class _Granular {
14109
14886
  };
14110
14887
 
14111
14888
  // src/agent-harness.ts
14112
- function asRecord2(value) {
14889
+ function asRecord4(value) {
14113
14890
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
14114
14891
  return value;
14115
14892
  }
14116
- function asArray(value) {
14893
+ function asArray2(value) {
14117
14894
  return Array.isArray(value) ? value : [];
14118
14895
  }
14119
14896
  function toSortedRecords(value) {
14120
- return Object.values(asRecord2(value) || {}).map((entry) => asRecord2(entry)).filter((entry) => Boolean(entry));
14897
+ return Object.values(asRecord4(value) || {}).map((entry) => asRecord4(entry)).filter((entry) => Boolean(entry));
14121
14898
  }
14122
14899
  function uniqueStrings(values, maxCount) {
14123
14900
  const seen = /* @__PURE__ */ new Set();
@@ -14143,7 +14920,7 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
14143
14920
  const headline = entry.label || entry.id || entry.path || "Unknown";
14144
14921
  const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
14145
14922
  const classLabel = entry.className || "unknown";
14146
- const preview = asArray(entry.fields).filter(
14923
+ const preview = asArray2(entry.fields).filter(
14147
14924
  (field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
14148
14925
  ).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
14149
14926
  return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
@@ -14280,14 +15057,14 @@ function normalizeActionSummaryForPrompt(line) {
14280
15057
  return line.replace(/\blimit=/g, "perPage=").replace(/\blimit:/g, "perPage:");
14281
15058
  }
14282
15059
  function getCurrentClosureId(liveDoc) {
14283
- const loop = asRecord2(liveDoc?.loop);
15060
+ const loop = asRecord4(liveDoc?.loop);
14284
15061
  return typeof loop?.currentClosureId === "string" ? loop.currentClosureId : null;
14285
15062
  }
14286
15063
  function getLatestClosure(liveDoc) {
14287
- const loop = asRecord2(liveDoc?.loop);
15064
+ const loop = asRecord4(liveDoc?.loop);
14288
15065
  const currentClosureId = getCurrentClosureId(liveDoc);
14289
- const closuresById = asRecord2(loop?.closuresById) || {};
14290
- const currentClosure = currentClosureId ? asRecord2(closuresById[currentClosureId]) : null;
15066
+ const closuresById = asRecord4(loop?.closuresById) || {};
15067
+ const currentClosure = currentClosureId ? asRecord4(closuresById[currentClosureId]) : null;
14291
15068
  if (currentClosure) {
14292
15069
  return {
14293
15070
  ...currentClosure,
@@ -14296,7 +15073,7 @@ function getLatestClosure(liveDoc) {
14296
15073
  }
14297
15074
  const closures = [];
14298
15075
  for (const [closureId, value] of Object.entries(closuresById)) {
14299
- const record = asRecord2(value);
15076
+ const record = asRecord4(value);
14300
15077
  if (!record) continue;
14301
15078
  closures.push({ ...record, closureId });
14302
15079
  }
@@ -14333,10 +15110,10 @@ function getJobTimestamp(job) {
14333
15110
  return Number(job.finishedAt) || Number(job.startedAt) || Number(job.submittedAt) || 0;
14334
15111
  }
14335
15112
  function getJobRecords(liveDoc) {
14336
- const jobsById = asRecord2(asRecord2(liveDoc?.jobs)?.byId) || {};
15113
+ const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
14337
15114
  const jobs = [];
14338
15115
  for (const [jobId, value] of Object.entries(jobsById)) {
14339
- const record = asRecord2(value);
15116
+ const record = asRecord4(value);
14340
15117
  if (!record) continue;
14341
15118
  jobs.push({ ...record, jobId });
14342
15119
  }
@@ -14346,7 +15123,7 @@ function getJobRecords(liveDoc) {
14346
15123
  return jobs;
14347
15124
  }
14348
15125
  function getPromptRecordsFromJobs(liveDoc) {
14349
- return getJobRecords(liveDoc).flatMap((job) => Object.values(asRecord2(job.prompts) || {})).map((prompt) => asRecord2(prompt)).filter((prompt) => Boolean(prompt));
15126
+ return getJobRecords(liveDoc).flatMap((job) => Object.values(asRecord4(job.prompts) || {})).map((prompt) => asRecord4(prompt)).filter((prompt) => Boolean(prompt));
14350
15127
  }
14351
15128
  function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
14352
15129
  const boundary = getWorkflowBoundary(liveDoc, options);
@@ -14361,15 +15138,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
14361
15138
  const openDecisionIds = [];
14362
15139
  const openPromptIds = [];
14363
15140
  for (const job of jobs) {
14364
- for (const line of asArray(job.actionSummary)) {
15141
+ for (const line of asArray2(job.actionSummary)) {
14365
15142
  if (typeof line === "string" && line.trim()) {
14366
15143
  actionSummaryLines.push(line.trim());
14367
15144
  }
14368
15145
  }
14369
- for (const rawEvent of asArray(job.actionTrace)) {
14370
- const event = asRecord2(rawEvent);
14371
- const details = asRecord2(event?.details);
14372
- const outcome = asRecord2(event?.outcome);
15146
+ for (const rawEvent of asArray2(job.actionTrace)) {
15147
+ const event = asRecord4(rawEvent);
15148
+ const details = asRecord4(event?.details);
15149
+ const outcome = asRecord4(event?.outcome);
14373
15150
  if (typeof details?.name === "string") {
14374
15151
  variableNames.push(details.name);
14375
15152
  }
@@ -14396,7 +15173,7 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
14396
15173
  }
14397
15174
  }
14398
15175
  }
14399
- const loop = asRecord2(liveDoc?.loop);
15176
+ const loop = asRecord4(liveDoc?.loop);
14400
15177
  const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
14401
15178
  const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
14402
15179
  const status = typeof task.status === "string" ? task.status : "pending";
@@ -14444,15 +15221,15 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
14444
15221
  openPromptIds.push(prompt.id);
14445
15222
  }
14446
15223
  }
14447
- const heap = asRecord2(liveDoc?.heap);
14448
- const variablesByName = asRecord2(heap?.variablesByName) || {};
14449
- const listsByName = asRecord2(heap?.listsByName) || {};
15224
+ const heap = asRecord4(liveDoc?.heap);
15225
+ const variablesByName = asRecord4(heap?.variablesByName) || {};
15226
+ const listsByName = asRecord4(heap?.listsByName) || {};
14450
15227
  const recentHints = extractFocusHintsFromActionSummary(actionSummaryLines);
14451
15228
  variableNames.push(...recentHints.variableNames);
14452
15229
  listNames.push(...recentHints.listNames);
14453
15230
  entryPaths.push(...recentHints.entryPaths);
14454
15231
  for (const variableName of uniqueStrings(variableNames)) {
14455
- const variable = asRecord2(variablesByName[variableName]);
15232
+ const variable = asRecord4(variablesByName[variableName]);
14456
15233
  if (!variable) continue;
14457
15234
  if (typeof variable.listName === "string") {
14458
15235
  listNames.push(variable.listName);
@@ -14462,13 +15239,13 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
14462
15239
  }
14463
15240
  }
14464
15241
  for (const listName of uniqueStrings(listNames)) {
14465
- const list = asRecord2(listsByName[listName]);
14466
- for (const path2 of asArray(list?.paths).slice(0, 4)) {
15242
+ const list = asRecord4(listsByName[listName]);
15243
+ for (const path2 of asArray2(list?.paths).slice(0, 4)) {
14467
15244
  entryPaths.push(path2);
14468
15245
  }
14469
15246
  }
14470
15247
  if (variableNames.length === 0 && boundary.reason !== "request_start") {
14471
- const recentVariables = Object.values(variablesByName).map((value) => asRecord2(value)).filter((value) => Boolean(value)).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, 3);
15248
+ const recentVariables = Object.values(variablesByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, 3);
14472
15249
  for (const variable of recentVariables) {
14473
15250
  if (typeof variable.name === "string") {
14474
15251
  variableNames.push(variable.name);
@@ -14551,12 +15328,12 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
14551
15328
  }
14552
15329
  function hasOpenPrompt(liveDoc, pendingPrompts) {
14553
15330
  if (pendingPrompts.length > 0) return true;
14554
- const jobsById = asRecord2(asRecord2(liveDoc?.jobs)?.byId) || {};
15331
+ const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
14555
15332
  for (const job of Object.values(jobsById)) {
14556
- const prompts = asRecord2(asRecord2(job)?.prompts);
15333
+ const prompts = asRecord4(asRecord4(job)?.prompts);
14557
15334
  if (!prompts) continue;
14558
15335
  for (const prompt of Object.values(prompts)) {
14559
- const record = asRecord2(prompt);
15336
+ const record = asRecord4(prompt);
14560
15337
  if (record?.status === "open") return true;
14561
15338
  }
14562
15339
  }
@@ -14564,7 +15341,7 @@ function hasOpenPrompt(liveDoc, pendingPrompts) {
14564
15341
  }
14565
15342
  function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14566
15343
  const lines = [];
14567
- const loop = asRecord2(liveDoc?.loop);
15344
+ const loop = asRecord4(liveDoc?.loop);
14568
15345
  const boundary = getWorkflowBoundary(liveDoc, options);
14569
15346
  const tasks = toSortedRecords(loop?.tasksById).filter((task) => {
14570
15347
  const updatedAt = Number(task.updatedAt) || Number(task.createdAt) || 0;
@@ -14624,8 +15401,8 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14624
15401
  const title = typeof decision.title === "string" && decision.title.trim() ? decision.title.trim() : "Decision";
14625
15402
  const decisionId = typeof decision.decisionId === "string" ? decision.decisionId : "unknown";
14626
15403
  if (status === "open") {
14627
- const candidatePreview = asArray(decision.candidates).slice(0, 3).map((candidate) => {
14628
- const record = asRecord2(candidate);
15404
+ const candidatePreview = asArray2(decision.candidates).slice(0, 3).map((candidate) => {
15405
+ const record = asRecord4(candidate);
14629
15406
  if (!record) return null;
14630
15407
  const candidateId = typeof record.id === "string" ? record.id : "unknown";
14631
15408
  const candidateLabel = typeof record.label === "string" && record.label.trim() ? record.label.trim() : candidateId;
@@ -14635,7 +15412,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14635
15412
  `- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`
14636
15413
  );
14637
15414
  } else {
14638
- const selected = asRecord2(decision.selected);
15415
+ const selected = asRecord4(decision.selected);
14639
15416
  const label = typeof selected?.label === "string" ? selected.label : typeof selected?.id === "string" ? selected.id : "unknown";
14640
15417
  lines.push(`- [resolved] ${title} (${decisionId}) -> ${label}`);
14641
15418
  }
@@ -14648,12 +15425,12 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14648
15425
  title: prompt.title,
14649
15426
  message: prompt.message
14650
15427
  })),
14651
- ...Object.values(asRecord2(asRecord2(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord2(asRecord2(job)?.prompts) || {})).map((prompt) => asRecord2(prompt)).filter(
15428
+ ...Object.values(asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord4(asRecord4(job)?.prompts) || {})).map((prompt) => asRecord4(prompt)).filter(
14652
15429
  (prompt) => Boolean(prompt && prompt.status === "open")
14653
15430
  )
14654
15431
  ];
14655
15432
  const visiblePrompts = boundary.reason === "request_start" ? openPrompts.filter((prompt) => {
14656
- const promptRecord = asRecord2(prompt);
15433
+ const promptRecord = asRecord4(prompt);
14657
15434
  const openedAt = Number(promptRecord?.openedAt) || 0;
14658
15435
  const promptId = typeof promptRecord?.id === "string" ? promptRecord.id : typeof prompt.id === "string" ? prompt.id : null;
14659
15436
  return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some(
@@ -14672,7 +15449,7 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14672
15449
  }
14673
15450
  }
14674
15451
  const currentClosureId = getCurrentClosureId(liveDoc);
14675
- const closureRecord = currentClosureId ? asRecord2(asRecord2(loop?.closuresById)?.[currentClosureId]) : null;
15452
+ const closureRecord = currentClosureId ? asRecord4(asRecord4(loop?.closuresById)?.[currentClosureId]) : null;
14676
15453
  const visibleClosure = closureRecord && (boundary.reason !== "request_start" || (Number(closureRecord.createdAt) || 0) >= boundary.timestamp) ? closureRecord : null;
14677
15454
  lines.push("", "Loop Closure:");
14678
15455
  if (visibleClosure) {
@@ -14685,10 +15462,10 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
14685
15462
  return lines.join("\n");
14686
15463
  }
14687
15464
  function projectHeapSummary(heap, options) {
14688
- const heapRecord = asRecord2(heap) || {};
14689
- const entriesByPath = asRecord2(heapRecord.entriesByPath) || {};
14690
- const listsByName = asRecord2(heapRecord.listsByName) || {};
14691
- const variablesByName = asRecord2(heapRecord.variablesByName) || {};
15465
+ const heapRecord = asRecord4(heap) || {};
15466
+ const entriesByPath = asRecord4(heapRecord.entriesByPath) || {};
15467
+ const listsByName = asRecord4(heapRecord.listsByName) || {};
15468
+ const variablesByName = asRecord4(heapRecord.variablesByName) || {};
14692
15469
  const focusedVariableNames = new Set(
14693
15470
  uniqueStrings(options?.focus?.variableNames || [])
14694
15471
  );
@@ -14703,7 +15480,7 @@ function projectHeapSummary(heap, options) {
14703
15480
  const maxVariables = options?.maxVariables ?? (hasFocus ? 4 : 6);
14704
15481
  const maxLists = options?.maxLists ?? (hasFocus ? 3 : 4);
14705
15482
  const maxEntries = options?.maxEntries ?? (hasFocus ? 5 : 6);
14706
- const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) => asRecord2(value)).filter((value) => Boolean(value)).sort((left, right) => {
15483
+ const variables = suppressRecentFallback ? [] : Object.values(variablesByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).sort((left, right) => {
14707
15484
  const leftFocused = left.name && focusedVariableNames.has(left.name) ? 1 : 0;
14708
15485
  const rightFocused = right.name && focusedVariableNames.has(right.name) ? 1 : 0;
14709
15486
  return rightFocused - leftFocused || (right.updatedAt || 0) - (left.updatedAt || 0);
@@ -14717,7 +15494,7 @@ function projectHeapSummary(heap, options) {
14717
15494
  for (const variable of variables) {
14718
15495
  if (variable.entryPath) referencedPaths.add(variable.entryPath);
14719
15496
  if (variable.listName) {
14720
- const list = asRecord2(
15497
+ const list = asRecord4(
14721
15498
  listsByName[variable.listName]
14722
15499
  );
14723
15500
  for (const path2 of list?.paths || []) referencedPaths.add(path2);
@@ -14726,10 +15503,10 @@ function projectHeapSummary(heap, options) {
14726
15503
  for (const path2 of focusedEntryPaths) {
14727
15504
  referencedPaths.add(path2);
14728
15505
  }
14729
- const visibleLists = Object.values(listsByName).map((value) => asRecord2(value)).filter((value) => Boolean(value)).filter(
15506
+ const visibleLists = Object.values(listsByName).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter(
14730
15507
  (list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))
14731
15508
  ).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
14732
- const visibleEntries = Object.values(entriesByPath).map((value) => 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);
15509
+ const visibleEntries = Object.values(entriesByPath).map((value) => asRecord4(value)).filter((value) => Boolean(value)).filter((entry) => entry.path && referencedPaths.has(entry.path)).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxEntries);
14733
15510
  const lines = [];
14734
15511
  lines.push("Variables:");
14735
15512
  if (variables.length === 0) {
@@ -14743,7 +15520,7 @@ function projectHeapSummary(heap, options) {
14743
15520
  continue;
14744
15521
  }
14745
15522
  if (variable.kind === "entry") {
14746
- const entry = variable.entryPath ? asRecord2(
15523
+ const entry = variable.entryPath ? asRecord4(
14747
15524
  entriesByPath[variable.entryPath]
14748
15525
  ) : null;
14749
15526
  lines.push(
@@ -14751,7 +15528,7 @@ function projectHeapSummary(heap, options) {
14751
15528
  );
14752
15529
  continue;
14753
15530
  }
14754
- const list = variable.listName ? asRecord2(listsByName[variable.listName]) : null;
15531
+ const list = variable.listName ? asRecord4(listsByName[variable.listName]) : null;
14755
15532
  lines.push(
14756
15533
  `- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`
14757
15534
  );
@@ -14784,7 +15561,7 @@ function createHarnessVerifierSnapshot(input) {
14784
15561
  input.projectionOptions
14785
15562
  );
14786
15563
  const heapDigest = hashString(
14787
- projectHeapSummary(asRecord2(input.liveDoc?.heap), {
15564
+ projectHeapSummary(asRecord4(input.liveDoc?.heap), {
14788
15565
  focus: workflowFocus
14789
15566
  })
14790
15567
  ) || "00000000";
@@ -15110,246 +15887,12 @@ ${loopBlock}
15110
15887
  - Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
15111
15888
  }
15112
15889
 
15113
- // src/job-presentation.ts
15114
- var RESPONSE_KEYS = [
15115
- "reply",
15116
- "response",
15117
- "text",
15118
- "message",
15119
- "summary",
15120
- "answer"
15121
- ];
15122
- var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
15123
- var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
15124
- var LIST_KEY_CANDIDATES = ["listName"];
15125
- var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
15126
- var VARIABLE_KEY_CANDIDATES = ["variableName"];
15127
- var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
15128
- var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
15129
- function asRecord3(value) {
15130
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
15131
- return value;
15132
- }
15133
- function normalizeText(value) {
15134
- if (typeof value !== "string") return null;
15135
- const trimmed = value.trim();
15136
- if (!trimmed) return null;
15137
- if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
15138
- return null;
15139
- }
15140
- return trimmed;
15141
- }
15142
- function humanTextFromStdout(stdout) {
15143
- for (const line of [...stdout].reverse()) {
15144
- const normalized = normalizeText(line);
15145
- if (!normalized) continue;
15146
- if (/^[A-Z_]+:/.test(normalized)) continue;
15147
- return normalized;
15148
- }
15149
- return null;
15150
- }
15151
- function pushString(target, value) {
15152
- if (typeof value === "string" && value.trim()) {
15153
- target.add(value.trim());
15154
- }
15155
- }
15156
- function pushStringArray(target, value) {
15157
- if (!Array.isArray(value)) return;
15158
- for (const item of value) {
15159
- pushString(target, item);
15160
- }
15161
- }
15162
- function collectReferencesFromRecord(record, refs) {
15163
- for (const key of ENTRY_KEY_CANDIDATES)
15164
- pushString(refs.entryPaths, record[key]);
15165
- for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
15166
- pushStringArray(refs.entryPaths, record[key]);
15167
- for (const key of LIST_KEY_CANDIDATES)
15168
- pushString(refs.listNames, record[key]);
15169
- for (const key of LIST_ARRAY_KEY_CANDIDATES)
15170
- pushStringArray(refs.listNames, record[key]);
15171
- for (const key of VARIABLE_KEY_CANDIDATES)
15172
- pushString(refs.variableNames, record[key]);
15173
- for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
15174
- pushStringArray(refs.variableNames, record[key]);
15175
- }
15176
- function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
15177
- if (value === null || value === void 0 || depth > 4 || seen.has(value))
15178
- return;
15179
- if (typeof value === "string") {
15180
- const trimmed = value.trim();
15181
- if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
15182
- if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
15183
- if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
15184
- return;
15185
- }
15186
- if (Array.isArray(value)) {
15187
- seen.add(value);
15188
- for (const item of value.slice(0, 24)) {
15189
- scanForHeapReferences(item, heap, refs, depth + 1, seen);
15190
- }
15191
- return;
15192
- }
15193
- const record = asRecord3(value);
15194
- if (!record) return;
15195
- seen.add(value);
15196
- collectReferencesFromRecord(record, refs);
15197
- for (const key of UI_CONTAINER_KEYS) {
15198
- const nested = asRecord3(record[key]);
15199
- if (nested) collectReferencesFromRecord(nested, refs);
15200
- }
15201
- for (const nested of Object.values(record).slice(0, 24)) {
15202
- scanForHeapReferences(nested, heap, refs, depth + 1, seen);
15203
- }
15204
- }
15205
- function resolveVariablesToReferences(variableNames, heap, refs) {
15206
- for (const variableName of variableNames) {
15207
- const variable = heap.variablesByName?.[variableName];
15208
- if (!variable) continue;
15209
- if (variable.kind === "entry" && variable.entryPath) {
15210
- refs.entryPaths.add(variable.entryPath);
15211
- }
15212
- if (variable.kind === "list" && variable.listName) {
15213
- refs.listNames.add(variable.listName);
15214
- }
15215
- }
15216
- }
15217
- function sortEntries(entries) {
15218
- return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
15219
- }
15220
- function sortLists(lists) {
15221
- return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
15222
- }
15223
- function dedupeEntries(entries) {
15224
- const seen = /* @__PURE__ */ new Set();
15225
- const result = [];
15226
- for (const entry of entries) {
15227
- if (!entry?.path || seen.has(entry.path)) continue;
15228
- seen.add(entry.path);
15229
- result.push(entry);
15230
- }
15231
- return result;
15232
- }
15233
- function dedupeLists(lists) {
15234
- const seen = /* @__PURE__ */ new Set();
15235
- const result = [];
15236
- for (const list of lists) {
15237
- if (!list?.name || seen.has(list.name)) continue;
15238
- seen.add(list.name);
15239
- result.push(list);
15240
- }
15241
- return result;
15242
- }
15243
- function extractResponseText(result, stdout) {
15244
- const directText = normalizeText(result);
15245
- if (directText) return directText;
15246
- const record = asRecord3(result);
15247
- if (record) {
15248
- for (const key of RESPONSE_KEYS) {
15249
- const normalized = normalizeText(record[key]);
15250
- if (normalized) return normalized;
15251
- }
15252
- for (const containerKey of UI_CONTAINER_KEYS) {
15253
- const nested = asRecord3(record[containerKey]);
15254
- if (!nested) continue;
15255
- for (const key of RESPONSE_KEYS) {
15256
- const normalized = normalizeText(nested[key]);
15257
- if (normalized) return normalized;
15258
- }
15259
- }
15260
- }
15261
- return humanTextFromStdout(stdout);
15262
- }
15263
- function fallbackResponseText(entries, lists) {
15264
- if (entries.length > 0) {
15265
- return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
15266
- }
15267
- if (lists.length > 0) {
15268
- const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
15269
- if (emptyOnly) {
15270
- return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
15271
- }
15272
- return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
15273
- }
15274
- return null;
15275
- }
15276
- function getJobRelatedEntries(heap, jobId) {
15277
- return sortEntries(
15278
- Object.values(heap.entriesByPath || {}).filter(
15279
- (entry) => entry.relatedJobIds?.includes(jobId)
15280
- )
15281
- );
15282
- }
15283
- function getJobRelatedLists(heap, jobId) {
15284
- return sortLists(
15285
- Object.values(heap.listsByName || {}).filter(
15286
- (list) => list.relatedJobIds?.includes(jobId)
15287
- )
15288
- );
15289
- }
15290
- function entriesFromLists(lists, heap) {
15291
- const entries = [];
15292
- for (const list of lists) {
15293
- for (const path2 of list.paths || []) {
15294
- const entry = heap.entriesByPath?.[path2];
15295
- if (entry) entries.push(entry);
15296
- }
15297
- }
15298
- return entries;
15299
- }
15300
- function resolveJobPresentation({
15301
- jobId,
15302
- result,
15303
- stdout = [],
15304
- sessionHeap,
15305
- allowExplicitArtifacts = true
15306
- }) {
15307
- const refs = {
15308
- entryPaths: /* @__PURE__ */ new Set(),
15309
- listNames: /* @__PURE__ */ new Set(),
15310
- variableNames: /* @__PURE__ */ new Set()
15311
- };
15312
- if (allowExplicitArtifacts) {
15313
- scanForHeapReferences(result, sessionHeap, refs);
15314
- resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
15315
- }
15316
- const referencedLists = sortLists(
15317
- [...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
15318
- );
15319
- const referencedEntries = sortEntries(
15320
- [...refs.entryPaths].map((path2) => sessionHeap.entriesByPath?.[path2]).filter((entry) => Boolean(entry))
15321
- );
15322
- const jobLists = getJobRelatedLists(sessionHeap, jobId);
15323
- const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
15324
- const changedEntries = dedupeEntries([
15325
- ...jobEntries,
15326
- ...entriesFromLists(jobLists, sessionHeap)
15327
- ]);
15328
- const explicitLists = dedupeLists(referencedLists);
15329
- const explicitEntries = dedupeEntries([
15330
- ...referencedEntries,
15331
- ...entriesFromLists(referencedLists, sessionHeap)
15332
- ]);
15333
- const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
15334
- const lists = hasExplicitArtifacts ? explicitLists : jobLists;
15335
- const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
15336
- const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
15337
- return {
15338
- responseText,
15339
- entries,
15340
- lists,
15341
- changedEntries,
15342
- changedLists: jobLists,
15343
- hasExplicitArtifacts
15344
- };
15345
- }
15346
-
15347
15890
  // src/agent-evals.ts
15348
15891
  var DEFAULT_CONTROLLER_BUDGETS = {
15349
15892
  maxIterations: 6,
15350
15893
  maxNoProgressIterations: 2
15351
15894
  };
15352
- function asRecord4(value) {
15895
+ function asRecord5(value) {
15353
15896
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
15354
15897
  return value;
15355
15898
  }
@@ -15397,7 +15940,7 @@ function buildArtifactDir(baseDir, suiteName = "granular-agent-evals") {
15397
15940
  function createTimestampedArtifactDirectory(options) {
15398
15941
  return buildArtifactDir(options?.baseDir, options?.suiteName);
15399
15942
  }
15400
- function asArray2(value) {
15943
+ function asArray3(value) {
15401
15944
  if (!value) return [];
15402
15945
  return Array.isArray(value) ? value : [value];
15403
15946
  }
@@ -15415,7 +15958,7 @@ function buildScenarioSteps(scenario) {
15415
15958
  request: scenario.request,
15416
15959
  human: scenario.human,
15417
15960
  expect: scenario.expect,
15418
- inspect: [...asArray2(scenario.inspect), ...asArray2(scenario.verify)],
15961
+ inspect: [...asArray3(scenario.inspect), ...asArray3(scenario.verify)],
15419
15962
  check: scenario.check,
15420
15963
  maxIterations: scenario.maxIterations,
15421
15964
  setup: {
@@ -15456,12 +15999,12 @@ function buildHistory(entries) {
15456
15999
  );
15457
16000
  }
15458
16001
  function getOpenPromptsFromDoc(liveDoc) {
15459
- const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
16002
+ const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
15460
16003
  const prompts = [];
15461
16004
  for (const job of Object.values(jobsById)) {
15462
- const promptRecords = asRecord4(asRecord4(job)?.prompts) || {};
16005
+ const promptRecords = asRecord5(asRecord5(job)?.prompts) || {};
15463
16006
  for (const raw of Object.values(promptRecords)) {
15464
- const record = asRecord4(raw);
16007
+ const record = asRecord5(raw);
15465
16008
  if (!record || record.status !== "open" || typeof record.promptId !== "string")
15466
16009
  continue;
15467
16010
  const prompt = normalizePrompt({
@@ -15482,11 +16025,11 @@ function getOpenPromptsFromDoc(liveDoc) {
15482
16025
  return prompts;
15483
16026
  }
15484
16027
  function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
15485
- const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
16028
+ const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
15486
16029
  return prompts.filter((prompt) => {
15487
16030
  for (const jobRecord of Object.values(jobsById)) {
15488
- const promptsById = asRecord4(asRecord4(jobRecord)?.prompts) || {};
15489
- const promptRecord = asRecord4(promptsById[prompt.id]);
16031
+ const promptsById = asRecord5(asRecord5(jobRecord)?.prompts) || {};
16032
+ const promptRecord = asRecord5(promptsById[prompt.id]);
15490
16033
  const openedAt = Number(promptRecord?.openedAt) || 0;
15491
16034
  if (openedAt >= boundaryTimestamp) return true;
15492
16035
  }
@@ -15579,10 +16122,10 @@ ${modelOutputInstruction()}`
15579
16122
  );
15580
16123
  }
15581
16124
  const raw = await response.json();
15582
- const content = asRecord4(
15583
- asRecord4(raw.choices?.[0])?.message
16125
+ const content = asRecord5(
16126
+ asRecord5(raw.choices?.[0])?.message
15584
16127
  )?.content;
15585
- const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => asRecord4(part)?.text || "").join("") : "";
16128
+ const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => asRecord5(part)?.text || "").join("") : "";
15586
16129
  const parsed = extractJsonObject(text);
15587
16130
  if (!parsed) {
15588
16131
  if (attempt < 3) {
@@ -15634,17 +16177,17 @@ async function withTimeout(promise, ms, label) {
15634
16177
  }
15635
16178
  }
15636
16179
  function getActionSummary(liveDoc, jobId) {
15637
- const jobsById = asRecord4(asRecord4(liveDoc?.jobs)?.byId) || {};
15638
- const job = asRecord4(jobsById[jobId]);
16180
+ const jobsById = asRecord5(asRecord5(liveDoc?.jobs)?.byId) || {};
16181
+ const job = asRecord5(jobsById[jobId]);
15639
16182
  return Array.isArray(job?.actionSummary) ? job.actionSummary.filter(
15640
16183
  (line) => typeof line === "string"
15641
16184
  ) : [];
15642
16185
  }
15643
16186
  function normalizeHeapSnapshot2(heap) {
15644
16187
  return {
15645
- entriesByPath: asRecord4(heap?.entriesByPath) || {},
15646
- listsByName: asRecord4(heap?.listsByName) || {},
15647
- variablesByName: asRecord4(heap?.variablesByName) || {},
16188
+ entriesByPath: asRecord5(heap?.entriesByPath) || {},
16189
+ listsByName: asRecord5(heap?.listsByName) || {},
16190
+ variablesByName: asRecord5(heap?.variablesByName) || {},
15648
16191
  updatedAt: typeof heap?.updatedAt === "number" ? heap.updatedAt : Date.now()
15649
16192
  };
15650
16193
  }
@@ -15830,8 +16373,8 @@ async function runAgentEvalSuite(options) {
15830
16373
  );
15831
16374
  }
15832
16375
  const inspectionResults = [];
15833
- const stepChecks = asArray2(step.check);
15834
- const stepInspections = asArray2(step.inspect);
16376
+ const stepChecks = asArray3(step.check);
16377
+ const stepInspections = asArray3(step.inspect);
15835
16378
  const context = {
15836
16379
  conversation,
15837
16380
  environment: conversation.environment,
@@ -15844,7 +16387,7 @@ async function runAgentEvalSuite(options) {
15844
16387
  promptInteractions: completed.promptInteractions,
15845
16388
  result: completed.result,
15846
16389
  heap: normalizeHeapSnapshot2(
15847
- asRecord4(
16390
+ asRecord5(
15848
16391
  cloneJson(conversation.environment.document)?.heap
15849
16392
  )
15850
16393
  ),
@@ -16055,7 +16598,7 @@ function createAgentEvalHarness(options) {
16055
16598
  }
16056
16599
  function buildCheckContext(conversation, completed, turnDir) {
16057
16600
  const liveDoc = cloneJson(conversation.environment.document);
16058
- const heap = normalizeHeapSnapshot2(asRecord4(liveDoc?.heap));
16601
+ const heap = normalizeHeapSnapshot2(asRecord5(liveDoc?.heap));
16059
16602
  return {
16060
16603
  conversation,
16061
16604
  environment: conversation.environment,
@@ -16131,7 +16674,7 @@ function createAgentEvalHarness(options) {
16131
16674
  jobId: pending.job.id,
16132
16675
  result: resumed.result,
16133
16676
  stdout: [...pending.stdout, ...resumed.stdout],
16134
- sessionHeap: normalizeHeapSnapshot2(asRecord4(liveDoc?.heap))
16677
+ sessionHeap: normalizeHeapSnapshot2(asRecord5(liveDoc?.heap))
16135
16678
  });
16136
16679
  const responseText = presentation.responseText || pending.finalReply || "Done.";
16137
16680
  pending.conversation.history.push({
@@ -16341,7 +16884,7 @@ function createAgentEvalHarness(options) {
16341
16884
  const settledLiveDoc = cloneJson(
16342
16885
  conversation.environment.document
16343
16886
  );
16344
- const sessionHeap = normalizeHeapSnapshot2(asRecord4(settledLiveDoc?.heap));
16887
+ const sessionHeap = normalizeHeapSnapshot2(asRecord5(settledLiveDoc?.heap));
16345
16888
  const presentation = resolveJobPresentation({
16346
16889
  jobId: job.id,
16347
16890
  result: outcome.result,