@granular-software/sdk 0.4.32 → 0.4.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -15222,7 +15222,8 @@ ${effectMetamodelTable}
15222
15222
  | \`getDomain()\`, \`getDomainTypes()\`, \`getDomainDocs()\`, \`getDomainDocumentation()\` | Domain summary and generated TypeScript / docs. |
15223
15223
  | \`getEffects()\` / \`getTools()\`, \`session.on("effects:changed", ...)\` | Effect catalog and live updates. |
15224
15224
  | \`checkReadiness()\`, \`on('readiness', ...)\` | Runtime readiness. |
15225
- | \`getHeap()\` | Session state snapshot (advanced). |
15225
+ | \`document\`, \`getHeap()\` | Current live session state from the WebSocket. |
15226
+ | \`messages.list()\`, \`timeline.list()\`, \`jobs.list/get()\`, \`heap.entries.list/get()\`, \`heap.lists.list/get()\`, \`transcript.list()\` | Durable session history and saved artifacts. |
15226
15227
  | \`disconnect()\` | Close the live runtime session. |
15227
15228
  | \`rpc(method, params)\` | Low-level session RPC (advanced). |
15228
15229
  | \`disconnect()\` | End the session. |
@@ -19413,6 +19414,540 @@ var JobImplementation = class {
19413
19414
  }
19414
19415
  };
19415
19416
 
19417
+ // src/job-presentation.ts
19418
+ var RESPONSE_KEYS = [
19419
+ "reply",
19420
+ "response",
19421
+ "text",
19422
+ "message",
19423
+ "summary",
19424
+ "answer"
19425
+ ];
19426
+ var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
19427
+ var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
19428
+ var LIST_KEY_CANDIDATES = ["listName"];
19429
+ var LIST_ARRAY_KEY_CANDIDATES = ["listNames"];
19430
+ var VARIABLE_KEY_CANDIDATES = ["variableName"];
19431
+ var VARIABLE_ARRAY_KEY_CANDIDATES = ["variableNames"];
19432
+ var UI_CONTAINER_KEYS = ["show", "display", "present", "ui"];
19433
+ function asRecord2(value) {
19434
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
19435
+ return value;
19436
+ }
19437
+ function normalizeText(value) {
19438
+ if (typeof value !== "string") return null;
19439
+ const trimmed = value.trim();
19440
+ if (!trimmed) return null;
19441
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
19442
+ return null;
19443
+ }
19444
+ return trimmed;
19445
+ }
19446
+ function humanTextFromStdout(stdout) {
19447
+ for (const line of [...stdout].reverse()) {
19448
+ const normalized = normalizeText(line);
19449
+ if (!normalized) continue;
19450
+ if (/^[A-Z_]+:/.test(normalized)) continue;
19451
+ return normalized;
19452
+ }
19453
+ return null;
19454
+ }
19455
+ function pushString(target, value) {
19456
+ if (typeof value === "string" && value.trim()) {
19457
+ target.add(value.trim());
19458
+ }
19459
+ }
19460
+ function pushStringArray(target, value) {
19461
+ if (!Array.isArray(value)) return;
19462
+ for (const item of value) {
19463
+ pushString(target, item);
19464
+ }
19465
+ }
19466
+ function collectReferencesFromRecord(record, refs) {
19467
+ for (const key of ENTRY_KEY_CANDIDATES)
19468
+ pushString(refs.entryPaths, record[key]);
19469
+ for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
19470
+ pushStringArray(refs.entryPaths, record[key]);
19471
+ for (const key of LIST_KEY_CANDIDATES)
19472
+ pushString(refs.listNames, record[key]);
19473
+ for (const key of LIST_ARRAY_KEY_CANDIDATES)
19474
+ pushStringArray(refs.listNames, record[key]);
19475
+ for (const key of VARIABLE_KEY_CANDIDATES)
19476
+ pushString(refs.variableNames, record[key]);
19477
+ for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
19478
+ pushStringArray(refs.variableNames, record[key]);
19479
+ }
19480
+ function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
19481
+ if (value === null || value === void 0 || depth > 4 || seen.has(value))
19482
+ return;
19483
+ if (typeof value === "string") {
19484
+ const trimmed = value.trim();
19485
+ if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
19486
+ if (heap.listsByName?.[trimmed]) refs.listNames.add(trimmed);
19487
+ if (heap.variablesByName?.[trimmed]) refs.variableNames.add(trimmed);
19488
+ return;
19489
+ }
19490
+ if (Array.isArray(value)) {
19491
+ seen.add(value);
19492
+ for (const item of value.slice(0, 24)) {
19493
+ scanForHeapReferences(item, heap, refs, depth + 1, seen);
19494
+ }
19495
+ return;
19496
+ }
19497
+ const record = asRecord2(value);
19498
+ if (!record) return;
19499
+ seen.add(value);
19500
+ collectReferencesFromRecord(record, refs);
19501
+ for (const key of UI_CONTAINER_KEYS) {
19502
+ const nested = asRecord2(record[key]);
19503
+ if (nested) collectReferencesFromRecord(nested, refs);
19504
+ }
19505
+ for (const nested of Object.values(record).slice(0, 24)) {
19506
+ scanForHeapReferences(nested, heap, refs, depth + 1, seen);
19507
+ }
19508
+ }
19509
+ function resolveVariablesToReferences(variableNames, heap, refs) {
19510
+ for (const variableName of variableNames) {
19511
+ const variable = heap.variablesByName?.[variableName];
19512
+ if (!variable) continue;
19513
+ if (variable.kind === "entry" && variable.entryPath) {
19514
+ refs.entryPaths.add(variable.entryPath);
19515
+ }
19516
+ if (variable.kind === "list" && variable.listName) {
19517
+ refs.listNames.add(variable.listName);
19518
+ }
19519
+ }
19520
+ }
19521
+ function sortEntries(entries) {
19522
+ return [...entries].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
19523
+ }
19524
+ function sortLists(lists) {
19525
+ return [...lists].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
19526
+ }
19527
+ function dedupeEntries(entries) {
19528
+ const seen = /* @__PURE__ */ new Set();
19529
+ const result = [];
19530
+ for (const entry of entries) {
19531
+ if (!entry?.path || seen.has(entry.path)) continue;
19532
+ seen.add(entry.path);
19533
+ result.push(entry);
19534
+ }
19535
+ return result;
19536
+ }
19537
+ function dedupeLists(lists) {
19538
+ const seen = /* @__PURE__ */ new Set();
19539
+ const result = [];
19540
+ for (const list of lists) {
19541
+ if (!list?.name || seen.has(list.name)) continue;
19542
+ seen.add(list.name);
19543
+ result.push(list);
19544
+ }
19545
+ return result;
19546
+ }
19547
+ function extractResponseText(result, stdout) {
19548
+ const directText = normalizeText(result);
19549
+ if (directText) return directText;
19550
+ const record = asRecord2(result);
19551
+ if (record) {
19552
+ for (const key of RESPONSE_KEYS) {
19553
+ const normalized = normalizeText(record[key]);
19554
+ if (normalized) return normalized;
19555
+ }
19556
+ for (const containerKey of UI_CONTAINER_KEYS) {
19557
+ const nested = asRecord2(record[containerKey]);
19558
+ if (!nested) continue;
19559
+ for (const key of RESPONSE_KEYS) {
19560
+ const normalized = normalizeText(nested[key]);
19561
+ if (normalized) return normalized;
19562
+ }
19563
+ }
19564
+ }
19565
+ return humanTextFromStdout(stdout);
19566
+ }
19567
+ function fallbackResponseText(entries, lists) {
19568
+ if (entries.length > 0) {
19569
+ return entries.length === 1 ? "I found one relevant record." : `I found ${entries.length} relevant records.`;
19570
+ }
19571
+ if (lists.length > 0) {
19572
+ const emptyOnly = lists.every((list) => (list.paths || []).length === 0);
19573
+ if (emptyOnly) {
19574
+ return lists.length === 1 ? "I saved one empty result set." : `I saved ${lists.length} empty result sets.`;
19575
+ }
19576
+ return lists.length === 1 ? "I saved one result set." : `I saved ${lists.length} result sets.`;
19577
+ }
19578
+ return null;
19579
+ }
19580
+ function getJobRelatedEntries(heap, jobId) {
19581
+ return sortEntries(
19582
+ Object.values(heap.entriesByPath || {}).filter(
19583
+ (entry) => entry.relatedJobIds?.includes(jobId)
19584
+ )
19585
+ );
19586
+ }
19587
+ function getJobRelatedLists(heap, jobId) {
19588
+ return sortLists(
19589
+ Object.values(heap.listsByName || {}).filter(
19590
+ (list) => list.relatedJobIds?.includes(jobId)
19591
+ )
19592
+ );
19593
+ }
19594
+ function entriesFromLists(lists, heap) {
19595
+ const entries = [];
19596
+ for (const list of lists) {
19597
+ for (const path6 of list.paths || []) {
19598
+ const entry = heap.entriesByPath?.[path6];
19599
+ if (entry) entries.push(entry);
19600
+ }
19601
+ }
19602
+ return entries;
19603
+ }
19604
+ function resolveJobPresentation({
19605
+ jobId,
19606
+ result,
19607
+ stdout = [],
19608
+ sessionHeap,
19609
+ allowExplicitArtifacts = true
19610
+ }) {
19611
+ const refs = {
19612
+ entryPaths: /* @__PURE__ */ new Set(),
19613
+ listNames: /* @__PURE__ */ new Set(),
19614
+ variableNames: /* @__PURE__ */ new Set()
19615
+ };
19616
+ if (allowExplicitArtifacts) {
19617
+ scanForHeapReferences(result, sessionHeap, refs);
19618
+ resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
19619
+ }
19620
+ const referencedLists = sortLists(
19621
+ [...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
19622
+ );
19623
+ const referencedEntries = sortEntries(
19624
+ [...refs.entryPaths].map((path6) => sessionHeap.entriesByPath?.[path6]).filter((entry) => Boolean(entry))
19625
+ );
19626
+ const jobLists = getJobRelatedLists(sessionHeap, jobId);
19627
+ const jobEntries = getJobRelatedEntries(sessionHeap, jobId);
19628
+ const changedEntries = dedupeEntries([
19629
+ ...jobEntries,
19630
+ ...entriesFromLists(jobLists, sessionHeap)
19631
+ ]);
19632
+ const explicitLists = dedupeLists(referencedLists);
19633
+ const explicitEntries = dedupeEntries([
19634
+ ...referencedEntries,
19635
+ ...entriesFromLists(referencedLists, sessionHeap)
19636
+ ]);
19637
+ const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
19638
+ const lists = hasExplicitArtifacts ? explicitLists : jobLists;
19639
+ const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
19640
+ const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
19641
+ return {
19642
+ responseText,
19643
+ entries,
19644
+ lists,
19645
+ changedEntries,
19646
+ changedLists: jobLists,
19647
+ hasExplicitArtifacts
19648
+ };
19649
+ }
19650
+
19651
+ // src/session-transcript.ts
19652
+ var EMPTY_HEAP = {
19653
+ entriesByPath: {},
19654
+ listsByName: {},
19655
+ variablesByName: {}};
19656
+ function asRecord3(value) {
19657
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
19658
+ return value;
19659
+ }
19660
+ function asArray(value) {
19661
+ return Array.isArray(value) ? value : [];
19662
+ }
19663
+ function asNumber(value) {
19664
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
19665
+ }
19666
+ function asString(value) {
19667
+ return typeof value === "string" ? value : void 0;
19668
+ }
19669
+ function trimString(value) {
19670
+ return typeof value === "string" ? value.trim() : "";
19671
+ }
19672
+ function normalizeShowRefs(value) {
19673
+ const record = asRecord3(value);
19674
+ if (!record) return void 0;
19675
+ const normalizeRefs = (input) => {
19676
+ if (!Array.isArray(input)) return void 0;
19677
+ const refs = Array.from(
19678
+ new Set(
19679
+ input.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean)
19680
+ )
19681
+ );
19682
+ return refs.length > 0 ? refs : void 0;
19683
+ };
19684
+ const show = {
19685
+ entryPaths: normalizeRefs(record.entryPaths),
19686
+ listNames: normalizeRefs(record.listNames),
19687
+ variableNames: normalizeRefs(record.variableNames)
19688
+ };
19689
+ return show.entryPaths || show.listNames || show.variableNames ? show : void 0;
19690
+ }
19691
+ function stringifyTranscriptValue(value, fallback2 = "") {
19692
+ if (typeof value === "string") {
19693
+ return value.trim() || fallback2;
19694
+ }
19695
+ if (typeof value === "boolean") {
19696
+ return value ? "Confirmed" : "Canceled";
19697
+ }
19698
+ if (value === void 0) {
19699
+ return fallback2;
19700
+ }
19701
+ try {
19702
+ const json = JSON.stringify(value, null, 2);
19703
+ if (!json || json === "undefined") return fallback2;
19704
+ return json.length > 2e3 ? `${json.slice(0, 2e3)}...` : json;
19705
+ } catch {
19706
+ return String(value);
19707
+ }
19708
+ }
19709
+ function buildArtifactHistory(show) {
19710
+ if (!show) return void 0;
19711
+ return `[Agent message]
19712
+ ${stringifyTranscriptValue({ show }, "")}`;
19713
+ }
19714
+ function normalizeConversationMessage(raw) {
19715
+ const record = asRecord3(raw);
19716
+ if (!record) return null;
19717
+ const role = record.role === "user" ? "user" : record.role === "assistant" ? "assistant" : null;
19718
+ if (!role) return null;
19719
+ const content = trimString(
19720
+ record.content ?? record.reply ?? record.message ?? record.text
19721
+ );
19722
+ const show = normalizeShowRefs(record.show);
19723
+ const id = asString(record.id) || crypto.randomUUID();
19724
+ const timestamp = asNumber(record.timestamp) || asNumber(record.ts) || 0;
19725
+ if (!content && !show) return null;
19726
+ return {
19727
+ id,
19728
+ role,
19729
+ content,
19730
+ timestamp,
19731
+ jobId: asString(record.jobId),
19732
+ promptId: asString(record.promptId),
19733
+ show,
19734
+ historyContent: role === "assistant" ? content ? `[Assistant reply]
19735
+ ${content}` : buildArtifactHistory(show) : void 0,
19736
+ source: "conversation"
19737
+ };
19738
+ }
19739
+ function normalizePromptEntries(jobId, rawPrompts, conversationPromptIds) {
19740
+ const promptsById = asRecord3(rawPrompts) || {};
19741
+ return Object.values(promptsById).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
19742
+ (left, right) => (asNumber(left.openedAt) || asNumber(left.answeredAt) || 0) - (asNumber(right.openedAt) || asNumber(right.answeredAt) || 0)
19743
+ ).flatMap((prompt3) => {
19744
+ const promptId = asString(prompt3.promptId);
19745
+ if (!promptId || conversationPromptIds.has(promptId)) return [];
19746
+ const title = trimString(prompt3.title);
19747
+ const message = trimString(prompt3.message);
19748
+ const assistantContent = message || title || "Input required";
19749
+ const openedAt = asNumber(prompt3.openedAt) || 0;
19750
+ const answeredAt = asNumber(prompt3.answeredAt) || openedAt;
19751
+ const entries = [
19752
+ {
19753
+ id: `prompt:${promptId}:assistant`,
19754
+ role: "assistant",
19755
+ content: assistantContent,
19756
+ timestamp: openedAt,
19757
+ jobId,
19758
+ promptId,
19759
+ historyContent: `[Assistant reply]
19760
+ ${assistantContent}`,
19761
+ source: "job_prompt"
19762
+ }
19763
+ ];
19764
+ if (Object.prototype.hasOwnProperty.call(prompt3, "answer")) {
19765
+ entries.push({
19766
+ id: `prompt:${promptId}:user`,
19767
+ role: "user",
19768
+ content: stringifyTranscriptValue(prompt3.answer, ""),
19769
+ timestamp: answeredAt,
19770
+ jobId,
19771
+ promptId,
19772
+ source: "job_prompt"
19773
+ });
19774
+ }
19775
+ return entries;
19776
+ });
19777
+ }
19778
+ function normalizeAgentMessageEntries(jobId, rawMessages) {
19779
+ return asArray(rawMessages).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
19780
+ (left, right) => (asNumber(left.timestamp) || asNumber(left.ts) || 0) - (asNumber(right.timestamp) || asNumber(right.ts) || 0)
19781
+ ).flatMap((message) => {
19782
+ const messageId = asString(message.messageId) || asString(message.id) || crypto.randomUUID();
19783
+ const timestamp = asNumber(message.timestamp) || asNumber(message.ts) || 0;
19784
+ const reply = trimString(
19785
+ message.reply ?? message.message ?? message.text ?? message.content
19786
+ );
19787
+ const show = normalizeShowRefs(message.show);
19788
+ const entries = [];
19789
+ if (reply) {
19790
+ entries.push({
19791
+ id: `agent:${messageId}:text`,
19792
+ role: "assistant",
19793
+ content: reply,
19794
+ timestamp,
19795
+ jobId,
19796
+ historyContent: `[Assistant reply]
19797
+ ${reply}`,
19798
+ source: "job_agent_message"
19799
+ });
19800
+ }
19801
+ if (show) {
19802
+ entries.push({
19803
+ id: `agent:${messageId}:artifacts`,
19804
+ role: "assistant",
19805
+ content: "",
19806
+ timestamp,
19807
+ jobId,
19808
+ show,
19809
+ historyContent: buildArtifactHistory(show),
19810
+ source: "job_agent_message"
19811
+ });
19812
+ }
19813
+ return entries;
19814
+ });
19815
+ }
19816
+ function buildJobFallbackEntries(jobId, job2, sessionHeap) {
19817
+ const timestamp = asNumber(job2.finishedAt) || asNumber(job2.startedAt) || asNumber(job2.submittedAt) || 0;
19818
+ const resultPreview = stringifyTranscriptValue(
19819
+ job2.result,
19820
+ "No job result recorded."
19821
+ );
19822
+ const presentation = resolveJobPresentation({
19823
+ jobId,
19824
+ result: job2.result,
19825
+ stdout: [],
19826
+ sessionHeap
19827
+ });
19828
+ const entries = [];
19829
+ const responseText = presentation.responseText || "";
19830
+ if (responseText) {
19831
+ entries.push({
19832
+ id: `job:${jobId}:result-text`,
19833
+ role: "assistant",
19834
+ content: responseText,
19835
+ timestamp,
19836
+ jobId,
19837
+ historyContent: `[Assistant reply]
19838
+ ${responseText}`,
19839
+ source: "job_result"
19840
+ });
19841
+ }
19842
+ const show = {
19843
+ entryPaths: presentation.entries.map((entry) => entry.path),
19844
+ listNames: presentation.lists.map((list) => list.name)
19845
+ };
19846
+ if (show.entryPaths && show.entryPaths.length > 0 || show.listNames && show.listNames.length > 0) {
19847
+ entries.push({
19848
+ id: `job:${jobId}:result-artifacts`,
19849
+ role: "assistant",
19850
+ content: "",
19851
+ timestamp,
19852
+ jobId,
19853
+ show,
19854
+ historyContent: buildArtifactHistory(show),
19855
+ source: "job_result"
19856
+ });
19857
+ }
19858
+ if (entries.length === 0 && trimString(job2.error)) {
19859
+ entries.push({
19860
+ id: `job:${jobId}:result-error`,
19861
+ role: "assistant",
19862
+ content: trimString(job2.error),
19863
+ timestamp,
19864
+ jobId,
19865
+ historyContent: `[Assistant reply]
19866
+ ${trimString(job2.error)}`,
19867
+ source: "job_result"
19868
+ });
19869
+ }
19870
+ if (entries.length === 0 && resultPreview && resultPreview !== "No job result recorded.") {
19871
+ entries.push({
19872
+ id: `job:${jobId}:result-preview`,
19873
+ role: "assistant",
19874
+ content: resultPreview,
19875
+ timestamp,
19876
+ jobId,
19877
+ historyContent: `[Assistant reply]
19878
+ ${resultPreview}`,
19879
+ source: "job_result"
19880
+ });
19881
+ }
19882
+ return entries;
19883
+ }
19884
+ function buildJobCodeEntry(jobId, job2) {
19885
+ const code = trimString(job2.source);
19886
+ if (!code) return null;
19887
+ const jobStatus = asString(job2.status);
19888
+ const error2 = jobStatus === "failed" || jobStatus === "canceled" || jobStatus === "timeout" ? trimString(job2.error) || `Job ${jobStatus}` : void 0;
19889
+ return {
19890
+ id: `job:${jobId}:code`,
19891
+ role: "assistant",
19892
+ content: "",
19893
+ timestamp: asNumber(job2.submittedAt) || asNumber(job2.startedAt) || asNumber(job2.finishedAt) || 0,
19894
+ jobId,
19895
+ code,
19896
+ jobStatus,
19897
+ jobResultPreview: stringifyTranscriptValue(job2.result, "No job result recorded."),
19898
+ error: error2,
19899
+ source: "job_code"
19900
+ };
19901
+ }
19902
+ function buildSessionTranscript(input) {
19903
+ const liveDoc = input.liveDoc || null;
19904
+ const sessionHeap = input.sessionHeap || EMPTY_HEAP;
19905
+ const transcript = [];
19906
+ const conversationMessages = asArray(asRecord3(liveDoc?.conversation)?.messages).map((message) => normalizeConversationMessage(message)).filter((message) => Boolean(message));
19907
+ const conversationPromptIds = new Set(
19908
+ conversationMessages.map((message) => message.promptId).filter((promptId) => Boolean(promptId))
19909
+ );
19910
+ const assistantConversationJobIds = new Set(
19911
+ conversationMessages.filter((message) => message.role === "assistant" && Boolean(message.jobId)).map((message) => message.jobId)
19912
+ );
19913
+ transcript.push(...conversationMessages);
19914
+ const jobsById = asRecord3(asRecord3(liveDoc?.jobs)?.byId) || {};
19915
+ const jobs = Object.values(jobsById).map((value) => asRecord3(value)).filter((value) => Boolean(value)).sort(
19916
+ (left, right) => (asNumber(left.submittedAt) || asNumber(left.startedAt) || asNumber(left.finishedAt) || 0) - (asNumber(right.submittedAt) || asNumber(right.startedAt) || asNumber(right.finishedAt) || 0)
19917
+ );
19918
+ for (const job2 of jobs) {
19919
+ const jobId = asString(job2.jobId);
19920
+ if (!jobId) continue;
19921
+ const codeEntry = buildJobCodeEntry(jobId, job2);
19922
+ if (codeEntry) {
19923
+ transcript.push(codeEntry);
19924
+ }
19925
+ transcript.push(
19926
+ ...normalizePromptEntries(jobId, job2.prompts, conversationPromptIds)
19927
+ );
19928
+ if (!assistantConversationJobIds.has(jobId)) {
19929
+ const agentEntries = normalizeAgentMessageEntries(jobId, job2.agentMessages);
19930
+ if (agentEntries.length > 0) {
19931
+ transcript.push(...agentEntries);
19932
+ } else {
19933
+ transcript.push(
19934
+ ...buildJobFallbackEntries(
19935
+ jobId,
19936
+ job2,
19937
+ sessionHeap
19938
+ )
19939
+ );
19940
+ }
19941
+ }
19942
+ }
19943
+ return transcript.sort((left, right) => {
19944
+ if (left.timestamp !== right.timestamp) {
19945
+ return left.timestamp - right.timestamp;
19946
+ }
19947
+ return left.id.localeCompare(right.id);
19948
+ });
19949
+ }
19950
+
19416
19951
  // src/effect-runtime.ts
19417
19952
  function computeEffectKey(effect) {
19418
19953
  const attachedClass = effect.className?.trim();
@@ -20109,7 +20644,9 @@ var Environment = class {
20109
20644
  { target: targetPath }
20110
20645
  );
20111
20646
  if (result.errors?.length) {
20112
- throw new Error(`attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`);
20647
+ throw new Error(
20648
+ `attach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
20649
+ );
20113
20650
  }
