@granular-software/sdk 0.4.23 → 0.4.25

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.
@@ -4547,6 +4547,7 @@ var Session = class {
4547
4547
  client;
4548
4548
  clientId;
4549
4549
  jobsMap = /* @__PURE__ */ new Map();
4550
+ pendingAgentMessagesByJobId = /* @__PURE__ */ new Map();
4550
4551
  eventListeners = /* @__PURE__ */ new Map();
4551
4552
  toolHandlers = /* @__PURE__ */ new Map();
4552
4553
  /** Tracks which tools are instance methods (className set, not static) */
@@ -4704,6 +4705,15 @@ var Session = class {
4704
4705
  createdAt: Date.now()
4705
4706
  });
4706
4707
  this.jobsMap.set(result.jobId, job);
4708
+ const pendingAgentMessages = this.pendingAgentMessagesByJobId.get(
4709
+ result.jobId
4710
+ );
4711
+ if (pendingAgentMessages && pendingAgentMessages.length > 0) {
4712
+ this.pendingAgentMessagesByJobId.delete(result.jobId);
4713
+ for (const message of pendingAgentMessages) {
4714
+ job.replayAgentMessage(message);
4715
+ }
4716
+ }
4707
4717
  return job;
4708
4718
  }
4709
4719
  /**
@@ -5105,6 +5115,22 @@ import { ${allImports} } from "./sandbox-tools";
5105
5115
  this.client.on("job.status", (data) => {
5106
5116
  this.emit("job:status", data);
5107
5117
  });
5118
+ this.client.on("job.agent_message", (data) => {
5119
+ const normalized = normalizeJobAgentMessageEnvelope(data);
5120
+ if (!normalized) return;
5121
+ if (this.jobsMap.has(normalized.jobId)) return;
5122
+ const pending = this.pendingAgentMessagesByJobId.get(normalized.jobId) || [];
5123
+ if (normalized.message.messageId && pending.some(
5124
+ (message) => message.messageId === normalized.message.messageId
5125
+ )) {
5126
+ return;
5127
+ }
5128
+ pending.push(normalized.message);
5129
+ this.pendingAgentMessagesByJobId.set(
5130
+ normalized.jobId,
5131
+ pending.slice(-25)
5132
+ );
5133
+ });
5108
5134
  this.client.on("exec.completed", (data) => {
5109
5135
  this.emit("exec:completed", data);
5110
5136
  });
@@ -5229,6 +5255,22 @@ function sanitizeFeedbackValue(value, depth = 0, seen = /* @__PURE__ */ new Weak
5229
5255
  }
5230
5256
  return truncateFeedbackString(String(value));
5231
5257
  }
5258
+ function normalizeJobAgentMessageEnvelope(data) {
5259
+ const d = data;
5260
+ if (typeof d?.jobId !== "string" || !d.jobId) {
5261
+ return null;
5262
+ }
5263
+ return {
5264
+ jobId: d.jobId,
5265
+ message: {
5266
+ messageId: d.messageId,
5267
+ kind: d.kind === "artifacts" ? "artifacts" : "text",
5268
+ reply: typeof d.reply === "string" ? d.reply : "",
5269
+ show: d.show,
5270
+ timestamp: d.timestamp || Date.now()
5271
+ }
5272
+ };
5273
+ }
5232
5274
  var JobImplementation = class {
5233
5275
  id;
5234
5276
  client;
@@ -5237,6 +5279,8 @@ var JobImplementation = class {
5237
5279
  _resolveResult;
5238
5280
  _rejectResult;
5239
5281
  eventListeners = /* @__PURE__ */ new Map();
5282
+ bufferedAgentMessages = [];
5283
+ bufferedAgentMessageIds = /* @__PURE__ */ new Set();
5240
5284
  metadata;
5241
5285
  constructor(id, client, initialState) {
5242
5286
  this.id = id;
@@ -5395,14 +5439,9 @@ var JobImplementation = class {
5395
5439
  }
5396
5440
  });
5397
5441
  this.client.on("job.agent_message", (data) => {
5398
- const d = data;
5399
- if (d.jobId === id) {
5400
- this.emit("agentMessage", {
5401
- messageId: d.messageId,
5402
- reply: typeof d.reply === "string" ? d.reply : "",
5403
- show: d.show,
5404
- timestamp: d.timestamp || Date.now()
5405
- });
5442
+ const normalized = normalizeJobAgentMessageEnvelope(data);
5443
+ if (normalized?.jobId === id) {
5444
+ this.captureAgentMessage(normalized.message);
5406
5445
  }
5407
5446
  });
5408
5447
  }
@@ -5429,6 +5468,14 @@ var JobImplementation = class {
5429
5468
  this.eventListeners.set(event, []);
5430
5469
  }
5431
5470
  this.eventListeners.get(event).push(handler);
5471
+ if (event === "agentMessage" && this.bufferedAgentMessages.length > 0) {
5472
+ for (const message of this.bufferedAgentMessages) {
5473
+ handler(message);
5474
+ }
5475
+ }
5476
+ }
5477
+ replayAgentMessage(message) {
5478
+ this.captureAgentMessage(message);
5432
5479
  }
