@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.
@@ -4712,7 +4712,9 @@ var Session = class {
4712
4712
  }
4713
4713
  }
4714
4714
  if (!revision) {
4715
- throw new Error("No domain revision available. Register live effects or ensure the build schema is activated.");
4715
+ throw new Error(
4716
+ "No domain revision available. Register live effects or ensure the build schema is activated."
4717
+ );
4716
4718
  }
4717
4719
  const result = await this.client.call("job.submit", {
4718
4720
  domainRevision: revision,
@@ -4748,7 +4750,11 @@ var Session = class {
4748
4750
  const prompt = this.promptCache.get(promptId);
4749
4751
  const resolvedAnswer = resolvePromptAnswer(prompt, answer);
4750
4752
  this.promptCache.delete(promptId);
4751
- await this.client.call("prompt.answer", { promptId, answer: resolvedAnswer, value: resolvedAnswer });
4753
+ await this.client.call("prompt.answer", {
4754
+ promptId,
4755
+ answer: resolvedAnswer,
4756
+ value: resolvedAnswer
4757
+ });
4752
4758
  }
4753
4759
  /**
4754
4760
  * Get the current list of available effects.
@@ -4780,7 +4786,8 @@ var Session = class {
4780
4786
  for (const tool of cat.tools) {
4781
4787
  if (!tool?.name) continue;
4782
4788
  const existing = toolMap.get(tool.name);
4783
- if (existing?.publishedAt && cat.publishedAt && existing.publishedAt > cat.publishedAt) continue;
4789
+ if (existing?.publishedAt && cat.publishedAt && existing.publishedAt > cat.publishedAt)
4790
+ continue;
4784
4791
  const isLocal = clientId === this.clientId;
4785
4792
  const ready = isLocal ? this.effects.has(tool.name) || this.toolHandlers.has(tool.name) || this.instanceTools.has(tool.name) : true;
4786
4793
  toolMap.set(tool.name, {
@@ -4818,7 +4825,10 @@ var Session = class {
4818
4825
  return () => {
4819
4826
  const listeners = this.eventListeners.get("effects:changed");
4820
4827
  if (listeners) {
4821
- this.eventListeners.set("effects:changed", listeners.filter((h) => h !== handler));
4828
+ this.eventListeners.set(
4829
+ "effects:changed",
4830
+ listeners.filter((h) => h !== handler)
4831
+ );
4822
4832
  }
4823
4833
  };
4824
4834
  }
@@ -4834,7 +4844,10 @@ var Session = class {
4834
4844
  return () => {
4835
4845
  const listeners = this.eventListeners.get("tools:changed");
4836
4846
  if (listeners) {
4837
- this.eventListeners.set("tools:changed", listeners.filter((h) => h !== handler));
4847
+ this.eventListeners.set(
4848
+ "tools:changed",
4849
+ listeners.filter((h) => h !== handler)
4850
+ );
4838
4851
  }
4839
4852
  };
4840
4853
  }
@@ -4842,11 +4855,16 @@ var Session = class {
4842
4855
  * Get the current domain state and available tools
4843
4856
  */
4844
4857
  async getDomain() {
4845
- const summary = await this.client.call("domain.getSummary", {});
4858
+ const summary = await this.client.call(
4859
+ "domain.getSummary",
4860
+ {}
4861
+ );
4846
4862
  if (summary.activeDomainRevision) {
4847
4863
  this.currentDomainRevision = summary.activeDomainRevision;
4848
4864
  } else {
4849
- this.currentDomainRevision = this.extractDomainRevisionFromDoc(this.client.doc);
4865
+ this.currentDomainRevision = this.extractDomainRevisionFromDoc(
4866
+ this.client.doc
4867
+ );
4850
4868
  }
4851
4869
  return summary;
4852
4870
  }
@@ -5021,20 +5039,31 @@ import { ${allImports} } from "./sandbox-tools";
5021
5039
  off(event, handler) {
5022
5040
  const handlers = this.eventListeners.get(event);
5023
5041
  if (handlers) {
5024
- this.eventListeners.set(event, handlers.filter((h) => h !== handler));
5042
+ this.eventListeners.set(
5043
+ event,
5044
+ handlers.filter((h) => h !== handler)
5045
+ );
5025
5046
  }
5026
5047
  }
5027
5048
  // --- Internal ---
5028
5049
  setupToolInvokeHandler() {
5029
5050
  this.client.registerRpcHandler("tool.invoke", async (params) => {
5030
5051
  const { callId, toolName, input } = params;
5031
- this.emit("effect:invoke", { callId, effectKey: toolName, toolName, input });
5052
+ this.emit("effect:invoke", {
5053
+ callId,
5054
+ effectKey: toolName,
5055
+ toolName,
5056
+ input
5057
+ });
5032
5058
  this.emit("tool:invoke", { callId, toolName, input });
5033
5059
  const handler = this.toolHandlers.get(toolName);
5034
5060
  if (!handler) {
5035
5061
  await this.client.call("tool.result", {
5036
5062
  callId,
5037
- error: { code: "TOOL_NOT_FOUND", message: `Tool handler not found: ${toolName}` }
5063
+ error: {
5064
+ code: "TOOL_NOT_FOUND",
5065
+ message: `Tool handler not found: ${toolName}`
5066
+ }
5038
5067
  });
5039
5068
  return;
5040
5069
  }
@@ -5043,7 +5072,11 @@ import { ${allImports} } from "./sandbox-tools";
5043
5072
  const invocationContext = this.buildLegacyEffectContext();
5044
5073
  if (this.instanceTools.has(toolName) && input && typeof input === "object" && "_objectId" in input) {
5045
5074
  const { _objectId, ...restParams } = input;
5046
- result = await handler(_objectId, restParams, invocationContext);
5075
+ result = await handler(
5076
+ _objectId,
5077
+ restParams,
5078
+ invocationContext
5079
+ );
5047
5080
  } else {
5048
5081
  result = await handler(input, invocationContext);
5049
5082
  }
@@ -5055,7 +5088,11 @@ import { ${allImports} } from "./sandbox-tools";
5055
5088
  });
5056
5089
  } catch (error) {
5057
5090
  const errorMessage = error instanceof Error ? error.message : String(error);
5058
- this.emit("effect:result", { callId, effectKey: toolName, error: errorMessage });
5091
+ this.emit("effect:result", {
5092
+ callId,
5093
+ effectKey: toolName,
5094
+ error: errorMessage
5095
+ });
5059
5096
  this.emit("tool:result", { callId, error: errorMessage });
5060
5097
  await this.client.call("tool.result", {
5061
5098
  callId,
@@ -5065,7 +5102,10 @@ import { ${allImports} } from "./sandbox-tools";
5065
5102
  });
5066
5103
  }
5067
5104
  setupEventHandlers() {
5068
- this.client.on("open", (payload) => this.emit("open", payload || {}));
5105
+ this.client.on(
5106
+ "open",
5107
+ (payload) => this.emit("open", payload || {})
5108
+ );
5069
5109
  this.client.on("sync", (doc) => {
5070
5110
  this.currentDomainRevision = this.extractDomainRevisionFromDoc(doc);
5071
5111
  this.emit("sync", doc);
@@ -5079,8 +5119,14 @@ import { ${allImports} } from "./sandbox-tools";
5079
5119
  };
5080
5120
  this.client.on("prompt", emitPrompt);
5081
5121
  this.client.on("prompt.request", emitPrompt);
5082
- this.client.on("disconnect", (payload) => this.emit("disconnect", payload || {}));
5083
- this.client.on("reconnect_error", (payload) => this.emit("reconnect_error", payload || {}));
5122
+ this.client.on(
5123
+ "disconnect",
5124
+ (payload) => this.emit("disconnect", payload || {})
5125
+ );
5126
+ this.client.on(
5127
+ "reconnect_error",
5128
+ (payload) => this.emit("reconnect_error", payload || {})
5129
+ );
5084
5130
  this.client.on("job.status", (data) => {
5085
5131
  this.emit("job:status", data);
5086
5132
  });
@@ -5196,7 +5242,10 @@ function sanitizeFeedbackValue(value, depth = 0, seen = /* @__PURE__ */ new Weak
5196
5242
  return "[Circular]";
5197
5243
  }
5198
5244
  seen.add(value);
5199
- const entries = Object.entries(value).slice(0, MAX_FEEDBACK_OBJECT_KEYS);
5245
+ const entries = Object.entries(value).slice(
5246
+ 0,
5247
+ MAX_FEEDBACK_OBJECT_KEYS
5248
+ );
5200
5249
  const sanitized = {};
5201
5250
  for (const [key, entryValue] of entries) {
5202
5251
  sanitized[key] = sanitizeFeedbackValue(entryValue, depth + 1, seen);
@@ -5247,12 +5296,18 @@ var JobImplementation = class {
5247
5296
  if (progressData.execId === id || progressData.jobId === id) {
5248
5297
  if (progressData.stdout) {
5249
5298
  this.markStarted();
5250
- this.metadata.stdout = [...this.metadata.stdout, truncateFeedbackString(progressData.stdout)].slice(-100);
5299
+ this.metadata.stdout = [
5300
+ ...this.metadata.stdout,
5301
+ truncateFeedbackString(progressData.stdout)
5302
+ ].slice(-100);
5251
5303
  this.emit("stdout", progressData.stdout);
5252
5304
  }
5253
5305
  if (progressData.stderr) {
5254
5306
  this.markStarted();
5255
- this.metadata.stderr = [...this.metadata.stderr, truncateFeedbackString(progressData.stderr)].slice(-100);
5307
+ this.metadata.stderr = [
5308
+ ...this.metadata.stderr,
5309
+ truncateFeedbackString(progressData.stderr)
5310
+ ].slice(-100);
5256
5311
  this.emit("stderr", progressData.stderr);
5257
5312
  }
5258
5313
  }
@@ -5274,12 +5329,18 @@ var JobImplementation = class {
5274
5329
  });
5275
5330
  this.client.on(`job.${id}.stdout`, (line) => {
5276
5331
  this.markStarted();
5277
- this.metadata.stdout = [...this.metadata.stdout, truncateFeedbackString(String(line))].slice(-100);
5332
+ this.metadata.stdout = [
5333
+ ...this.metadata.stdout,
5334
+ truncateFeedbackString(String(line))
5335
+ ].slice(-100);
5278
5336
  this.emit("stdout", line);
5279
5337
  });
5280
5338
  this.client.on(`job.${id}.stderr`, (line) => {
5281
5339
  this.markStarted();
5282
- this.metadata.stderr = [...this.metadata.stderr, truncateFeedbackString(String(line))].slice(-100);
5340
+ this.metadata.stderr = [
5341
+ ...this.metadata.stderr,
5342
+ truncateFeedbackString(String(line))
5343
+ ].slice(-100);
5283
5344
  this.emit("stderr", line);
5284
5345
  });
5285
5346
  this.client.on(`job.${id}.result`, (result) => {
@@ -5311,7 +5372,11 @@ var JobImplementation = class {
5311
5372
  this.client.on("job.failed", (data) => {
5312
5373
  const jobData = data;
5313
5374
  if (jobData.jobId === id) {
5314
- this.finalize("failed", void 0, jobData.error || new Error("Job failed"));
5375
+ this.finalize(
5376
+ "failed",
5377
+ void 0,
5378
+ jobData.error || new Error("Job failed")
5379
+ );
5315
5380
  this.emit("status", this.status);
5316
5381
  }
5317
5382
  });
@@ -5324,7 +5389,12 @@ var JobImplementation = class {
5324
5389
  input: sanitizeFeedbackValue(d.input),
5325
5390
  startedAt: d.timestamp || Date.now()
5326
5391
  });
5327
- this.emit("toolCallStart", { callId: d.callId, toolName: d.toolName, input: d.input, timestamp: d.timestamp });
5392
+ this.emit("toolCallStart", {
5393
+ callId: d.callId,
5394
+ toolName: d.toolName,
5395
+ input: d.input,
5396
+ timestamp: d.timestamp
5397
+ });
5328
5398
  }
5329
5399
  });
5330
5400
  this.client.on("tool.call.end", (data) => {
@@ -5339,7 +5409,25 @@ var JobImplementation = class {
5339
5409
  completedAt,
5340
5410
  durationMs: typeof d.durationMs === "number" ? d.durationMs : void 0
5341
5411
  });
5342
- this.emit("toolCallEnd", { callId: d.callId, toolName: d.toolName, result: d.result, error: d.error, durationMs: d.durationMs, timestamp: d.timestamp });
5412
+ this.emit("toolCallEnd", {
5413
+ callId: d.callId,
5414
+ toolName: d.toolName,
5415
+ result: d.result,
5416
+ error: d.error,
5417
+ durationMs: d.durationMs,
5418
+ timestamp: d.timestamp
5419
+ });
5420
+ }
5421
+ });
5422
+ 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
+ });
5343
5431
  }
