@granular-software/sdk 0.4.21 → 0.4.23

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/index.mjs CHANGED
@@ -4685,7 +4685,9 @@ var Session = class {
4685
4685
  }
4686
4686
  }
4687
4687
  if (!revision) {
4688
- throw new Error("No domain revision available. Register live effects or ensure the build schema is activated.");
4688
+ throw new Error(
4689
+ "No domain revision available. Register live effects or ensure the build schema is activated."
4690
+ );
4689
4691
  }
4690
4692
  const result = await this.client.call("job.submit", {
4691
4693
  domainRevision: revision,
@@ -4721,7 +4723,11 @@ var Session = class {
4721
4723
  const prompt = this.promptCache.get(promptId);
4722
4724
  const resolvedAnswer = resolvePromptAnswer(prompt, answer);
4723
4725
  this.promptCache.delete(promptId);
4724
- await this.client.call("prompt.answer", { promptId, answer: resolvedAnswer, value: resolvedAnswer });
4726
+ await this.client.call("prompt.answer", {
4727
+ promptId,
4728
+ answer: resolvedAnswer,
4729
+ value: resolvedAnswer
4730
+ });
4725
4731
  }
4726
4732
  /**
4727
4733
  * Get the current list of available effects.
@@ -4753,7 +4759,8 @@ var Session = class {
4753
4759
  for (const tool of cat.tools) {
4754
4760
  if (!tool?.name) continue;
4755
4761
  const existing = toolMap.get(tool.name);
4756
- if (existing?.publishedAt && cat.publishedAt && existing.publishedAt > cat.publishedAt) continue;
4762
+ if (existing?.publishedAt && cat.publishedAt && existing.publishedAt > cat.publishedAt)
4763
+ continue;
4757
4764
  const isLocal = clientId === this.clientId;
4758
4765
  const ready = isLocal ? this.effects.has(tool.name) || this.toolHandlers.has(tool.name) || this.instanceTools.has(tool.name) : true;
4759
4766
  toolMap.set(tool.name, {
@@ -4791,7 +4798,10 @@ var Session = class {
4791
4798
  return () => {
4792
4799
  const listeners = this.eventListeners.get("effects:changed");
4793
4800
  if (listeners) {
4794
- this.eventListeners.set("effects:changed", listeners.filter((h) => h !== handler));
4801
+ this.eventListeners.set(
4802
+ "effects:changed",
4803
+ listeners.filter((h) => h !== handler)
4804
+ );
4795
4805
  }
4796
4806
  };
4797
4807
  }
@@ -4807,7 +4817,10 @@ var Session = class {
4807
4817
  return () => {
4808
4818
  const listeners = this.eventListeners.get("tools:changed");
4809
4819
  if (listeners) {
4810
- this.eventListeners.set("tools:changed", listeners.filter((h) => h !== handler));
4820
+ this.eventListeners.set(
4821
+ "tools:changed",
4822
+ listeners.filter((h) => h !== handler)
4823
+ );
4811
4824
  }
4812
4825
  };
4813
4826
  }
@@ -4815,11 +4828,16 @@ var Session = class {
4815
4828
  * Get the current domain state and available tools
4816
4829
  */
4817
4830
  async getDomain() {
4818
- const summary = await this.client.call("domain.getSummary", {});
4831
+ const summary = await this.client.call(
4832
+ "domain.getSummary",
4833
+ {}
4834
+ );
4819
4835
  if (summary.activeDomainRevision) {
4820
4836
  this.currentDomainRevision = summary.activeDomainRevision;
4821
4837
  } else {
4822
- this.currentDomainRevision = this.extractDomainRevisionFromDoc(this.client.doc);
4838
+ this.currentDomainRevision = this.extractDomainRevisionFromDoc(
4839
+ this.client.doc
4840
+ );
4823
4841
  }
4824
4842
  return summary;
4825
4843
  }
@@ -4994,20 +5012,31 @@ import { ${allImports} } from "./sandbox-tools";
4994
5012
  off(event, handler) {
4995
5013
  const handlers = this.eventListeners.get(event);
4996
5014
  if (handlers) {
4997
- this.eventListeners.set(event, handlers.filter((h) => h !== handler));
5015
+ this.eventListeners.set(
5016
+ event,
5017
+ handlers.filter((h) => h !== handler)
5018
+ );
4998
5019
  }
4999
5020
  }
5000
5021
  // --- Internal ---
5001
5022
  setupToolInvokeHandler() {
5002
5023
  this.client.registerRpcHandler("tool.invoke", async (params) => {
5003
5024
  const { callId, toolName, input } = params;
5004
- this.emit("effect:invoke", { callId, effectKey: toolName, toolName, input });
5025
+ this.emit("effect:invoke", {
5026
+ callId,
5027
+ effectKey: toolName,
5028
+ toolName,
5029
+ input
5030
+ });
5005
5031
  this.emit("tool:invoke", { callId, toolName, input });
5006
5032
  const handler = this.toolHandlers.get(toolName);
5007
5033
  if (!handler) {
5008
5034
  await this.client.call("tool.result", {
5009
5035
  callId,
5010
- error: { code: "TOOL_NOT_FOUND", message: `Tool handler not found: ${toolName}` }
5036
+ error: {
5037
+ code: "TOOL_NOT_FOUND",
5038
+ message: `Tool handler not found: ${toolName}`
5039
+ }
5011
5040
  });
5012
5041
  return;
5013
5042
  }
@@ -5016,7 +5045,11 @@ import { ${allImports} } from "./sandbox-tools";
5016
5045
  const invocationContext = this.buildLegacyEffectContext();
5017
5046
  if (this.instanceTools.has(toolName) && input && typeof input === "object" && "_objectId" in input) {
5018
5047
  const { _objectId, ...restParams } = input;
5019
- result = await handler(_objectId, restParams, invocationContext);
5048
+ result = await handler(
5049
+ _objectId,
5050
+ restParams,
5051
+ invocationContext
5052
+ );
5020
5053
  } else {
5021
5054
  result = await handler(input, invocationContext);
5022
5055
  }
@@ -5028,7 +5061,11 @@ import { ${allImports} } from "./sandbox-tools";
5028
5061
  });
5029
5062
  } catch (error) {
5030
5063
  const errorMessage = error instanceof Error ? error.message : String(error);
5031
- this.emit("effect:result", { callId, effectKey: toolName, error: errorMessage });
5064
+ this.emit("effect:result", {
5065
+ callId,
5066
+ effectKey: toolName,
5067
+ error: errorMessage
5068
+ });
5032
5069
  this.emit("tool:result", { callId, error: errorMessage });
5033
5070
  await this.client.call("tool.result", {
5034
5071
  callId,
@@ -5038,7 +5075,10 @@ import { ${allImports} } from "./sandbox-tools";
5038
5075
  });
5039
5076
  }
5040
5077
  setupEventHandlers() {
5041
- this.client.on("open", (payload) => this.emit("open", payload || {}));
5078
+ this.client.on(
5079
+ "open",
5080
+ (payload) => this.emit("open", payload || {})
5081
+ );
5042
5082
  this.client.on("sync", (doc) => {
5043
5083
  this.currentDomainRevision = this.extractDomainRevisionFromDoc(doc);
5044
5084
  this.emit("sync", doc);
@@ -5052,8 +5092,14 @@ import { ${allImports} } from "./sandbox-tools";
5052
5092
  };
5053
5093
  this.client.on("prompt", emitPrompt);
5054
5094
  this.client.on("prompt.request", emitPrompt);
5055
- this.client.on("disconnect", (payload) => this.emit("disconnect", payload || {}));
5056
- this.client.on("reconnect_error", (payload) => this.emit("reconnect_error", payload || {}));
5095
+ this.client.on(
5096
+ "disconnect",
5097
+ (payload) => this.emit("disconnect", payload || {})
5098
+ );
5099
+ this.client.on(
5100
+ "reconnect_error",
5101
+ (payload) => this.emit("reconnect_error", payload || {})
5102
+ );
5057
5103
  this.client.on("job.status", (data) => {
5058
5104
  this.emit("job:status", data);
5059
5105
  });
@@ -5169,7 +5215,10 @@ function sanitizeFeedbackValue(value, depth = 0, seen = /* @__PURE__ */ new Weak
5169
5215
  return "[Circular]";
5170
5216
  }
5171
5217
  seen.add(value);
5172
- const entries = Object.entries(value).slice(0, MAX_FEEDBACK_OBJECT_KEYS);
5218
+ const entries = Object.entries(value).slice(
5219
+ 0,
5220
+ MAX_FEEDBACK_OBJECT_KEYS
5221
+ );
5173
5222
  const sanitized = {};
5174
5223
  for (const [key, entryValue] of entries) {
5175
5224
  sanitized[key] = sanitizeFeedbackValue(entryValue, depth + 1, seen);
@@ -5220,12 +5269,18 @@ var JobImplementation = class {
5220
5269
  if (progressData.execId === id || progressData.jobId === id) {
5221
5270
  if (progressData.stdout) {
5222
5271
  this.markStarted();
5223
- this.metadata.stdout = [...this.metadata.stdout, truncateFeedbackString(progressData.stdout)].slice(-100);
5272
+ this.metadata.stdout = [
5273
+ ...this.metadata.stdout,
5274
+ truncateFeedbackString(progressData.stdout)
5275
+ ].slice(-100);
5224
5276
  this.emit("stdout", progressData.stdout);
5225
5277
  }
5226
5278
  if (progressData.stderr) {
5227
5279
  this.markStarted();
5228
- this.metadata.stderr = [...this.metadata.stderr, truncateFeedbackString(progressData.stderr)].slice(-100);
5280
+ this.metadata.stderr = [
5281
+ ...this.metadata.stderr,
5282
+ truncateFeedbackString(progressData.stderr)
5283
+ ].slice(-100);
5229
5284
  this.emit("stderr", progressData.stderr);
5230
5285
  }
5231
5286
  }
@@ -5247,12 +5302,18 @@ var JobImplementation = class {
5247
5302
  });
5248
5303
  this.client.on(`job.${id}.stdout`, (line) => {
5249
5304
  this.markStarted();
5250
- this.metadata.stdout = [...this.metadata.stdout, truncateFeedbackString(String(line))].slice(-100);
5305
+ this.metadata.stdout = [
5306
+ ...this.metadata.stdout,
5307
+ truncateFeedbackString(String(line))
5308
+ ].slice(-100);
5251
5309
  this.emit("stdout", line);
5252
5310
  });
5253
5311
  this.client.on(`job.${id}.stderr`, (line) => {
5254
5312
  this.markStarted();
5255
- this.metadata.stderr = [...this.metadata.stderr, truncateFeedbackString(String(line))].slice(-100);
5313
+ this.metadata.stderr = [
5314
+ ...this.metadata.stderr,
5315
+ truncateFeedbackString(String(line))
5316
+ ].slice(-100);
5256
5317
  this.emit("stderr", line);
5257
5318
  });
5258
5319
  this.client.on(`job.${id}.result`, (result) => {
@@ -5284,7 +5345,11 @@ var JobImplementation = class {
5284
5345
  this.client.on("job.failed", (data) => {
5285
5346
  const jobData = data;
5286
5347
  if (jobData.jobId === id) {
5287
- this.finalize("failed", void 0, jobData.error || new Error("Job failed"));
5348
+ this.finalize(
5349
+ "failed",
5350
+ void 0,
5351
+ jobData.error || new Error("Job failed")
5352
+ );
5288
5353
  this.emit("status", this.status);
5289
5354
  }
5290
5355
  });
@@ -5297,7 +5362,12 @@ var JobImplementation = class {
5297
5362
  input: sanitizeFeedbackValue(d.input),
5298
5363
  startedAt: d.timestamp || Date.now()
5299
5364
  });
5300
- this.emit("toolCallStart", { callId: d.callId, toolName: d.toolName, input: d.input, timestamp: d.timestamp });
5365
+ this.emit("toolCallStart", {
5366
+ callId: d.callId,
5367
+ toolName: d.toolName,
5368
+ input: d.input,
5369
+ timestamp: d.timestamp
5370
+ });
5301
5371
  }
5302
5372
  });
5303
5373
  this.client.on("tool.call.end", (data) => {
@@ -5312,7 +5382,25 @@ var JobImplementation = class {
5312
5382
  completedAt,
5313
5383
  durationMs: typeof d.durationMs === "number" ? d.durationMs : void 0
5314
5384
  });
5315
- this.emit("toolCallEnd", { callId: d.callId, toolName: d.toolName, result: d.result, error: d.error, durationMs: d.durationMs, timestamp: d.timestamp });
5385
+ this.emit("toolCallEnd", {
5386
+ callId: d.callId,
5387
+ toolName: d.toolName,
5388
+ result: d.result,
5389
+ error: d.error,
5390
+ durationMs: d.durationMs,
5391
+ timestamp: d.timestamp
5392
+ });
5393
+ }
5394
+ });
5395
+ this.client.on("job.agent_message", (data) => {
5396
+ const d = data;
5397
+ if (d.jobId === id) {
5398
+ this.emit("agentMessage", {
5399
+ messageId: d.messageId,
5400
+ reply: typeof d.reply === "string" ? d.reply : "",
5401
+ show: d.show,
5402
+ timestamp: d.timestamp || Date.now()
5403
+ });
5316
5404
  }
5317
5405
  });
5318
5406
  }
@@ -5382,7 +5470,9 @@ var JobImplementation = class {
5382
5470
  }
5383
5471
  upsertToolCall(next) {
5384
5472
  const callId = next.callId || `tool-call-${Date.now()}`;
5385
- const existingIndex = this.metadata.toolCalls.findIndex((entry) => entry.callId === callId);
5473
+ const existingIndex = this.metadata.toolCalls.findIndex(
5474
+ (entry) => entry.callId === callId
5475
+ );
5386
5476
  const existing = existingIndex >= 0 ? this.metadata.toolCalls[existingIndex] : void 0;
5387
5477
  const merged = {
5388
5478
  ...existing,
@@ -13347,7 +13437,8 @@ function uniqueStrings(values, maxCount) {
13347
13437
  }
13348
13438
  function formatScalar(value) {
13349
13439
  if (typeof value === "string") return JSON.stringify(value);
13350
- if (typeof value === "number" || typeof value === "boolean") return String(value);
13440
+ if (typeof value === "number" || typeof value === "boolean")
13441
+ return String(value);
13351
13442
  if (value === null) return "null";
13352
13443
  return "unknown";
13353
13444
  }
@@ -13355,7 +13446,9 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
13355
13446
  const headline = entry.label || entry.id || entry.path || "Unknown";
13356
13447
  const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
13357
13448
  const classLabel = entry.className || "unknown";
13358
- const preview = asArray(entry.fields).filter((field) => field?.name && field.name !== "_realId" && field.name !== "real_id").slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
13449
+ const preview = asArray(entry.fields).filter(
13450
+ (field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
13451
+ ).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
13359
13452
  return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
13360
13453
  }
13361
13454
  function hashString(value) {
@@ -13371,7 +13464,9 @@ function hasSubstantiveAwaitAfterPrompt(code, marker) {
13371
13464
  const startIndex = code.indexOf(marker);
13372
13465
  if (startIndex === -1) return true;
13373
13466
  const segment = code.slice(startIndex + marker.length);
13374
- const callMatches = segment.matchAll(/await\s+([A-Za-z0-9_$.]+)\.([A-Za-z0-9_]+)\s*\(/g);
13467
+ const callMatches = segment.matchAll(
13468
+ /await\s+([A-Za-z0-9_$.]+)\.([A-Za-z0-9_]+)\s*\(/g
13469
+ );
13375
13470
  for (const match of callMatches) {
13376
13471
  const receiver = match[1] || "";
13377
13472
  const method = match[2] || "";
@@ -13403,9 +13498,16 @@ function reviewGeneratedJobCode(code) {
13403
13498
  /approved\./i
13404
13499
  ];
13405
13500
  if (normalized.includes("await loop.confirm(")) {
13406
- const postConfirm = normalized.slice(normalized.indexOf("await loop.confirm("));
13407
- const hasPlaceholder = placeholderPatterns.some((pattern) => pattern.test(postConfirm));
13408
- const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(normalized, "await loop.confirm(");
13501
+ const postConfirm = normalized.slice(
13502
+ normalized.indexOf("await loop.confirm(")
13503
+ );
13504
+ const hasPlaceholder = placeholderPatterns.some(
13505
+ (pattern) => pattern.test(postConfirm)
13506
+ );
13507
+ const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(
13508
+ normalized,
13509
+ "await loop.confirm("
13510
+ );
13409
13511
  if (!hasSubstantiveAwait || hasPlaceholder) {
13410
13512
  issues.push({
13411
13513
  code: "placeholder_after_confirm",
@@ -13415,9 +13517,16 @@ function reviewGeneratedJobCode(code) {
13415
13517
  }
13416
13518
  }
13417
13519
  if (normalized.includes("await loop.ask_user(")) {
13418
- const postPrompt = normalized.slice(normalized.indexOf("await loop.ask_user("));
13419
- const hasPlaceholder = placeholderPatterns.some((pattern) => pattern.test(postPrompt));
13420
- const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(normalized, "await loop.ask_user(");
13520
+ const postPrompt = normalized.slice(
13521
+ normalized.indexOf("await loop.ask_user(")
13522
+ );
13523
+ const hasPlaceholder = placeholderPatterns.some(
13524
+ (pattern) => pattern.test(postPrompt)
13525
+ );
13526
+ const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(
13527
+ normalized,
13528
+ "await loop.ask_user("
13529
+ );
13421
13530
  if (hasPlaceholder && !hasSubstantiveAwait) {
13422
13531
  issues.push({
13423
13532
  code: "placeholder_after_ask_user",
@@ -13498,7 +13607,9 @@ function getLatestClosure(liveDoc) {
13498
13607
  if (!record) continue;
13499
13608
  closures.push({ ...record, closureId });
13500
13609
  }
13501
- closures.sort((left, right) => (Number(right.createdAt) || 0) - (Number(left.createdAt) || 0));
13610
+ closures.sort(
13611
+ (left, right) => (Number(right.createdAt) || 0) - (Number(left.createdAt) || 0)
13612
+ );
13502
13613
  return closures[0] || null;
13503
13614
  }
13504
13615
  function getWorkflowBoundary(liveDoc, options) {
@@ -13536,7 +13647,9 @@ function getJobRecords(liveDoc) {
13536
13647
  if (!record) continue;
13537
13648
  jobs.push({ ...record, jobId });
13538
13649
  }
13539
- jobs.sort((left, right) => getJobTimestamp(right) - getJobTimestamp(left));
13650
+ jobs.sort(
13651
+ (left, right) => getJobTimestamp(right) - getJobTimestamp(left)
13652
+ );
13540
13653
  return jobs;
13541
13654
  }
13542
13655
  function getPromptRecordsFromJobs(liveDoc) {
@@ -13544,7 +13657,9 @@ function getPromptRecordsFromJobs(liveDoc) {
13544
13657
  }
13545
13658
  function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
13546
13659
  const boundary = getWorkflowBoundary(liveDoc, options);
13547
- const jobs = getJobRecords(liveDoc).filter((job) => getJobTimestamp(job) >= boundary.timestamp).slice(0, 6).reverse();
13660
+ const jobs = getJobRecords(liveDoc).filter(
13661
+ (job) => getJobTimestamp(job) >= boundary.timestamp
13662
+ ).slice(0, 6).reverse();
13548
13663
  const actionSummaryLines = [];
13549
13664
  const variableNames = [];
13550
13665
  const listNames = [];
@@ -13596,7 +13711,9 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
13596
13711
  return updatedAt >= boundary.timestamp;
13597
13712
  }
13598
13713
  return status !== "completed" && status !== "canceled" ? true : updatedAt >= boundary.timestamp;
13599
- }).sort((left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0));
13714
+ }).sort(
13715
+ (left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0)
13716
+ );
13600
13717
  for (const task of tasks.slice(0, 4)) {
13601
13718
  if (typeof task.taskId === "string") {
13602
13719
  activeTaskIds.push(task.taskId);
@@ -13608,7 +13725,9 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
13608
13725
  return updatedAt >= boundary.timestamp;
13609
13726
  }
13610
13727
  return decision.status === "open" || updatedAt >= boundary.timestamp;
13611
- }).sort((left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0));
13728
+ }).sort(
13729
+ (left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0)
13730
+ );
13612
13731
  for (const decision of decisions.slice(0, 3)) {
13613
13732
  if (typeof decision.decisionId === "string" && decision.status === "open") {
13614
13733
  openDecisionIds.push(decision.decisionId);
@@ -13687,11 +13806,15 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
13687
13806
  const lines = [];
13688
13807
  lines.push("Workflow Boundary:");
13689
13808
  if (focus.boundaryReason === "request_start") {
13690
- lines.push("- Start from work recorded after the current user request began.");
13809
+ lines.push(
13810
+ "- Start from work recorded after the current user request began."
13811
+ );
13691
13812
  } else if (focus.boundaryReason === "last_closed_loop" && focus.latestClosureId) {
13692
13813
  lines.push(`- Start from work recorded after ${focus.latestClosureId}.`);
13693
13814
  } else {
13694
- lines.push("- No prior closed loop recorded; use the latest user request as the boundary.");
13815
+ lines.push(
13816
+ "- No prior closed loop recorded; use the latest user request as the boundary."
13817
+ );
13695
13818
  }
13696
13819
  lines.push("", "Recent Actions:");
13697
13820
  if (focus.recentActionSummary.length === 0) {
@@ -13760,12 +13883,17 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
13760
13883
  return updatedAt >= boundary.timestamp;
13761
13884
  }
13762
13885
  return status !== "completed" && status !== "canceled" ? true : updatedAt >= boundary.timestamp;
13763
- }).sort((left, right) => (Number(right.updatedAt) || 0) - (Number(left.updatedAt) || 0));
13886
+ }).sort(
13887
+ (left, right) => (Number(right.updatedAt) || 0) - (Number(left.updatedAt) || 0)
13888
+ );
13764
13889
  const activeTasks = tasks.filter((task) => {
13765
13890
  const status = typeof task.status === "string" ? task.status : "pending";
13766
13891
  return status !== "completed" && status !== "canceled";
13767
13892
  });
13768
- const visibleTasks = (activeTasks.length > 0 ? activeTasks : tasks).slice(0, 5);
13893
+ const visibleTasks = (activeTasks.length > 0 ? activeTasks : tasks).slice(
13894
+ 0,
13895
+ 5
13896
+ );
13769
13897
  const hiddenTaskCount = Math.max(0, activeTasks.length - visibleTasks.length);
13770
13898
  lines.push("Tasks:");
13771
13899
  if (visibleTasks.length === 0) {
@@ -13789,8 +13917,12 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
13789
13917
  return updatedAt >= boundary.timestamp;
13790
13918
  }
13791
13919
  return decision.status === "open" || updatedAt >= boundary.timestamp;
13792
- }).sort((left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0));
13793
- const openDecisions = decisions.filter((decision) => decision.status === "open");
13920
+ }).sort(
13921
+ (left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0)
13922
+ );
13923
+ const openDecisions = decisions.filter(
13924
+ (decision) => decision.status === "open"
13925
+ );
13794
13926
  const visibleDecisions = (openDecisions.length > 0 ? openDecisions : decisions.slice(0, 1)).slice(0, 3);
13795
13927
  lines.push("", "Recent Decisions:");
13796
13928
  if (visibleDecisions.length === 0) {
@@ -13809,7 +13941,9 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
13809
13941
  const candidateLabel = typeof record.label === "string" && record.label.trim() ? record.label.trim() : candidateId;
13810
13942
  return candidateLabel === candidateId ? candidateId : `${candidateLabel} (${candidateId})`;
13811
13943
  }).filter((value) => Boolean(value)).join(", ");