5433
5480
  buildFeedbackMetadata() {
5434
5481
  const startedAt = this.metadata.startedAt;
@@ -5498,6 +5545,18 @@ var JobImplementation = class {
5498
5545
  handlers.forEach((h) => h(data));
5499
5546
  }
5500
5547
  }
5548
+ captureAgentMessage(message) {
5549
+ if (message.messageId && this.bufferedAgentMessageIds.has(message.messageId)) {
5550
+ return;
5551
+ }
5552
+ if (message.messageId) {
5553
+ this.bufferedAgentMessageIds.add(message.messageId);
5554
+ }
5555
+ this.bufferedAgentMessages = [...this.bufferedAgentMessages, message].slice(
5556
+ -25
5557
+ );
5558
+ this.emit("agentMessage", message);
5559
+ }
5501
5560
  };
5502
5561
 
5503
5562
  // src/endpoints.ts
@@ -13552,14 +13611,22 @@ function reviewGeneratedJobCode(code) {
13552
13611
  }
13553
13612
  }
13554
13613
  }
13555
- const hasConversationalReturn = /return\s+[`'"]/.test(normalized) || /\breply\s*:/.test(normalized) || /\bagent_message\s*\(/.test(normalized);
13614
+ const hasConversationalReturn = /return\s+[`'"]/.test(normalized) || /\breply\s*:/.test(normalized) || /\bagent_message\s*\(/.test(normalized) || /\bagent_text_message\s*\(/.test(normalized);
13556
13615
  const returnsObjectLiteral = /return\s+\{[\s\S]*?\}/.test(normalized);
13616
+ const returnsShowPayload = /return\s+\{[\s\S]*?\bshow\s*:/.test(normalized);
13557
13617
  const closesLoop = /loop\.close_loop\s*\(/.test(normalized);
13558
13618
  if (!hasConversationalReturn && returnsObjectLiteral && !closesLoop) {
13559
13619
  issues.push({
13560
13620
  code: "missing_user_reply",
13561
13621
  severity: "error",
13562
- 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."
13622
+ 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."
13623
+ });
13624
+ }
13625
+ if (returnsShowPayload) {
13626
+ issues.push({
13627
+ code: "return_show_not_for_ui",
13628
+ severity: "error",
13629
+ 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."
13563
13630
  });
13564
13631
  }
13565
13632
  return issues;
@@ -14404,7 +14471,7 @@ ${loopBlock}
14404
14471
 
14405
14472
  \u2500\u2500\u2500 CODE RULES \u2500\u2500\u2500
14406
14473
  - Import from \`./sandbox-tools\`.
14407
- - If you use \`heap\`, \`loop\`, or \`agent_message\`, import them explicitly from \`./sandbox-tools\`.
14474
+ - If you use \`heap\`, \`loop\`, \`agent_text_message\`, \`agent_heap_objects\`, or legacy \`agent_message\`, import them explicitly from \`./sandbox-tools\`.
14408
14475
  - Write top-level executable code with \`await\` at top level.
14409
14476
  - The generated job body must be plain runnable JavaScript. The DOMAIN TYPES block is only a reference for shapes and available methods.
14410
14477
  - 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.
@@ -14413,9 +14480,9 @@ ${loopBlock}
14413
14480
  - Never invent helper methods such as \`find(...)\` or unsupported parameters such as \`id\` when the typedefs require \`path\`.
14414
14481
  - Use \`ClassName.get({ path })\` only when you already know an object's graph path.
14415
14482
  - Use \`ClassName.count()\` when you only need a total.
14416
- - Use \`ClassName.page({ page, perPage, saveAs })\` when you need both records and pagination metadata like \`totalCount\` or \`hasMore\`.
14417
- - Use \`ClassName.list({ page, perPage, saveAs })\` to load one typed page of records. \`limit\` is only a legacy alias for \`perPage\`.
14418
- - Use \`for await (const item of ClassName.iterate({ perPage, maxItems }))\` for large batch jobs so you do not materialize the whole result set at once.
14483
+ - Use \`ClassName.page({ page, perPage, saveAs })\` when you need both records and pagination metadata like \`totalCount\` or \`hasMore\`. \`perPage\` defaults to \`100\` and larger values are clamped to \`100\`.
14484
+ - Use \`ClassName.list({ page, perPage, saveAs })\` to load one typed page of records. \`limit\` is only a legacy alias for \`perPage\`, \`perPage\` defaults to \`100\`, and larger values are clamped to \`100\`.
14485
+ - Use \`for await (const item of ClassName.iterate({ perPage, maxItems }))\` for large batch jobs so you do not materialize the whole result set at once. \`perPage\` defaults to \`100\` and larger values are clamped to \`100\`.
14419
14486
  - Instance methods: \`await instance.method_name(params)\`.
14420
14487
  - Static methods: \`await ClassName.static_method(params)\`.
14421
14488
  - Global effects: \`await effect_name(params)\`.
@@ -14446,21 +14513,31 @@ ${loopBlock}
14446
14513
  - \`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.
14447
14514
  - Avoid \`as any\` and other broad casts when the DOMAIN TYPES block already tells you the correct class or list type.
14448
14515
  - Prefer manipulating heap-backed instances and typed lists instead of returning raw JSON blobs or object IDs unless the user explicitly asks for them.
14449
- - 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.
14450
- - You may call \`agent_message(...)\` multiple times in one job to post several assistant messages while the job is still running.
14451
- - Prefer \`agent_message({ reply, show })\` when you want to leave a user-facing answer and optionally show heap-backed records in the UI.
14452
- - \`agent_message(...)\` also accepts \`content\`, \`message\`, or \`text\` instead of \`reply\`.
14453
- - \`agent_message({ show })\` may receive explicit refs or sandbox instances and arrays of sandbox instances. The runtime will convert those into UI references.
14454
- - When it helps the UI show specific heap-backed results, you may instead return:
14455
- \`{ reply: string, show: { entryPaths?: string[], listNames?: string[], variableNames?: string[] } }\`
14456
- - 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.
14516
+ - 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(...)\`.
14517
+ - Use \`agent_text_message("...")\` for all user-visible text shown in the UI.
14518
+ - Use \`agent_heap_objects(...)\` only when you want the UI to render heap-backed records or lists.
14519
+ - If you want to show both text and records, call \`agent_text_message(...)\` and \`agent_heap_objects(...)\` separately in whatever order fits the interaction.
14520
+ - \`agent_text_message(...)\` should be used with a plain text string in normal generated code.
14521
+ - \`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(...)\`.
14522
+ - 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.
14523
+ - Do not assume heap changes will be displayed automatically. If records should appear in the UI, you must call \`agent_heap_objects(...)\`.
14524
+ - 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.
14525
+ - Do not rely on the final return value for user-visible output. A plain return value is not considered a displayed UI answer.
14526
+ - \`agent_message(...)\` remains available as a legacy compatibility alias, but prefer \`agent_text_message(...)\` and \`agent_heap_objects(...)\` in new code.
14457
14527
  - Do not return bare structured JSON, low-level diagnostics, or database-shaped payloads as the final answer unless the user explicitly asks for them.
14458
14528
  - 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.
14459
14529
  - Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
14460
14530
  }
14461
14531
 
14462
14532
  // src/job-presentation.ts
14463
- var RESPONSE_KEYS = ["reply", "response", "text", "message", "summary", "answer"];
14533
+ var RESPONSE_KEYS = [
14534
+ "reply",
14535
+ "response",
14536
+ "text",
14537
+ "message",
14538
+ "summary",
14539
+ "answer"
14540
+ ];
14464
14541
  var ENTRY_KEY_CANDIDATES = ["entryPath", "path"];
14465
14542
  var ENTRY_ARRAY_KEY_CANDIDATES = ["entryPaths", "paths"];
14466
14543
  var LIST_KEY_CANDIDATES = ["listName"];
@@ -14502,15 +14579,22 @@ function pushStringArray(target, value) {
14502
14579
  }
14503
14580
  }
14504
14581
  function collectReferencesFromRecord(record, refs) {
14505
- for (const key of ENTRY_KEY_CANDIDATES) pushString(refs.entryPaths, record[key]);
14506
- for (const key of ENTRY_ARRAY_KEY_CANDIDATES) pushStringArray(refs.entryPaths, record[key]);
14507
- for (const key of LIST_KEY_CANDIDATES) pushString(refs.listNames, record[key]);
14508
- for (const key of LIST_ARRAY_KEY_CANDIDATES) pushStringArray(refs.listNames, record[key]);
14509
- for (const key of VARIABLE_KEY_CANDIDATES) pushString(refs.variableNames, record[key]);
14510
- for (const key of VARIABLE_ARRAY_KEY_CANDIDATES) pushStringArray(refs.variableNames, record[key]);
14582
+ for (const key of ENTRY_KEY_CANDIDATES)
14583
+ pushString(refs.entryPaths, record[key]);
14584
+ for (const key of ENTRY_ARRAY_KEY_CANDIDATES)
14585
+ pushStringArray(refs.entryPaths, record[key]);
14586
+ for (const key of LIST_KEY_CANDIDATES)
14587
+ pushString(refs.listNames, record[key]);
14588
+ for (const key of LIST_ARRAY_KEY_CANDIDATES)
14589
+ pushStringArray(refs.listNames, record[key]);
14590
+ for (const key of VARIABLE_KEY_CANDIDATES)
14591
+ pushString(refs.variableNames, record[key]);
14592
+ for (const key of VARIABLE_ARRAY_KEY_CANDIDATES)
14593
+ pushStringArray(refs.variableNames, record[key]);
14511
14594
  }
14512
14595
  function scanForHeapReferences(value, heap, refs, depth = 0, seen = /* @__PURE__ */ new Set()) {
14513
- if (value === null || value === void 0 || depth > 4 || seen.has(value)) return;
14596
+ if (value === null || value === void 0 || depth > 4 || seen.has(value))
14597
+ return;
14514
14598
  if (typeof value === "string") {
14515
14599
  const trimmed = value.trim();
14516
14600
  if (heap.entriesByPath?.[trimmed]) refs.entryPaths.add(trimmed);
@@ -14606,12 +14690,16 @@ function fallbackResponseText(entries, lists) {
14606
14690
  }
14607
14691
  function getJobRelatedEntries(heap, jobId) {
14608
14692
  return sortEntries(
14609
- Object.values(heap.entriesByPath || {}).filter((entry) => entry.relatedJobIds?.includes(jobId))
14693
+ Object.values(heap.entriesByPath || {}).filter(
14694
+ (entry) => entry.relatedJobIds?.includes(jobId)
14695
+ )
14610
14696
  );
14611
14697
  }
14612
14698
  function getJobRelatedLists(heap, jobId) {
14613
14699
  return sortLists(
14614
- Object.values(heap.listsByName || {}).filter((list) => list.relatedJobIds?.includes(jobId))
14700
+ Object.values(heap.listsByName || {}).filter(
14701
+ (list) => list.relatedJobIds?.includes(jobId)
14702
+ )
14615
14703
  );
14616
14704
  }
14617
14705
  function entriesFromLists(lists, heap) {
@@ -14628,15 +14716,18 @@ function resolveJobPresentation({
14628
14716
  jobId,
14629
14717
  result,
14630
14718
  stdout = [],
14631
- sessionHeap
14719
+ sessionHeap,
14720
+ allowExplicitArtifacts = true
14632
14721
  }) {
14633
14722
  const refs = {
14634
14723
  entryPaths: /* @__PURE__ */ new Set(),
14635
14724
  listNames: /* @__PURE__ */ new Set(),
14636
14725
  variableNames: /* @__PURE__ */ new Set()
14637
14726
  };
14638
- scanForHeapReferences(result, sessionHeap, refs);
14639
- resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
14727
+ if (allowExplicitArtifacts) {
14728
+ scanForHeapReferences(result, sessionHeap, refs);
14729
+ resolveVariablesToReferences(refs.variableNames, sessionHeap, refs);
14730
+ }
14640
14731
  const referencedLists = sortLists(
14641
14732
  [...refs.listNames].map((name) => sessionHeap.listsByName?.[name]).filter((list) => Boolean(list))
14642
14733
  );
@@ -14649,20 +14740,22 @@ function resolveJobPresentation({
14649
14740
  ...jobEntries,
14650
14741
  ...entriesFromLists(jobLists, sessionHeap)
14651
14742
  ]);
14652
- const lists = dedupeLists([...referencedLists, ...jobLists]);
14653
- const entries = dedupeEntries([
14743
+ const explicitLists = dedupeLists(referencedLists);
14744
+ const explicitEntries = dedupeEntries([
14654
14745
  ...referencedEntries,
14655
- ...entriesFromLists(referencedLists, sessionHeap),
14656
- ...jobEntries,
14657
- ...entriesFromLists(jobLists, sessionHeap)
14746
+ ...entriesFromLists(referencedLists, sessionHeap)
14658
14747
  ]);
14748
+ const hasExplicitArtifacts = allowExplicitArtifacts && (explicitEntries.length > 0 || explicitLists.length > 0);
14749
+ const lists = hasExplicitArtifacts ? explicitLists : jobLists;
14750
+ const entries = hasExplicitArtifacts ? explicitEntries : changedEntries;
14659
14751
  const responseText = extractResponseText(result, stdout) || fallbackResponseText(entries, lists);
14660
14752
  return {
14661
14753
  responseText,
14662
14754
  entries,
14663
14755
  lists,
14664
14756
  changedEntries,
14665
- changedLists: jobLists
14757
+ changedLists: jobLists,
14758
+ hasExplicitArtifacts
14666
14759
  };
14667
14760
  }
14668
14761