5344
5432
  });
5345
5433
  }
@@ -5409,7 +5497,9 @@ var JobImplementation = class {
5409
5497
  }
5410
5498
  upsertToolCall(next) {
5411
5499
  const callId = next.callId || `tool-call-${Date.now()}`;
5412
- const existingIndex = this.metadata.toolCalls.findIndex((entry) => entry.callId === callId);
5500
+ const existingIndex = this.metadata.toolCalls.findIndex(
5501
+ (entry) => entry.callId === callId
5502
+ );
5413
5503
  const existing = existingIndex >= 0 ? this.metadata.toolCalls[existingIndex] : void 0;
5414
5504
  const merged = {
5415
5505
  ...existing,
@@ -13374,7 +13464,8 @@ function uniqueStrings(values, maxCount) {
13374
13464
  }
13375
13465
  function formatScalar(value) {
13376
13466
  if (typeof value === "string") return JSON.stringify(value);
13377
- if (typeof value === "number" || typeof value === "boolean") return String(value);
13467
+ if (typeof value === "number" || typeof value === "boolean")
13468
+ return String(value);
13378
13469
  if (value === null) return "null";
13379
13470
  return "unknown";
13380
13471
  }
@@ -13382,7 +13473,9 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
13382
13473
  const headline = entry.label || entry.id || entry.path || "Unknown";
13383
13474
  const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
13384
13475
  const classLabel = entry.className || "unknown";
13385
- 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(", ");
13476
+ const preview = asArray(entry.fields).filter(
13477
+ (field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
13478
+ ).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
13386
13479
  return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
13387
13480
  }
13388
13481
  function hashString(value) {
@@ -13398,7 +13491,9 @@ function hasSubstantiveAwaitAfterPrompt(code, marker) {
13398
13491
  const startIndex = code.indexOf(marker);
13399
13492
  if (startIndex === -1) return true;
13400
13493
  const segment = code.slice(startIndex + marker.length);
13401
- const callMatches = segment.matchAll(/await\s+([A-Za-z0-9_$.]+)\.([A-Za-z0-9_]+)\s*\(/g);
13494
+ const callMatches = segment.matchAll(
13495
+ /await\s+([A-Za-z0-9_$.]+)\.([A-Za-z0-9_]+)\s*\(/g
13496
+ );
13402
13497
  for (const match of callMatches) {
13403
13498
  const receiver = match[1] || "";
13404
13499
  const method = match[2] || "";
@@ -13430,9 +13525,16 @@ function reviewGeneratedJobCode(code) {
13430
13525
  /approved\./i
13431
13526
  ];
13432
13527
  if (normalized.includes("await loop.confirm(")) {
13433
- const postConfirm = normalized.slice(normalized.indexOf("await loop.confirm("));
13434
- const hasPlaceholder = placeholderPatterns.some((pattern) => pattern.test(postConfirm));
13435
- const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(normalized, "await loop.confirm(");
13528
+ const postConfirm = normalized.slice(
13529
+ normalized.indexOf("await loop.confirm(")
13530
+ );
13531
+ const hasPlaceholder = placeholderPatterns.some(
13532
+ (pattern) => pattern.test(postConfirm)
13533
+ );
13534
+ const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(
13535
+ normalized,
13536
+ "await loop.confirm("
13537
+ );
13436
13538
  if (!hasSubstantiveAwait || hasPlaceholder) {
13437
13539
  issues.push({
13438
13540
  code: "placeholder_after_confirm",
@@ -13442,9 +13544,16 @@ function reviewGeneratedJobCode(code) {
13442
13544
  }
13443
13545
  }
13444
13546
  if (normalized.includes("await loop.ask_user(")) {
13445
- const postPrompt = normalized.slice(normalized.indexOf("await loop.ask_user("));
13446
- const hasPlaceholder = placeholderPatterns.some((pattern) => pattern.test(postPrompt));
13447
- const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(normalized, "await loop.ask_user(");
13547
+ const postPrompt = normalized.slice(
13548
+ normalized.indexOf("await loop.ask_user(")
13549
+ );
13550
+ const hasPlaceholder = placeholderPatterns.some(
13551
+ (pattern) => pattern.test(postPrompt)
13552
+ );
13553
+ const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(
13554
+ normalized,
13555
+ "await loop.ask_user("
13556
+ );
13448
13557
  if (hasPlaceholder && !hasSubstantiveAwait) {
13449
13558
  issues.push({
13450
13559
  code: "placeholder_after_ask_user",
@@ -13525,7 +13634,9 @@ function getLatestClosure(liveDoc) {
13525
13634
  if (!record) continue;
13526
13635
  closures.push({ ...record, closureId });
13527
13636
  }
13528
- closures.sort((left, right) => (Number(right.createdAt) || 0) - (Number(left.createdAt) || 0));
13637
+ closures.sort(
13638
+ (left, right) => (Number(right.createdAt) || 0) - (Number(left.createdAt) || 0)
13639
+ );
13529
13640
  return closures[0] || null;
13530
13641
  }
13531
13642
  function getWorkflowBoundary(liveDoc, options) {
@@ -13563,7 +13674,9 @@ function getJobRecords(liveDoc) {
13563
13674
  if (!record) continue;
13564
13675
  jobs.push({ ...record, jobId });
13565
13676
  }
13566
- jobs.sort((left, right) => getJobTimestamp(right) - getJobTimestamp(left));
13677
+ jobs.sort(
13678
+ (left, right) => getJobTimestamp(right) - getJobTimestamp(left)
13679
+ );
13567
13680
  return jobs;
13568
13681
  }
13569
13682
  function getPromptRecordsFromJobs(liveDoc) {
@@ -13571,7 +13684,9 @@ function getPromptRecordsFromJobs(liveDoc) {
13571
13684
  }
13572
13685
  function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
13573
13686
  const boundary = getWorkflowBoundary(liveDoc, options);
13574
- const jobs = getJobRecords(liveDoc).filter((job) => getJobTimestamp(job) >= boundary.timestamp).slice(0, 6).reverse();
13687
+ const jobs = getJobRecords(liveDoc).filter(
13688
+ (job) => getJobTimestamp(job) >= boundary.timestamp
13689
+ ).slice(0, 6).reverse();
13575
13690
  const actionSummaryLines = [];
13576
13691
  const variableNames = [];
13577
13692
  const listNames = [];
@@ -13623,7 +13738,9 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
13623
13738
  return updatedAt >= boundary.timestamp;
13624
13739
  }
13625
13740
  return status !== "completed" && status !== "canceled" ? true : updatedAt >= boundary.timestamp;
13626
- }).sort((left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0));
13741
+ }).sort(
13742
+ (left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0)
13743
+ );
13627
13744
  for (const task of tasks.slice(0, 4)) {
13628
13745
  if (typeof task.taskId === "string") {
13629
13746
  activeTaskIds.push(task.taskId);
@@ -13635,7 +13752,9 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
13635
13752
  return updatedAt >= boundary.timestamp;
13636
13753
  }
13637
13754
  return decision.status === "open" || updatedAt >= boundary.timestamp;
13638
- }).sort((left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0));
13755
+ }).sort(
13756
+ (left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0)
13757
+ );
13639
13758
  for (const decision of decisions.slice(0, 3)) {
13640
13759
  if (typeof decision.decisionId === "string" && decision.status === "open") {
13641
13760
  openDecisionIds.push(decision.decisionId);
@@ -13714,11 +13833,15 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
13714
13833
  const lines = [];
13715
13834
  lines.push("Workflow Boundary:");
13716
13835
  if (focus.boundaryReason === "request_start") {
13717
- lines.push("- Start from work recorded after the current user request began.");
13836
+ lines.push(
13837
+ "- Start from work recorded after the current user request began."
13838
+ );
13718
13839
  } else if (focus.boundaryReason === "last_closed_loop" && focus.latestClosureId) {
13719
13840
  lines.push(`- Start from work recorded after ${focus.latestClosureId}.`);
13720
13841
  } else {
13721
- lines.push("- No prior closed loop recorded; use the latest user request as the boundary.");
13842
+ lines.push(
13843
+ "- No prior closed loop recorded; use the latest user request as the boundary."
13844
+ );
13722
13845
  }
13723
13846
  lines.push("", "Recent Actions:");
13724
13847
  if (focus.recentActionSummary.length === 0) {
@@ -13782,12 +13905,17 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
13782
13905
  return updatedAt >= boundary.timestamp;
13783
13906
  }
13784
13907
  return status !== "completed" && status !== "canceled" ? true : updatedAt >= boundary.timestamp;
13785
- }).sort((left, right) => (Number(right.updatedAt) || 0) - (Number(left.updatedAt) || 0));
13908
+ }).sort(
13909
+ (left, right) => (Number(right.updatedAt) || 0) - (Number(left.updatedAt) || 0)
13910
+ );
13786
13911
  const activeTasks = tasks.filter((task) => {
13787
13912
  const status = typeof task.status === "string" ? task.status : "pending";
13788
13913
  return status !== "completed" && status !== "canceled";
13789
13914
  });
13790
- const visibleTasks = (activeTasks.length > 0 ? activeTasks : tasks).slice(0, 5);
13915
+ const visibleTasks = (activeTasks.length > 0 ? activeTasks : tasks).slice(
13916
+ 0,
13917
+ 5
13918
+ );
13791
13919
  const hiddenTaskCount = Math.max(0, activeTasks.length - visibleTasks.length);
13792
13920
  lines.push("Tasks:");
13793
13921
  if (visibleTasks.length === 0) {
@@ -13811,8 +13939,12 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
13811
13939
  return updatedAt >= boundary.timestamp;
13812
13940
  }
13813
13941
  return decision.status === "open" || updatedAt >= boundary.timestamp;
13814
- }).sort((left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0));
13815
- const openDecisions = decisions.filter((decision) => decision.status === "open");
13942
+ }).sort(
13943
+ (left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0)
13944
+ );
13945
+ const openDecisions = decisions.filter(
13946
+ (decision) => decision.status === "open"
13947
+ );
13816
13948
  const visibleDecisions = (openDecisions.length > 0 ? openDecisions : decisions.slice(0, 1)).slice(0, 3);