20114
20651
  }
20115
20652
  /**
@@ -20144,7 +20681,9 @@ var Environment = class {
20144
20681
  }`
20145
20682
  );
20146
20683
  if (result.errors?.length) {
20147
- throw new Error(`detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`);
20684
+ throw new Error(
20685
+ `detach failed: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
20686
+ );
20148
20687
  }
20149
20688
  }
20150
20689
  /**
@@ -20266,7 +20805,9 @@ var Environment = class {
20266
20805
  async _runGraphql(query, label) {
20267
20806
  const result = await this.graphql(query);
20268
20807
  if (result.errors?.length) {
20269
- throw new Error(`${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`);
20808
+ throw new Error(
20809
+ `${label}: ${result.errors[0]?.message ?? "Unknown GraphQL error"}`
20810
+ );
20270
20811
  }
20271
20812
  return result.data;
20272
20813
  }
@@ -20801,6 +21342,9 @@ var EnvironmentSession = class extends Session {
20801
21342
  get envName() {
20802
21343
  return this.environment.envName;
20803
21344
  }
21345
+ get tag() {
21346
+ return this.environment.tag;
21347
+ }
20804
21348
  get versionId() {
20805
21349
  return this.environment.versionId;
20806
21350
  }
@@ -20820,12 +21364,171 @@ var EnvironmentSession = class extends Session {
20820
21364
  return this.environment.feedback;
20821
21365
  }
20822
21366
  /**
20823
- * Return a plain JS snapshot of the synced session heap.
21367
+ * Return a plain JS copy of the synced session heap.
20824
21368
  */