13812
- lines.push(`- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`);
13944
+ lines.push(
13945
+ `- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`
13946
+ );
13813
13947
  } else {
13814
13948
  const selected = asRecord2(decision.selected);
13815
13949
  const label = typeof selected?.label === "string" ? selected.label : typeof selected?.id === "string" ? selected.id : "unknown";
@@ -13824,13 +13958,17 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
13824
13958
  title: prompt.title,
13825
13959
  message: prompt.message
13826
13960
  })),
13827
- ...Object.values(asRecord2(asRecord2(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord2(asRecord2(job)?.prompts) || {})).map((prompt) => asRecord2(prompt)).filter((prompt) => Boolean(prompt && prompt.status === "open"))
13961
+ ...Object.values(asRecord2(asRecord2(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord2(asRecord2(job)?.prompts) || {})).map((prompt) => asRecord2(prompt)).filter(
13962
+ (prompt) => Boolean(prompt && prompt.status === "open")
13963
+ )
13828
13964
  ];
13829
13965
  const visiblePrompts = boundary.reason === "request_start" ? openPrompts.filter((prompt) => {
13830
13966
  const promptRecord = asRecord2(prompt);
13831
13967
  const openedAt = Number(promptRecord?.openedAt) || 0;
13832
13968
  const promptId = typeof promptRecord?.id === "string" ? promptRecord.id : typeof prompt.id === "string" ? prompt.id : null;
13833
- return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some((pendingPrompt) => pendingPrompt.id === promptId) : false);
13969
+ return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some(
13970
+ (pendingPrompt) => pendingPrompt.id === promptId
13971
+ ) : false);
13834
13972
  }) : openPrompts;