13817
13949
  lines.push("", "Recent Decisions:");
13818
13950
  if (visibleDecisions.length === 0) {
@@ -13831,7 +13963,9 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
13831
13963
  const candidateLabel = typeof record.label === "string" && record.label.trim() ? record.label.trim() : candidateId;
13832
13964
  return candidateLabel === candidateId ? candidateId : `${candidateLabel} (${candidateId})`;
13833
13965
  }).filter((value) => Boolean(value)).join(", ");
13834
- lines.push(`- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`);
13966
+ lines.push(
13967
+ `- [open] ${title} (${decisionId})${candidatePreview ? ` \u2014 candidates: ${candidatePreview}` : ""}`
13968
+ );
13835
13969
  } else {
13836
13970
  const selected = asRecord2(decision.selected);
13837
13971
  const label = typeof selected?.label === "string" ? selected.label : typeof selected?.id === "string" ? selected.id : "unknown";
@@ -13846,13 +13980,17 @@ function projectLoopSummary(liveDoc, pendingPrompts = [], options) {
13846
13980
  title: prompt.title,
13847
13981
  message: prompt.message
13848
13982
  })),
13849
- ...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"))
13983
+ ...Object.values(asRecord2(asRecord2(liveDoc?.jobs)?.byId) || {}).flatMap((job) => Object.values(asRecord2(asRecord2(job)?.prompts) || {})).map((prompt) => asRecord2(prompt)).filter(
13984
+ (prompt) => Boolean(prompt && prompt.status === "open")
13985
+ )
13850
13986
  ];
