@granular-software/sdk 0.4.24 → 0.4.26

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.
@@ -1,4 +1,4 @@
1
- import { P as Prompt, d as Environment, c as SessionHeapSnapshot, a$ as ManifestContent, ax as RecordObjectOptions, T as ToolWithHandler, G as Granular, C as ConnectOptions, y as CreateEnvironmentData, i as GranularOptions } from './client-CQFKsCTd.mjs';
1
+ import { P as Prompt, e as Environment, c as SessionHeapSnapshot, b3 as ManifestContent, aB as RecordObjectOptions, T as ToolWithHandler, G as Granular, C as ConnectOptions, z as CreateEnvironmentData, j as GranularOptions } from './client-Zoo8YITZ.mjs';
2
2
  import { GeneratedJobCodeIssue, HarnessControllerBudgets } from './agent-harness.mjs';
3
3
  import '@automerge/automerge';
4
4
  import '@automerge/automerge/slim';
@@ -1,4 +1,4 @@
1
- import { P as Prompt, d as Environment, c as SessionHeapSnapshot, a$ as ManifestContent, ax as RecordObjectOptions, T as ToolWithHandler, G as Granular, C as ConnectOptions, y as CreateEnvironmentData, i as GranularOptions } from './client-CQFKsCTd.js';
1
+ import { P as Prompt, e as Environment, c as SessionHeapSnapshot, b3 as ManifestContent, aB as RecordObjectOptions, T as ToolWithHandler, G as Granular, C as ConnectOptions, z as CreateEnvironmentData, j as GranularOptions } from './client-Zoo8YITZ.js';
2
2
  import { GeneratedJobCodeIssue, HarnessControllerBudgets } from './agent-harness.js';
3
3
  import '@automerge/automerge';
4
4
  import '@automerge/automerge/slim';
@@ -4572,6 +4572,7 @@ var Session = class {
4572
4572
  client;
4573
4573
  clientId;
4574
4574
  jobsMap = /* @__PURE__ */ new Map();
4575
+ pendingAgentMessagesByJobId = /* @__PURE__ */ new Map();
4575
4576
  eventListeners = /* @__PURE__ */ new Map();
4576
4577
  toolHandlers = /* @__PURE__ */ new Map();
4577
4578
  /** Tracks which tools are instance methods (className set, not static) */
@@ -4614,6 +4615,22 @@ var Session = class {
4614
4615
  }
4615
4616
  };
4616
4617
  }
4618
+ stringifyConversationValue(value) {
4619
+ if (typeof value === "string") {
4620
+ return value;
4621
+ }
4622
+ if (typeof value === "boolean") {
4623
+ return value ? "Confirmed" : "Canceled";
4624
+ }
4625
+ if (value === void 0) {
4626
+ return "";
4627
+ }
4628
+ try {
4629
+ return JSON.stringify(value, null, 2);
4630
+ } catch {
4631
+ return String(value);
4632
+ }
4633
+ }
4617
4634
  // --- Public API ---
4618
4635
  get document() {
4619
4636
  return this.client.doc;
@@ -4729,6 +4746,15 @@ var Session = class {
4729
4746
  createdAt: Date.now()
4730
4747
  });
4731
4748
  this.jobsMap.set(result.jobId, job);
4749
+ const pendingAgentMessages = this.pendingAgentMessagesByJobId.get(
4750
+ result.jobId
4751
+ );
4752
+ if (pendingAgentMessages && pendingAgentMessages.length > 0) {
4753
+ this.pendingAgentMessagesByJobId.delete(result.jobId);
4754
+ for (const message of pendingAgentMessages) {
4755
+ job.replayAgentMessage(message);
4756
+ }
4757
+ }
4732
4758
  return job;
4733
4759
  }