13835
13973
  lines.push("", "Open Prompts:");
13836
13974
  if (visiblePrompts.length === 0) {
@@ -13861,9 +13999,15 @@ function projectHeapSummary(heap, options) {
13861
13999
  const entriesByPath = asRecord2(heapRecord.entriesByPath) || {};
13862
14000
  const listsByName = asRecord2(heapRecord.listsByName) || {};
13863
14001
  const variablesByName = asRecord2(heapRecord.variablesByName) || {};
13864
- const focusedVariableNames = new Set(uniqueStrings(options?.focus?.variableNames || []));
13865
- const focusedListNames = new Set(uniqueStrings(options?.focus?.listNames || []));
13866
- const focusedEntryPaths = new Set(uniqueStrings(options?.focus?.entryPaths || []));
14002
+ const focusedVariableNames = new Set(
14003
+ uniqueStrings(options?.focus?.variableNames || [])
14004
+ );
14005
+ const focusedListNames = new Set(
14006
+ uniqueStrings(options?.focus?.listNames || [])
14007
+ );
14008
+ const focusedEntryPaths = new Set(
14009
+ uniqueStrings(options?.focus?.entryPaths || [])
14010
+ );
13867
14011
  const hasFocus = focusedVariableNames.size > 0 || focusedListNames.size > 0 || focusedEntryPaths.size > 0;
13868
14012
  const suppressRecentFallback = Boolean(options?.focus) && !hasFocus;
13869
14013
  const maxVariables = options?.maxVariables ?? (hasFocus ? 4 : 6);
@@ -13875,20 +14019,26 @@ function projectHeapSummary(heap, options) {
13875
14019
  return rightFocused - leftFocused || (right.updatedAt || 0) - (left.updatedAt || 0);
13876
14020
  }).filter((variable, index) => {
13877
14021
  if (index < maxVariables) return true;
13878
- return Boolean(variable.name && focusedVariableNames.has(variable.name));
14022
+ return Boolean(
14023
+ variable.name && focusedVariableNames.has(variable.name)
14024
+ );
13879
14025
  }).slice(0, maxVariables);
13880
14026
  const referencedPaths = /* @__PURE__ */ new Set();
13881
14027
  for (const variable of variables) {
13882
14028
  if (variable.entryPath) referencedPaths.add(variable.entryPath);
13883
14029
  if (variable.listName) {
13884
- const list = asRecord2(listsByName[variable.listName]);
14030
+ const list = asRecord2(
14031
+ listsByName[variable.listName]
14032
+ );
13885
14033
  for (const path of list?.paths || []) referencedPaths.add(path);
13886
14034
  }
13887
14035
  }
13888
14036
  for (const path of focusedEntryPaths) {
13889
14037
  referencedPaths.add(path);
13890
14038
  }
13891
- const visibleLists = Object.values(listsByName).map((value) => asRecord2(value)).filter((value) => Boolean(value)).filter((list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
14039
+ const visibleLists = Object.values(listsByName).map((value) => asRecord2(value)).filter((value) => Boolean(value)).filter(
14040
+ (list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))
14041
+ ).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
13892
14042
  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);
13893
14043
  const lines = [];
13894
14044
  lines.push("Variables:");
@@ -13897,16 +14047,24 @@ function projectHeapSummary(heap, options) {
13897
14047
  } else {
13898
14048
  for (const variable of variables) {
13899
14049
  if (variable.kind === "scalar") {
13900
- lines.push(`- ${variable.name}: scalar = ${formatScalar(variable.value)}`);
14050
+ lines.push(
14051
+ `- ${variable.name}: scalar = ${formatScalar(variable.value)}`
14052
+ );
13901
14053
  continue;
13902
14054
  }
13903
14055
  if (variable.kind === "entry") {
13904
- const entry = variable.entryPath ? asRecord2(entriesByPath[variable.entryPath]) : null;
13905
- lines.push(`- ${variable.name}: entry<${variable.className || entry?.className || "unknown"}> -> ${entry ? describeHeapEntry(entry) : variable.entryPath || "missing"}`);
14056
+ const entry = variable.entryPath ? asRecord2(
14057
+ entriesByPath[variable.entryPath]
14058
+ ) : null;
14059
+ lines.push(
14060
+ `- ${variable.name}: entry<${variable.className || entry?.className || "unknown"}> -> ${entry ? describeHeapEntry(entry) : variable.entryPath || "missing"}`
14061
+ );
13906
14062
  continue;
13907
14063
  }
13908
14064
  const list = variable.listName ? asRecord2(listsByName[variable.listName]) : null;
13909
- lines.push(`- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`);
14065
+ lines.push(
14066
+ `- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`
14067
+ );
13910
14068
  }
13911
14069
  }
13912
14070
  lines.push("", "Named Lists:");
@@ -13914,7 +14072,9 @@ function projectHeapSummary(heap, options) {
13914
14072
  lines.push("- none");
13915
14073
  } else {
13916
14074
  for (const list of visibleLists) {
13917
- lines.push(`- ${list.name}: ${list.className || "unknown"}[${(list.paths || []).length}]`);
14075
+ lines.push(
14076
+ `- ${list.name}: ${list.className || "unknown"}[${(list.paths || []).length}]`
14077
+ );
13918
14078
  }
13919
14079
  }
13920
14080
  lines.push("", "Active Entries:");
@@ -13928,11 +14088,19 @@ function projectHeapSummary(heap, options) {
13928
14088
  return lines.join("\n");
13929
14089
  }
13930
14090
  function createHarnessVerifierSnapshot(input) {
13931
- const workflowFocus = projectWorkflowFocus(input.liveDoc, [], input.projectionOptions);
13932
- const heapDigest = hashString(projectHeapSummary(asRecord2(input.liveDoc?.heap), {
13933
- focus: workflowFocus
13934
- })) || "00000000";
13935
- const loopDigest = hashString(projectWorkflowSummary(input.liveDoc, [], input.projectionOptions)) || "00000000";
14091
+ const workflowFocus = projectWorkflowFocus(
14092
+ input.liveDoc,
14093
+ [],
14094
+ input.projectionOptions
14095
+ );
14096
+ const heapDigest = hashString(
14097
+ projectHeapSummary(asRecord2(input.liveDoc?.heap), {
14098
+ focus: workflowFocus
14099
+ })
14100
+ ) || "00000000";
14101
+ const loopDigest = hashString(
14102
+ projectWorkflowSummary(input.liveDoc, [], input.projectionOptions)
14103
+ ) || "00000000";
13936
14104
  return {
13937
14105
  codeDigest: hashString(input.finalCode?.trim()),
13938
14106
  resultDigest: hashString(input.resultPreview?.trim()),
@@ -14062,8 +14230,12 @@ function buildGranularAgentToolBlock(tools) {
14062
14230
  return "No live effects are available in this session yet.";
14063
14231
  }
14064
14232
  const globalTools = normalizedTools.filter((tool) => !tool.className);
14065
- const staticTools = normalizedTools.filter((tool) => Boolean(tool.className && tool.static));
14066
- const instanceTools = normalizedTools.filter((tool) => Boolean(tool.className && !tool.static));
14233
+ const staticTools = normalizedTools.filter(
14234
+ (tool) => Boolean(tool.className && tool.static)
14235
+ );
14236
+ const instanceTools = normalizedTools.filter(
14237
+ (tool) => Boolean(tool.className && !tool.static)
14238
+ );
14067
14239
  const lines = [
14068
14240
  "Treat this block as the planning map. Use DOMAIN TYPES below for exact signatures."
14069
14241
  ];
@@ -14278,7 +14450,10 @@ ${loopBlock}
14278
14450
  - Avoid \`as any\` and other broad casts when the DOMAIN TYPES block already tells you the correct class or list type.
14279
14451
  - Prefer manipulating heap-backed instances and typed lists instead of returning raw JSON blobs or object IDs unless the user explicitly asks for them.
14280
14452
  - 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.
14453
+ - You may call \`agent_message(...)\` multiple times in one job to post several assistant messages while the job is still running.
14281
14454
  - Prefer \`agent_message({ reply, show })\` when you want to leave a user-facing answer and optionally show heap-backed records in the UI.
14455
+ - \`agent_message(...)\` also accepts \`content\`, \`message\`, or \`text\` instead of \`reply\`.
14456
+ - \`agent_message({ show })\` may receive explicit refs or sandbox instances and arrays of sandbox instances. The runtime will convert those into UI references.
14282
14457
  - When it helps the UI show specific heap-backed results, you may instead return:
14283
14458
  \`{ reply: string, show: { entryPaths?: string[], listNames?: string[], variableNames?: string[] } }\`
14284
14459
  - 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.