13851
13987
  const visiblePrompts = boundary.reason === "request_start" ? openPrompts.filter((prompt) => {
13852
13988
  const promptRecord = asRecord2(prompt);
13853
13989
  const openedAt = Number(promptRecord?.openedAt) || 0;
13854
13990
  const promptId = typeof promptRecord?.id === "string" ? promptRecord.id : typeof prompt.id === "string" ? prompt.id : null;
13855
- return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some((pendingPrompt) => pendingPrompt.id === promptId) : false);
13991
+ return openedAt >= boundary.timestamp || (promptId ? pendingPrompts.some(
13992
+ (pendingPrompt) => pendingPrompt.id === promptId
13993
+ ) : false);
13856
13994
  }) : openPrompts;
13857
13995
  lines.push("", "Open Prompts:");
13858
13996
  if (visiblePrompts.length === 0) {
@@ -13883,9 +14021,15 @@ function projectHeapSummary(heap, options) {
13883
14021
  const entriesByPath = asRecord2(heapRecord.entriesByPath) || {};
13884
14022
  const listsByName = asRecord2(heapRecord.listsByName) || {};
13885
14023
  const variablesByName = asRecord2(heapRecord.variablesByName) || {};
13886
- const focusedVariableNames = new Set(uniqueStrings(options?.focus?.variableNames || []));
13887
- const focusedListNames = new Set(uniqueStrings(options?.focus?.listNames || []));
13888
- const focusedEntryPaths = new Set(uniqueStrings(options?.focus?.entryPaths || []));
14024
+ const focusedVariableNames = new Set(
14025
+ uniqueStrings(options?.focus?.variableNames || [])
14026
+ );
14027
+ const focusedListNames = new Set(
14028
+ uniqueStrings(options?.focus?.listNames || [])
14029
+ );
14030
+ const focusedEntryPaths = new Set(
14031
+ uniqueStrings(options?.focus?.entryPaths || [])
14032
+ );
13889
14033
  const hasFocus = focusedVariableNames.size > 0 || focusedListNames.size > 0 || focusedEntryPaths.size > 0;
13890
14034
  const suppressRecentFallback = Boolean(options?.focus) && !hasFocus;
13891
14035
  const maxVariables = options?.maxVariables ?? (hasFocus ? 4 : 6);
@@ -13897,20 +14041,26 @@ function projectHeapSummary(heap, options) {
13897
14041
  return rightFocused - leftFocused || (right.updatedAt || 0) - (left.updatedAt || 0);
13898
14042
  }).filter((variable, index) => {
13899
14043
  if (index < maxVariables) return true;
13900
- return Boolean(variable.name && focusedVariableNames.has(variable.name));
14044
+ return Boolean(
14045
+ variable.name && focusedVariableNames.has(variable.name)
14046
+ );
13901
14047
  }).slice(0, maxVariables);
13902
14048
  const referencedPaths = /* @__PURE__ */ new Set();
13903
14049
  for (const variable of variables) {
13904
14050
  if (variable.entryPath) referencedPaths.add(variable.entryPath);
13905
14051
  if (variable.listName) {
13906
- const list = asRecord2(listsByName[variable.listName]);
14052
+ const list = asRecord2(
14053
+ listsByName[variable.listName]
14054
+ );
13907
14055
  for (const path2 of list?.paths || []) referencedPaths.add(path2);
13908
14056
  }
13909
14057
  }
13910
14058
  for (const path2 of focusedEntryPaths) {
13911
14059
  referencedPaths.add(path2);
13912
14060
  }
13913
- 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);
14061
+ const visibleLists = Object.values(listsByName).map((value) => asRecord2(value)).filter((value) => Boolean(value)).filter(
14062
+ (list) => variables.some((variable) => variable.listName === list.name) || Boolean(list.name && focusedListNames.has(list.name))
14063
+ ).sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0)).slice(0, maxLists);
13914
14064
  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);