4734
4760
  /**
@@ -4755,6 +4781,20 @@ var Session = class {
4755
4781
  answer: resolvedAnswer,
4756
4782
  value: resolvedAnswer
4757
4783
  });
4784
+ try {
4785
+ const content = this.stringifyConversationValue(resolvedAnswer);
4786
+ if (content.trim()) {
4787
+ await this.appendConversationMessage({
4788
+ role: "user",
4789
+ content,
4790
+ promptId
4791
+ });
4792
+ }
4793
+ } catch {
4794
+ }
4795
+ }
4796
+ async appendConversationMessage(input) {
4797
+ return this.client.call("conversation.append", input);
4758
4798
  }
4759
4799
  /**
4760
4800
  * Get the current list of available effects.
@@ -5130,6 +5170,22 @@ import { ${allImports} } from "./sandbox-tools";
5130
5170
  this.client.on("job.status", (data) => {
5131
5171
  this.emit("job:status", data);
5132
5172
  });
5173
+ this.client.on("job.agent_message", (data) => {
5174
+ const normalized = normalizeJobAgentMessageEnvelope(data);
5175
+ if (!normalized) return;
5176
+ if (this.jobsMap.has(normalized.jobId)) return;
5177
+ const pending = this.pendingAgentMessagesByJobId.get(normalized.jobId) || [];
5178
+ if (normalized.message.messageId && pending.some(
5179
+ (message) => message.messageId === normalized.message.messageId
5180
+ )) {
5181
+ return;
5182
+ }
5183
+ pending.push(normalized.message);
5184
+ this.pendingAgentMessagesByJobId.set(
5185
+ normalized.jobId,
5186
+ pending.slice(-25)
5187
+ );
5188
+ });
5133
5189
  this.client.on("exec.completed", (data) => {
5134
5190
  this.emit("exec:completed", data);
5135
5191
  });
@@ -5254,6 +5310,22 @@ function sanitizeFeedbackValue(value, depth = 0, seen = /* @__PURE__ */ new Weak
5254
5310
  }
5255
5311
  return truncateFeedbackString(String(value));
5256
5312
  }
5313
+ function normalizeJobAgentMessageEnvelope(data) {
5314
+ const d = data;
5315
+ if (typeof d?.jobId !== "string" || !d.jobId) {
5316
+ return null;
5317
+ }
5318
+ return {
5319
+ jobId: d.jobId,
5320
+ message: {
5321
+ messageId: d.messageId,
5322
+ kind: d.kind === "artifacts" ? "artifacts" : "text",
5323
+ reply: typeof d.reply === "string" ? d.reply : "",
5324
+ show: d.show,
5325
+ timestamp: d.timestamp || Date.now()
5326
+ }
5327
+ };
5328
+ }
5257
5329
  var JobImplementation = class {
5258
5330
  id;
5259
5331
  client;
@@ -5262,6 +5334,8 @@ var JobImplementation = class {
5262
5334
  _resolveResult;
5263
5335
  _rejectResult;
5264
5336
  eventListeners = /* @__PURE__ */ new Map();
5337
+ bufferedAgentMessages = [];
5338
+ bufferedAgentMessageIds = /* @__PURE__ */ new Set();
5265
5339
  metadata;
5266
5340
  constructor(id, client, initialState) {
5267
5341
  this.id = id;
@@ -5420,14 +5494,9 @@ var JobImplementation = class {
5420
5494
  }
5421
5495
  });
5422
5496
  this.client.on("job.agent_message", (data) => {
5423
- const d = data;
5424
- if (d.jobId === id) {
5425
- this.emit("agentMessage", {
5426
- messageId: d.messageId,
5427
- reply: typeof d.reply === "string" ? d.reply : "",
5428
- show: d.show,
5429
- timestamp: d.timestamp || Date.now()
5430
- });
5497
+ const normalized = normalizeJobAgentMessageEnvelope(data);
5498
+ if (normalized?.jobId === id) {
5499
+ this.captureAgentMessage(normalized.message);
5431
5500
  }
5432
5501
  });
5433
5502
  }
@@ -5454,6 +5523,14 @@ var JobImplementation = class {
5454
5523
  this.eventListeners.set(event, []);
5455
5524
  }
5456
5525
  this.eventListeners.get(event).push(handler);
5526
+ if (event === "agentMessage" && this.bufferedAgentMessages.length > 0) {
5527
+ for (const message of this.bufferedAgentMessages) {
5528
+ handler(message);
5529
+ }
5530
+ }
5531
+ }
5532
+ replayAgentMessage(message) {
5533
+ this.captureAgentMessage(message);
5457
5534
  }
5458
5535
  buildFeedbackMetadata() {
5459
5536
  const startedAt = this.metadata.startedAt;
@@ -5523,6 +5600,18 @@ var JobImplementation = class {
5523
5600
  handlers.forEach((h) => h(data));
5524
5601
  }
5525
5602
  }
