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