13915
14065
  const lines = [];
13916
14066
  lines.push("Variables:");
@@ -13919,16 +14069,24 @@ function projectHeapSummary(heap, options) {
13919
14069
  } else {
13920
14070
  for (const variable of variables) {
13921
14071
  if (variable.kind === "scalar") {
13922
- lines.push(`- ${variable.name}: scalar = ${formatScalar(variable.value)}`);
14072
+ lines.push(
14073
+ `- ${variable.name}: scalar = ${formatScalar(variable.value)}`
14074
+ );
13923
14075
  continue;
13924
14076
  }
13925
14077
  if (variable.kind === "entry") {
13926
- const entry = variable.entryPath ? asRecord2(entriesByPath[variable.entryPath]) : null;
13927
- lines.push(`- ${variable.name}: entry<${variable.className || entry?.className || "unknown"}> -> ${entry ? describeHeapEntry(entry) : variable.entryPath || "missing"}`);
14078
+ const entry = variable.entryPath ? asRecord2(
14079
+ entriesByPath[variable.entryPath]
14080
+ ) : null;
14081
+ lines.push(
14082
+ `- ${variable.name}: entry<${variable.className || entry?.className || "unknown"}> -> ${entry ? describeHeapEntry(entry) : variable.entryPath || "missing"}`
14083
+ );
13928
14084
  continue;
13929
14085
  }
13930
14086
  const list = variable.listName ? asRecord2(listsByName[variable.listName]) : null;
13931
- lines.push(`- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`);
14087
+ lines.push(
14088
+ `- ${variable.name}: list<${variable.className || list?.className || "unknown"}> -> ${(list?.paths || []).length} item(s)`
14089
+ );
13932
14090
  }
13933
14091
  }