5603
+ captureAgentMessage(message) {
5604
+ if (message.messageId && this.bufferedAgentMessageIds.has(message.messageId)) {
5605
+ return;
5606
+ }
5607
+ if (message.messageId) {
5608
+ this.bufferedAgentMessageIds.add(message.messageId);
5609
+ }
5610
+ this.bufferedAgentMessages = [...this.bufferedAgentMessages, message].slice(
5611
+ -25
5612
+ );
5613
+ this.emit("agentMessage", message);
5614
+ }
5526
5615
  };
5527
5616
 
5528
5617
  // src/endpoints.ts
@@ -13577,14 +13666,22 @@ function reviewGeneratedJobCode(code) {
13577
13666
  }
13578
13667
  }
13579
13668
  }
13580
- const hasConversationalReturn = /return\s+[`'"]/.test(normalized) || /\breply\s*:/.test(normalized) || /\bagent_message\s*\(/.test(normalized);
13669
+ const hasConversationalReturn = /return\s+[`'"]/.test(normalized) || /\breply\s*:/.test(normalized) || /\bagent_message\s*\(/.test(normalized) || /\bagent_text_message\s*\(/.test(normalized);
13581
13670
  const returnsObjectLiteral = /return\s+\{[\s\S]*?\}/.test(normalized);
13671
+ const returnsShowPayload = /return\s+\{[\s\S]*?\bshow\s*:/.test(normalized);
13582
13672
  const closesLoop = /loop\.close_loop\s*\(/.test(normalized);
13583
13673
  if (!hasConversationalReturn && returnsObjectLiteral && !closesLoop) {
13584
13674
  issues.push({
13585
13675
  code: "missing_user_reply",
13586
13676
  severity: "error",
13587
- message: "User-facing jobs must end with a natural-language answer. Return a short string, an object with a top-level `reply` string, or use agent_message({ reply, show }). Do not end with bare structured JSON."
13677
+ message: "User-facing jobs must end with a natural-language answer. Return a short string, an object with a top-level `reply` string, or post text with agent_text_message(...). Do not end with bare structured JSON."
13678
+ });
13679
+ }
13680
+ if (returnsShowPayload) {
13681
+ issues.push({
13682
+ code: "return_show_not_for_ui",
13683
+ severity: "error",
13684
+ message: "Do not use the final return value to send UI record refs through `show`. Use agent_heap_objects(...) for heap-backed UI, then return plain text if you still want a final textual answer."
13588
13685
  });
13589
13686
  }
13590
13687
  return issues;
@@ -14429,7 +14526,7 @@ ${loopBlock}
14429
14526
 
14430
14527
  \u2500\u2500\u2500 CODE RULES \u2500\u2500\u2500
14431
14528
  - Import from \`./sandbox-tools\`.
14432
- - If you use \`heap\`, \`loop\`, or \`agent_message\`, import them explicitly from \`./sandbox-tools\`.
14529
+ - If you use \`heap\`, \`loop\`, \`agent_text_message\`, \`agent_heap_objects\`, or legacy \`agent_message\`, import them explicitly from \`./sandbox-tools\`.
14433
14530
  - Write top-level executable code with \`await\` at top level.
14434
14531
  - The generated job body must be plain runnable JavaScript. The DOMAIN TYPES block is only a reference for shapes and available methods.
14435
14532
  - Do not write TypeScript-only syntax in executable code: no type annotations, no interfaces, no enums, no \`as Type\` casts, no \`satisfies\`, and no generic type parameters in code.
@@ -14471,21 +14568,31 @@ ${loopBlock}
14471
14568
  - \`loop.close_loop(...)\`: record the current workflow outcome with a short summary before stopping. Do not call it in the same job that opens a new user prompt unless the workflow is explicitly blocked. This does not end the session forever.
14472
14569
  - Avoid \`as any\` and other broad casts when the DOMAIN TYPES block already tells you the correct class or list type.
14473
14570
  - Prefer manipulating heap-backed instances and typed lists instead of returning raw JSON blobs or object IDs unless the user explicitly asks for them.
14474
- - If the user expects an answer after the job runs, the final \`return\` value must be either a short natural-language string or an object with a top-level \`reply\` string.
14475
- - You may call \`agent_message(...)\` multiple times in one job to post several assistant messages while the job is still running.
14476
- - Prefer \`agent_message({ reply, show })\` when you want to leave a user-facing answer and optionally show heap-backed records in the UI.
14477
- - \`agent_message(...)\` also accepts \`content\`, \`message\`, or \`text\` instead of \`reply\`.
14478
- - \`agent_message({ show })\` may receive explicit refs or sandbox instances and arrays of sandbox instances. The runtime will convert those into UI references.
14479
- - When it helps the UI show specific heap-backed results, you may instead return:
14480
- \`{ reply: string, show: { entryPaths?: string[], listNames?: string[], variableNames?: string[] } }\`
14481
- - If you create or load objects the user should see, save them in the heap and return references to them through \`show\` instead of serializing full objects.
14571
+ - Every job that intends to answer the user must emit at least one explicit UI message with \`agent_text_message(...)\` and/or \`agent_heap_objects(...)\`.
14572
+ - Use \`agent_text_message("...")\` for all user-visible text shown in the UI.
14573
+ - Use \`agent_heap_objects(...)\` only when you want the UI to render heap-backed records or lists.
14574
+ - If you want to show both text and records, call \`agent_text_message(...)\` and \`agent_heap_objects(...)\` separately in whatever order fits the interaction.
14575
+ - \`agent_text_message(...)\` should be used with a plain text string in normal generated code.
14576
+ - \`agent_heap_objects(...)\` should point at heap-backed values: explicit \`entryPaths\` / \`listNames\` / \`variableNames\`, a named list saved with \`saveAs\`, or values read back from \`heap.getVar(...)\`.
14577
+ - If you just fetched records and want to show them in the UI, save or reference them through the heap first, then call \`agent_heap_objects(...)\`. Do not try to hand-build UI payloads in job code.
14578
+ - Do not assume heap changes will be displayed automatically. If records should appear in the UI, you must call \`agent_heap_objects(...)\`.
14579
+ - Never write \`return { reply, show }\` or \`return { show: ... }\` for UI. If you want the UI to render records or lists, call \`agent_heap_objects(...)\` instead.
14580
+ - Do not rely on the final return value for user-visible output. A plain return value is not considered a displayed UI answer.
14581
+ - \`agent_message(...)\` remains available as a legacy compatibility alias, but prefer \`agent_text_message(...)\` and \`agent_heap_objects(...)\` in new code.
14482
14582
  - Do not return bare structured JSON, low-level diagnostics, or database-shaped payloads as the final answer unless the user explicitly asks for them.
14483
14583
  - Prefer simple executable JavaScript over clever interpolation. Avoid nested template literals or unusually dense inline expressions when a small temporary variable or string concatenation would be clearer and safer.
14484
14584
  - Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
14485
14585
  }
14486
14586
 
14487
14587
  // src/job-presentation.ts
14488
- var RESPONSE_KEYS = ["reply", "response", "text", "message", "summary", "answer"];
14588
+ var RESPONSE_KEYS = [
14589
+ "reply",
14590
+ "response",
14591
+ "text",
14592
+ "message",
14593
+ "summary",
14594
+ "answer"
14595
+ ];
14489
14596
  var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
14490
14597
  var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
14491
14598
  var LIST_KEY_CANDIDATES = ["listName"];
@@ -14527,15 +14634,22 @@ function pushStringArray(target, value) {
14527
14634
  }
14528
14635
  }
14529
14636
  function collectReferencesFromRecord(record, refs) {
14530
- for (const key of ENTRY_KEY_CANDIDATES) pushString(refs.entryPaths, record[key]);
14531
- for (const key of ENTRY_ARRAY_KEY_CANDIDATES) pushStringArray(refs.entryPaths, record[key]);
14532
- for (const key of LIST_KEY_CANDIDATES) pushString(refs.listNames, record[key]);
14533
- for (const key of LIST_ARRAY_KEY_CANDIDATES) pushStringArray(refs.listNames, record[key]);
14534
- for (const key of VARIABLE_KEY_CANDIDATES) pushString(refs.variableNames, record[key]);
14535
- for (const key of VARIABLE_ARRAY_KEY_CANDIDATES) pushStringArray(refs.variableNames, record[key]);
14637
+ for (const key of ENTRY_KEY_CANDIDATES)
14638
+ pushString(refs.entryPaths, record[key]);
14639
+ for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
14640
+ pushStringArray(refs.entryPaths, record[key]);
14641
+ for (const key of LIST_KEY_CANDIDATES)
14642
+ pushString(refs.listNames, record[key]);
14643
+ for (const key of LIST_ARRAY_KEY_CANDIDATES)
14644
+ pushStringArray(refs.listNames, record[key]);
14645
+ for (const key of VARIABLE_KEY_CANDIDATES)
14646
+ pushString(refs.variableNames, record[key]);
14647
+ for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
14648
+ pushStringArray(refs.variableNames, record[key]);
14536
14649
  }
14537
14650
  function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
14538
- if (value === null || value === void 0 || depth > 4 || seen.has(value)) return;
14651
+ if (value === null || value === void 0 || depth > 4 || seen.has(value))
14652
+ return;
14539
14653
  if (typeof value === "string") {
14540
14654
  const trimmed = value.trim();
14541
14655
  if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
@@ -14631,12 +14745,16 @@ function fallbackResponseText(entries, lists) {
14631
14745
  }
14632
14746
  function getJobRelatedEntries(heap, jobId) {
14633
14747
  return sortEntries(
14634
- Object.values(heap.entriesByPath || {}).filter((entry) => entry.relatedJobIds?.includes(jobId))
14748
+ Object.values(heap.entriesByPath || {}).filter(
14749
+ (entry) => entry.relatedJobIds?.includes(jobId)
14750
+ )
14635
14751
  );
14636
14752
  }
14637
14753
  function getJobRelatedLists(heap, jobId) {
14638
14754
  return sortLists(
14639
- Object.values(heap.listsByName || {}).filter((list) => list.relatedJobIds?.includes(jobId))
14755
+ Object.values(heap.listsByName || {}).filter(
14756
+ (list) => list.relatedJobIds?.includes(jobId)
14757
+ )
14640
14758
  );
14641
14759
  }
14642
14760
  function entriesFromLists(lists, heap) {
@@ -14653,15 +14771,18 @@ function resolveJobPresentation({
14653
14771
  jobId,
14654
14772
  result,
14655
14773
  stdout = [],
14656
- sessionHeap
14774
+ sessionHeap,
14775
+ allowExplicitArtifacts = true
14657
14776
  }) {
14658
14777
  const refs = {
14659
14778
  entryPaths: /* @__PURE__ */ new Set(),
14660
14779
  listNames: /* @__PURE__ */ new Set(),
14661
14780
  variableNames: /* @__PURE__ */ new Set()
14662
14781
  };
14663
- scanForHeapReferences(result, sessionHeap, refs);
14664
- resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
14782
+ if (allowExplicitArtifacts) {
14783
+ scanForHeapReferences(result, sessionHeap, refs);
14784
+ resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
14785
+ }
14665
14786
  const referencedLists = sortLists(
14666
14787
  [...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
14667
14788
  );
@@ -14674,20 +14795,22 @@ function resolveJobPresentation({
14674
14795
  ...jobEntries,
14675
14796
  ...entriesFromLists(jobLists, sessionHeap)
14676
14797
  ]);
14677
- const lists = dedupeLists([...referencedLists, ...jobLists]);
14678
- const entries = dedupeEntries([
14798
+ const explicitLists = dedupeLists(referencedLists);
14799
+ const explicitEntries = dedupeEntries([
14679
14800
  ...referencedEntries,
14680
- ...entriesFromLists(referencedLists, sessionHeap),
14681
- ...jobEntries,
14682
- ...entriesFromLists(jobLists, sessionHeap)
14801
+ ...entriesFromLists(referencedLists, sessionHeap)
14683
14802
  ]);
14803
+ const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
14804
+ const lists = hasExplicitArtifacts ? explicitLists : jobLists;
14805
+ const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
14684
14806
  const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
14685
14807
  return {
14686
14808
  responseText,
14687
14809
  entries,
14688
14810
  lists,
14689
14811
  changedEntries,
14690
- changedLists: jobLists
14812
+ changedLists: jobLists,
14813
+ hasExplicitArtifacts
14691
14814
  };
14692
14815
  }
14693
14816