20825
21369
  getHeap() {
20826
21370
  const doc = this.document;
20827
21371
  return normalizeHeapSnapshot(doc?.heap);
20828
21372
  }
21373
+ async sessionDataRequest(path6, query) {
21374
+ const searchParams = new URLSearchParams();
21375
+ for (const [key, value] of Object.entries(query || {})) {
21376
+ if (value !== null && typeof value !== "undefined" && value !== "") {
21377
+ searchParams.set(key, String(value));
21378
+ }
21379
+ }
21380
+ const queryString = searchParams.toString();
21381
+ const response = await fetch(
21382
+ `${this.environment.runtimeBaseUrl}/orchestrator/ws/sessions/${encodeURIComponent(this.sessionId)}${path6}${queryString ? `?${queryString}` : ""}`,
21383
+ {
21384
+ method: "GET",
21385
+ headers: {
21386
+ Authorization: `Bearer ${this.environment.authToken}`,
21387
+ "Content-Type": "application/json"
21388
+ }
21389
+ }
21390
+ );
21391
+ if (!response.ok) {
21392
+ const errorText = await response.text();
21393
+ throw new Error(
21394
+ `Session data API Error (${response.status}): ${errorText}`
21395
+ );
21396
+ }
21397
+ return response.json();
21398
+ }
21399
+ async collectAllSessionItems(listPage) {
21400
+ const items = [];
21401
+ let cursor = null;
21402
+ do {
21403
+ const page = await listPage({ limit: 500, cursor });
21404
+ items.push(...page.items);
21405
+ cursor = page.nextCursor;
21406
+ } while (cursor);
21407
+ return items;
21408
+ }
21409
+ /**
21410
+ * Fetch the live session document from the runtime DO.
21411
+ *
21412
+ * For history and saved artifacts, prefer the collection APIs on
21413
+ * `messages`, `timeline`, `jobs`, and `heap`.
21414
+ */
21415
+ async getDocument() {
21416
+ return this.sessionDataRequest("/document");
21417
+ }
21418
+ get messages() {
21419
+ return {
21420
+ list: (options = {}) => this.sessionDataRequest(
21421
+ "/messages",
21422
+ options
21423
+ )
21424
+ };
21425
+ }
21426
+ get timeline() {
21427
+ return {
21428
+ list: (options = {}) => this.sessionDataRequest(
21429
+ "/timeline",
21430
+ options
21431
+ )
21432
+ };
21433
+ }
21434
+ get jobs() {
21435
+ return {
21436
+ list: (options = {}) => this.sessionDataRequest(
21437
+ "/jobs",
21438
+ options
21439
+ ),
21440
+ get: (jobId) => this.sessionDataRequest(
21441
+ `/jobs/${encodeURIComponent(jobId)}`
21442
+ )
21443
+ };
21444
+ }
21445
+ get heap() {
21446
+ return {
21447
+ entries: {
21448
+ list: (options = {}) => this.sessionDataRequest(
21449
+ "/heap/entries",
21450
+ options
21451
+ ),
21452
+ get: (path6) => this.sessionDataRequest(
21453
+ `/heap/entries/${encodeURIComponent(path6)}`
21454
+ )
21455
+ },
21456
+ lists: {
21457
+ list: (options = {}) => this.sessionDataRequest(
21458
+ "/heap/lists",
21459
+ options
21460
+ ),
21461
+ get: (name) => this.sessionDataRequest(
21462
+ `/heap/lists/${encodeURIComponent(name)}`
21463
+ )
21464
+ }
21465
+ };
21466
+ }
21467
+ get transcript() {
21468
+ return {
21469
+ list: async (options = {}) => {
21470
+ const [messages, jobs, entries, lists] = await Promise.all([
21471
+ this.collectAllSessionItems(this.messages.list),
21472
+ this.collectAllSessionItems(
21473
+ (pageOptions) => this.jobs.list({ ...pageOptions, status: "all" })
21474
+ ),
21475
+ this.collectAllSessionItems(this.heap.entries.list),
21476
+ this.collectAllSessionItems(this.heap.lists.list)
21477
+ ]);
21478
+ const liveDoc = {
21479
+ conversation: { messages },
21480
+ jobs: {
21481
+ byId: Object.fromEntries(
21482
+ jobs.map((job2) => {
21483
+ const record = job2 && typeof job2 === "object" ? job2 : null;
21484
+ const id = typeof record?.jobId === "string" ? record.jobId : typeof record?.id === "string" ? record.id : null;
21485
+ return id ? [id, record] : null;
21486
+ }).filter(
21487
+ (entry) => Boolean(entry)
21488
+ )
21489
+ )
21490
+ }
21491
+ };
21492
+ const heap = normalizeHeapSnapshot({
21493
+ entriesByPath: Object.fromEntries(
21494
+ entries.map((entry) => {
21495
+ return entry?.path ? [
21496
+ entry.path,
21497
+ entry
21498
+ ] : null;
21499
+ }).filter(
21500
+ (entry) => Boolean(entry)
21501
+ )
21502
+ ),
21503
+ listsByName: Object.fromEntries(
21504
+ lists.map((list) => {
21505
+ return list?.name ? [list.name, list] : null;
21506
+ }).filter(
21507
+ (entry) => Boolean(entry)
21508
+ )
21509
+ ),
21510
+ variablesByName: this.getHeap().variablesByName,
21511
+ updatedAt: Date.now()
21512
+ });
21513
+ const allItems = buildSessionTranscript({
21514
+ liveDoc,
21515
+ sessionHeap: heap
21516
+ });
21517
+ const limit = Math.max(
21518
+ 1,
21519
+ Math.min(500, Math.floor(options.limit ?? 100))
21520
+ );
21521
+ const offset = typeof options.cursor === "string" ? Number.parseInt(options.cursor, 10) || 0 : 0;
21522
+ const items = allItems.slice(offset, offset + limit);
21523
+ const nextOffset = offset + items.length;
21524
+ return {
21525
+ items,
21526
+ nextCursor: nextOffset < allItems.length ? String(nextOffset) : null,
21527
+ totalCount: allItems.length
21528
+ };
21529
+ }
21530
+ };
21531
+ }
20829
21532
  async graphql(query, variables) {
20830
21533
  return this.environment.graphql(query, variables);
20831
21534
  }