13934
14092
  lines.push("", "Named Lists:");
@@ -13936,7 +14094,9 @@ function projectHeapSummary(heap, options) {
13936
14094
  lines.push("- none");
13937
14095
  } else {
13938
14096
  for (const list of visibleLists) {
13939
- lines.push(`- ${list.name}: ${list.className || "unknown"}[${(list.paths || []).length}]`);
14097
+ lines.push(
14098
+ `- ${list.name}: ${list.className || "unknown"}[${(list.paths || []).length}]`
14099
+ );
13940
14100
  }
13941
14101
  }
13942
14102
  lines.push("", "Active Entries:");
@@ -13950,11 +14110,19 @@ function projectHeapSummary(heap, options) {
13950
14110
  return lines.join("\n");
13951
14111
  }
13952
14112
  function createHarnessVerifierSnapshot(input) {
13953
- const workflowFocus = projectWorkflowFocus(input.liveDoc, [], input.projectionOptions);
13954
- const heapDigest = hashString(projectHeapSummary(asRecord2(input.liveDoc?.heap), {
13955
- focus: workflowFocus
13956
- })) || "00000000";
13957
- const loopDigest = hashString(projectWorkflowSummary(input.liveDoc, [], input.projectionOptions)) || "00000000";
14113
+ const workflowFocus = projectWorkflowFocus(
14114
+ input.liveDoc,
14115
+ [],
14116
+ input.projectionOptions
14117
+ );
14118
+ const heapDigest = hashString(
14119
+ projectHeapSummary(asRecord2(input.liveDoc?.heap), {
14120
+ focus: workflowFocus
14121
+ })
14122
+ ) || "00000000";
14123
+ const loopDigest = hashString(
14124
+ projectWorkflowSummary(input.liveDoc, [], input.projectionOptions)
14125
+ ) || "00000000";
13958
14126
  return {
13959
14127
  codeDigest: hashString(input.finalCode?.trim()),
13960
14128
  resultDigest: hashString(input.resultPreview?.trim()),
@@ -14084,8 +14252,12 @@ function buildGranularAgentToolBlock(tools) {
14084
14252
  return "No live effects are available in this session yet.";
14085
14253
  }
14086
14254
  const globalTools = normalizedTools.filter((tool) => !tool.className);
14087
- const staticTools = normalizedTools.filter((tool) => Boolean(tool.className && tool.static));
14088
- const instanceTools = normalizedTools.filter((tool) => Boolean(tool.className && !tool.static));
14255
+ const staticTools = normalizedTools.filter(
14256
+ (tool) => Boolean(tool.className && tool.static)
14257
+ );
14258
+ const instanceTools = normalizedTools.filter(
14259
+ (tool) => Boolean(tool.className && !tool.static)
14260
+ );
14089
14261
  const lines = [
14090
14262
  "Treat this block as the planning map. Use DOMAIN TYPES below for exact signatures."
14091
14263
  ];
@@ -14300,7 +14472,10 @@ ${loopBlock}
14300
14472
  - Avoid \`as any\` and other broad casts when the DOMAIN TYPES block already tells you the correct class or list type.
14301
14473
  - Prefer manipulating heap-backed instances and typed lists instead of returning raw JSON blobs or object IDs unless the user explicitly asks for them.
14302
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.
14303
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.
14304
14479
  - When it helps the UI show specific heap-backed results, you may instead return:
14305
14480
  \`{ reply: string, show: { entryPaths?: string[], listNames?: string[], variableNames?: string[] } }\`
14306
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.