@@ -22053,14 +22756,18 @@ async function resolveOntologyId(granular, ontology) {
22053
22756
  const { config } = createGranularClient();
22054
22757
  const requestedOntology = ontology ?? config.sandboxId;
22055
22758
  if (!requestedOntology) {
22056
- throw new Error("No ontology configured. Run `granular init` first or pass `--ontology`.");
22759
+ throw new Error(
22760
+ "No ontology configured. Run `granular init` first or pass `--ontology`."
22761
+ );
22057
22762
  }
22058
22763
  try {
22059
22764
  const sandbox = await granular.sandboxes.get(requestedOntology);
22060
22765
  return sandbox.sandboxId;
22061
22766
  } catch {
22062
22767
  const sandboxes = await granular.sandboxes.list();
22063
- const existing = sandboxes.items.find((item) => item.name === requestedOntology);
22768
+ const existing = sandboxes.items.find(
22769
+ (item) => item.name === requestedOntology
22770
+ );
22064
22771
  if (existing) {
22065
22772
  return existing.sandboxId;
22066
22773
  }
@@ -22068,7 +22775,8 @@ async function resolveOntologyId(granular, ontology) {
22068
22775
  throw new Error(`Ontology not found: ${requestedOntology}`);
22069
22776
  }
22070
22777
  function matchesEnvironmentName(environment, requested) {
22071
- return environment.environment === requested || environment.envName === requested;
22778
+ const managedPrefix = `__sdk__${requested}__`;
22779
+ return environment.environment === requested || environment.envName === requested || environment.environment?.startsWith(managedPrefix) || environment.envName?.startsWith(managedPrefix) || environment.tag?.name === requested || environment.buildPolicy?.tagName === requested || environment.tracking?.tagName === requested;
22072
22780
  }
22073
22781
  async function resolveEnvironmentData(granular, options) {
22074
22782
  if (options.environmentId) {
@@ -22077,7 +22785,9 @@ async function resolveEnvironmentData(granular, options) {
22077
22785
  const ontologyId = await resolveOntologyId(granular, options.ontology);
22078
22786
  const environmentName = options.environment ?? "dev";
22079
22787
  const environments = await granular.environments.list(ontologyId);
22080
- const existing = environments.find((environment) => matchesEnvironmentName(environment, environmentName));
22788
+ const existing = environments.find(
22789
+ (environment) => matchesEnvironmentName(environment, environmentName)
22790
+ );
22081
22791
  if (existing) {
22082
22792
  return existing;
22083
22793
  }
@@ -22109,7 +22819,9 @@ async function listSessionsForEnvironment(granular, environmentId, status) {
22109
22819
  async function connectRuntime(options) {
22110
22820
  if (options.sessionId) {
22111
22821
  const { granular: granular2 } = createGranularClient();
22112
- const environment2 = await granular2.connectSession({ sessionId: options.sessionId });
22822
+ const environment2 = await granular2.connectSession({
22823
+ sessionId: options.sessionId
22824
+ });
22113
22825
  return {
22114
22826
  granular: granular2,
22115
22827
  environment: environment2,
@@ -22120,7 +22832,9 @@ async function connectRuntime(options) {
22120
22832
  const { granular, config } = createGranularClient();
22121
22833
  const ontologyId = options.ontology ?? config.sandboxId;
22122
22834
  if (!ontologyId) {
22123
- throw new Error("No ontology configured. Run `granular init` first or pass `--ontology`.");
22835
+ throw new Error(
22836
+ "No ontology configured. Run `granular init` first or pass `--ontology`."
22837
+ );
22124
22838
  }
22125
22839
  const environmentHandle = await granular.connect({
22126
22840
  ontology: ontologyId,
@@ -22171,13 +22885,14 @@ function readJobCodeFromOptions(options) {
22171
22885
  // src/cli/commands/connect.ts
22172
22886
  async function connectTestCommand(options) {
22173
22887
  const emitJson = options.json === true;
22888
+ const requestedEnvironment = options.environment ?? "dev";
22174
22889
  if (!emitJson) {
22175
22890
  printHeader();
22176
22891
  }
22177
22892
  await withRuntimeConnection(
22178
22893
  {
22179
22894
  ontology: options.ontology,
22180
- environment: options.environment ?? "dev",
22895
+ environment: requestedEnvironment,
22181
22896
  userId: options.user,
22182
22897
  permissions: normalizePermissions(options.permissions)
22183
22898
  },
@@ -22187,7 +22902,7 @@ async function connectTestCommand(options) {
22187
22902
  ontologyId,
22188
22903
  sandboxId: environment.sandboxId,
22189
22904
  environmentId: environment.environmentId,
22190
- environment: environment.envName,
22905
+ environment: environment.tag || requestedEnvironment,
22191
22906
  sessionId: environment.sessionId,
22192
22907
  subjectId: environment.subjectId,
22193
22908
  versionId: environment.versionId
@@ -22200,7 +22915,7 @@ async function connectTestCommand(options) {
22200
22915
  console.log();
22201
22916
  keyValue({
22202
22917
  "Ontology ID": payload.ontologyId,
22203
- "Environment": payload.environment,
22918
+ Environment: payload.environment,
22204
22919
  "Environment ID": payload.environmentId,
22205
22920
  "Session ID": payload.sessionId,
22206
22921
  "Subject ID": payload.subjectId,
@@ -22267,7 +22982,9 @@ async function sessionDocCommand(options) {
22267
22982
  ontologyId,
22268
22983
  sessionId: environment.sessionId,
22269
22984
  environmentId: environment.environmentId,
22270
- document: documentToJson(environment.document)
22985
+ document: documentToJson(
22986
+ environment.document
22987
+ )
22271
22988
  };
22272
22989
  if (!emitJson) {
22273
22990
  step("Session document");
@@ -22279,6 +22996,7 @@ async function sessionDocCommand(options) {
22279
22996
  }
22280
22997
  async function sessionCreateCommand(options) {
22281
22998
  const emitJson = options.json === true;
22999
+ const requestedEnvironment = options.environment ?? "dev";
22282
23000
  if (!emitJson) {
22283
23001
  printHeader();
22284
23002
  }
@@ -22286,7 +23004,7 @@ async function sessionCreateCommand(options) {
22286
23004
  const permissions = normalizePermissions(options.permissions);
22287
23005
  const envData = await resolveEnvironmentData(granular, {
22288
23006
  ontology: options.ontology,
22289
- environment: options.environment ?? "dev",
23007
+ environment: requestedEnvironment,
22290
23008
  environmentId: options.environmentId,
22291
23009
  userId: options.user,
22292
23010
  permissions,
@@ -22300,7 +23018,7 @@ async function sessionCreateCommand(options) {
22300
23018
  ok: true,
22301
23019
  ontologyId: envData.sandboxId,
22302
23020
  environmentId: environment.environmentId,
22303
- environment: environment.envName,
23021
+ environment: environment.tag || envData.tag?.name || envData.buildPolicy.tagName || requestedEnvironment,
22304
23022
  sessionId: environment.sessionId,
22305
23023
  subjectId: environment.subjectId,
22306
23024
  versionId: environment.versionId
@@ -22313,7 +23031,7 @@ async function sessionCreateCommand(options) {
22313
23031
  console.log();
22314
23032
  keyValue({
22315
23033
  "Ontology ID": payload.ontologyId,
22316
- "Environment": payload.environment,
23034
+ Environment: payload.environment,
22317
23035
  "Environment ID": payload.environmentId,
22318
23036
  "Session ID": payload.sessionId,
22319
23037
  "Subject ID": payload.subjectId,
@@ -22331,21 +23049,26 @@ async function sessionCreateCommand(options) {
22331
23049
  async function sessionListCommand(options) {
22332
23050
  const emitJson = options.json === true;
22333
23051
  const status = options.status ?? "active";
23052
+ const requestedEnvironment = options.environment ?? "dev";
22334
23053
  if (!emitJson) {
22335
23054
  printHeader();
22336
23055
  }
22337
23056
  const { granular } = createGranularClient();
22338
23057
  const environmentData = await resolveEnvironmentData(granular, {
22339
23058
  ontology: options.ontology,
22340
- environment: options.environment ?? "dev",
23059
+ environment: requestedEnvironment,
22341
23060
  environmentId: options.environmentId,
22342
23061
  createIfMissing: false
22343
23062
  });
22344
- const items = await listSessionsForEnvironment(granular, environmentData.environmentId, status);
23063
+ const items = await listSessionsForEnvironment(
23064
+ granular,
23065
+ environmentData.environmentId,
23066
+ status
23067
+ );
22345
23068
  const payload = {
22346
23069
  ontologyId: environmentData.sandboxId,
22347
23070
  environmentId: environmentData.environmentId,
22348
- environment: environmentData.environment ?? environmentData.envName,
23071
+ environment: environmentData.tag?.name || environmentData.buildPolicy.tagName || requestedEnvironment,
22349
23072
  status,
22350
23073
  items
22351
23074
  };