@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.js CHANGED
@@ -4707,7 +4707,9 @@ var Session = class {
4707
4707
  }
4708
4708
  }
4709
4709
  if (!revision) {
4710
- throw new Error("No domain revision available. Register live effects or ensure the build schema is activated.");
4710
+ throw new Error(
4711
+ "No domain revision available. Register live effects or ensure the build schema is activated."
4712
+ );
4711
4713
  }
4712
4714
  const result = await this.client.call("job.submit", {
4713
4715
  domainRevision: revision,
@@ -4743,7 +4745,11 @@ var Session = class {
4743
4745
  const prompt = this.promptCache.get(promptId);
4744
4746
  const resolvedAnswer = resolvePromptAnswer(prompt, answer);
4745
4747
  this.promptCache.delete(promptId);
4746
- await this.client.call("prompt.answer", { promptId, answer: resolvedAnswer, value: resolvedAnswer });
4748
+ await this.client.call("prompt.answer", {
4749
+ promptId,
4750
+ answer: resolvedAnswer,
4751
+ value: resolvedAnswer
4752
+ });
4747
4753
  }
4748
4754
  /**
4749
4755
  * Get the current list of available effects.
@@ -4775,7 +4781,8 @@ var Session = class {
4775
4781
  for (const tool of cat.tools) {
4776
4782
  if (!tool?.name) continue;
4777
4783
  const existing = toolMap.get(tool.name);
4778
- if (existing?.publishedAt && cat.publishedAt && existing.publishedAt > cat.publishedAt) continue;
4784
+ if (existing?.publishedAt && cat.publishedAt && existing.publishedAt > cat.publishedAt)
4785
+ continue;
4779
4786
  const isLocal = clientId === this.clientId;
4780
4787
  const ready = isLocal ? this.effects.has(tool.name) || this.toolHandlers.has(tool.name) || this.instanceTools.has(tool.name) : true;
4781
4788
  toolMap.set(tool.name, {
@@ -4813,7 +4820,10 @@ var Session = class {
4813
4820
  return () => {
4814
4821
  const listeners = this.eventListeners.get("effects:changed");
4815
4822
  if (listeners) {
4816
- this.eventListeners.set("effects:changed", listeners.filter((h) => h !== handler));
4823
+ this.eventListeners.set(
4824
+ "effects:changed",
4825
+ listeners.filter((h) => h !== handler)
4826
+ );
4817
4827
  }
4818
4828
  };
4819
4829
  }
@@ -4829,7 +4839,10 @@ var Session = class {
4829
4839
  return () => {
4830
4840
  const listeners = this.eventListeners.get("tools:changed");
4831
4841
  if (listeners) {
4832
- this.eventListeners.set("tools:changed", listeners.filter((h) => h !== handler));
4842
+ this.eventListeners.set(
4843
+ "tools:changed",
4844
+ listeners.filter((h) => h !== handler)
4845
+ );
4833
4846
  }
4834
4847
  };
4835
4848
  }
@@ -4837,11 +4850,16 @@ var Session = class {
4837
4850
  * Get the current domain state and available tools
4838
4851
  */
4839
4852
  async getDomain() {
4840
- const summary = await this.client.call("domain.getSummary", {});
4853
+ const summary = await this.client.call(
4854
+ "domain.getSummary",
4855
+ {}
4856
+ );
4841
4857
  if (summary.activeDomainRevision) {
4842
4858
  this.currentDomainRevision = summary.activeDomainRevision;
4843
4859
  } else {
4844
- this.currentDomainRevision = this.extractDomainRevisionFromDoc(this.client.doc);
4860
+ this.currentDomainRevision = this.extractDomainRevisionFromDoc(
4861
+ this.client.doc
4862
+ );
4845
4863
  }
4846
4864
  return summary;
4847
4865
  }
@@ -5016,20 +5034,31 @@ import { ${allImports} } from "./sandbox-tools";
5016
5034
  off(event, handler) {
5017
5035
  const handlers = this.eventListeners.get(event);
5018
5036
  if (handlers) {
5019
- this.eventListeners.set(event, handlers.filter((h) => h !== handler));
5037
+ this.eventListeners.set(
5038
+ event,
5039
+ handlers.filter((h) => h !== handler)
5040
+ );
5020
5041
  }
5021
5042
  }
5022
5043
  // --- Internal ---
5023
5044
  setupToolInvokeHandler() {
5024
5045
  this.client.registerRpcHandler("tool.invoke", async (params) => {
5025
5046
  const { callId, toolName, input } = params;
5026
- this.emit("effect:invoke", { callId, effectKey: toolName, toolName, input });
5047
+ this.emit("effect:invoke", {
5048
+ callId,
5049
+ effectKey: toolName,
5050
+ toolName,
5051
+ input
5052
+ });
5027
5053
  this.emit("tool:invoke", { callId, toolName, input });
5028
5054
  const handler = this.toolHandlers.get(toolName);
5029
5055
  if (!handler) {
5030
5056
  await this.client.call("tool.result", {
5031
5057
  callId,
5032
- error: { code: "TOOL_NOT_FOUND", message: `Tool handler not found: ${toolName}` }
5058
+ error: {
5059
+ code: "TOOL_NOT_FOUND",
5060
+ message: `Tool handler not found: ${toolName}`
5061
+ }
5033
5062
  });
5034
5063
  return;
5035
5064
  }
@@ -5038,7 +5067,11 @@ import { ${allImports} } from "./sandbox-tools";
5038
5067
  const invocationContext = this.buildLegacyEffectContext();
5039
5068
  if (this.instanceTools.has(toolName) && input && typeof input === "object" && "_objectId" in input) {
5040
5069
  const { _objectId, ...restParams } = input;
5041
- result = await handler(_objectId, restParams, invocationContext);
5070
+ result = await handler(
5071
+ _objectId,
5072
+ restParams,
5073
+ invocationContext
5074
+ );
5042
5075
  } else {
5043
5076
  result = await handler(input, invocationContext);
5044
5077
  }
@@ -5050,7 +5083,11 @@ import { ${allImports} } from "./sandbox-tools";
5050
5083
  });
5051
5084
  } catch (error) {
5052
5085
  const errorMessage = error instanceof Error ? error.message : String(error);
5053
- this.emit("effect:result", { callId, effectKey: toolName, error: errorMessage });
5086
+ this.emit("effect:result", {
5087
+ callId,
5088
+ effectKey: toolName,
5089
+ error: errorMessage
5090
+ });
5054
5091
  this.emit("tool:result", { callId, error: errorMessage });
5055
5092
  await this.client.call("tool.result", {
5056
5093
  callId,
@@ -5060,7 +5097,10 @@ import { ${allImports} } from "./sandbox-tools";
5060
5097
  });
5061
5098
  }
5062
5099
  setupEventHandlers() {
5063
- this.client.on("open", (payload) => this.emit("open", payload || {}));
5100
+ this.client.on(
5101
+ "open",
5102
+ (payload) => this.emit("open", payload || {})
5103
+ );
5064
5104
  this.client.on("sync", (doc) => {
5065
5105
  this.currentDomainRevision = this.extractDomainRevisionFromDoc(doc);
5066
5106
  this.emit("sync", doc);
@@ -5074,8 +5114,14 @@ import { ${allImports} } from "./sandbox-tools";
5074
5114
  };
5075
5115
  this.client.on("prompt", emitPrompt);
5076
5116
  this.client.on("prompt.request", emitPrompt);
5077
- this.client.on("disconnect", (payload) => this.emit("disconnect", payload || {}));
5078
- this.client.on("reconnect_error", (payload) => this.emit("reconnect_error", payload || {}));
5117
+ this.client.on(
5118
+ "disconnect",
5119
+ (payload) => this.emit("disconnect", payload || {})
5120
+ );
5121
+ this.client.on(
5122
+ "reconnect_error",
5123
+ (payload) => this.emit("reconnect_error", payload || {})
5124
+ );
5079
5125
  this.client.on("job.status", (data) => {
5080
5126
  this.emit("job:status", data);
5081
5127
  });
@@ -5191,7 +5237,10 @@ function sanitizeFeedbackValue(value, depth = 0, seen = /* @__PURE__ */ new Weak
5191
5237
  return "[Circular]";
5192
5238
  }
5193
5239
  seen.add(value);
5194
- const entries = Object.entries(value).slice(0, MAX_FEEDBACK_OBJECT_KEYS);
5240
+ const entries = Object.entries(value).slice(
5241
+ 0,
5242
+ MAX_FEEDBACK_OBJECT_KEYS
5243
+ );
5195
5244
  const sanitized = {};
5196
5245
  for (const [key, entryValue] of entries) {
5197
5246
  sanitized[key] = sanitizeFeedbackValue(entryValue, depth + 1, seen);
@@ -5242,12 +5291,18 @@ var JobImplementation = class {
5242
5291
  if (progressData.execId === id || progressData.jobId === id) {
5243
5292
  if (progressData.stdout) {
5244
5293
  this.markStarted();
5245
- this.metadata.stdout = [...this.metadata.stdout, truncateFeedbackString(progressData.stdout)].slice(-100);
5294
+ this.metadata.stdout = [
5295
+ ...this.metadata.stdout,
5296
+ truncateFeedbackString(progressData.stdout)
5297
+ ].slice(-100);
5246
5298
  this.emit("stdout", progressData.stdout);
5247
5299
  }
5248
5300
  if (progressData.stderr) {
5249
5301
  this.markStarted();
5250
- this.metadata.stderr = [...this.metadata.stderr, truncateFeedbackString(progressData.stderr)].slice(-100);
5302
+ this.metadata.stderr = [
5303
+ ...this.metadata.stderr,
5304
+ truncateFeedbackString(progressData.stderr)
5305
+ ].slice(-100);
5251
5306
  this.emit("stderr", progressData.stderr);
5252
5307
  }
5253
5308
  }
@@ -5269,12 +5324,18 @@ var JobImplementation = class {
5269
5324
  });
5270
5325
  this.client.on(`job.${id}.stdout`, (line) => {
5271
5326
  this.markStarted();
5272
- this.metadata.stdout = [...this.metadata.stdout, truncateFeedbackString(String(line))].slice(-100);
5327
+ this.metadata.stdout = [
5328
+ ...this.metadata.stdout,
5329
+ truncateFeedbackString(String(line))
5330
+ ].slice(-100);
5273
5331
  this.emit("stdout", line);
5274
5332
  });
5275
5333
  this.client.on(`job.${id}.stderr`, (line) => {
5276
5334
  this.markStarted();
5277
- this.metadata.stderr = [...this.metadata.stderr, truncateFeedbackString(String(line))].slice(-100);
5335
+ this.metadata.stderr = [
5336
+ ...this.metadata.stderr,
5337
+ truncateFeedbackString(String(line))
5338
+ ].slice(-100);
5278
5339
  this.emit("stderr", line);
5279
5340
  });
5280
5341
  this.client.on(`job.${id}.result`, (result) => {
@@ -5306,7 +5367,11 @@ var JobImplementation = class {
5306
5367
  this.client.on("job.failed", (data) => {
5307
5368
  const jobData = data;
5308
5369
  if (jobData.jobId === id) {
5309
- this.finalize("failed", void 0, jobData.error || new Error("Job failed"));
5370
+ this.finalize(
5371
+ "failed",
5372
+ void 0,
5373
+ jobData.error || new Error("Job failed")
5374
+ );
5310
5375
  this.emit("status", this.status);
5311
5376
  }
5312
5377
  });
@@ -5319,7 +5384,12 @@ var JobImplementation = class {
5319
5384
  input: sanitizeFeedbackValue(d.input),
5320
5385
  startedAt: d.timestamp || Date.now()
5321
5386
  });
5322
- this.emit("toolCallStart", { callId: d.callId, toolName: d.toolName, input: d.input, timestamp: d.timestamp });
5387
+ this.emit("toolCallStart", {
5388
+ callId: d.callId,
5389
+ toolName: d.toolName,
5390
+ input: d.input,
5391
+ timestamp: d.timestamp
5392
+ });
5323
5393
  }
5324
5394
  });
5325
5395
  this.client.on("tool.call.end", (data) => {
@@ -5334,7 +5404,25 @@ var JobImplementation = class {
5334
5404
  completedAt,
5335
5405
  durationMs: typeof d.durationMs === "number" ? d.durationMs : void 0
5336
5406
  });
5337
- this.emit("toolCallEnd", { callId: d.callId, toolName: d.toolName, result: d.result, error: d.error, durationMs: d.durationMs, timestamp: d.timestamp });
5407
+ this.emit("toolCallEnd", {
5408
+ callId: d.callId,
5409
+ toolName: d.toolName,
5410
+ result: d.result,
5411
+ error: d.error,
5412
+ durationMs: d.durationMs,
5413
+ timestamp: d.timestamp
5414
+ });
5415
+ }
5416
+ });
5417
+ this.client.on("job.agent_message", (data) => {
5418
+ const d = data;
5419
+ if (d.jobId === id) {
5420
+ this.emit("agentMessage", {
5421
+ messageId: d.messageId,
5422
+ reply: typeof d.reply === "string" ? d.reply : "",
5423
+ show: d.show,
5424
+ timestamp: d.timestamp || Date.now()
5425
+ });
5338
5426
  }
5339
5427
  });
5340
5428
  }
@@ -5404,7 +5492,9 @@ var JobImplementation = class {
5404
5492
  }
5405
5493
  upsertToolCall(next) {
5406
5494
  const callId = next.callId || `tool-call-${Date.now()}`;
5407
- const existingIndex = this.metadata.toolCalls.findIndex((entry) => entry.callId === callId);
5495
+ const existingIndex = this.metadata.toolCalls.findIndex(
5496
+ (entry) => entry.callId === callId
5497
+ );
5408
5498
  const existing = existingIndex >= 0 ? this.metadata.toolCalls[existingIndex] : void 0;
5409
5499
  const merged = {
5410
5500
  ...existing,
@@ -13369,7 +13459,8 @@ function uniqueStrings(values, maxCount) {
13369
13459
  }
13370
13460
  function formatScalar(value) {
13371
13461
  if (typeof value === "string") return JSON.stringify(value);
13372
- if (typeof value === "number" || typeof value === "boolean") return String(value);
13462
+ if (typeof value === "number" || typeof value === "boolean")
13463
+ return String(value);
13373
13464
  if (value === null) return "null";
13374
13465
  return "unknown";
13375
13466
  }
@@ -13377,7 +13468,9 @@ function describeHeapEntry(entry, previewFieldLimit = 3) {
13377
13468
  const headline = entry.label || entry.id || entry.path || "Unknown";
13378
13469
  const pathLabel = entry.path && entry.path !== headline ? ` <${entry.path}>` : "";
13379
13470
  const classLabel = entry.className || "unknown";
13380
- 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(", ");
13471
+ const preview = asArray(entry.fields).filter(
13472
+ (field) => field?.name && field.name !== "_realId" && field.name !== "real_id"
13473
+ ).slice(0, previewFieldLimit).map((field) => `${field.name}=${formatScalar(field.value)}`).join(", ");
13381
13474
  return preview ? `${headline}${pathLabel} [${classLabel}] ${preview}` : `${headline}${pathLabel} [${classLabel}]`;
13382
13475
  }
13383
13476
  function hashString(value) {
@@ -13393,7 +13486,9 @@ function hasSubstantiveAwaitAfterPrompt(code, marker) {
13393
13486
  const startIndex = code.indexOf(marker);
13394
13487
  if (startIndex === -1) return true;
13395
13488
  const segment = code.slice(startIndex + marker.length);
13396
- const callMatches = segment.matchAll(/await\s+([A-Za-z0-9_$.]+)\.([A-Za-z0-9_]+)\s*\(/g);
13489
+ const callMatches = segment.matchAll(
13490
+ /await\s+([A-Za-z0-9_$.]+)\.([A-Za-z0-9_]+)\s*\(/g
13491
+ );
13397
13492
  for (const match of callMatches) {
13398
13493
  const receiver = match[1] || "";
13399
13494
  const method = match[2] || "";
@@ -13425,9 +13520,16 @@ function reviewGeneratedJobCode(code) {
13425
13520
  /approved\./i
13426
13521
  ];
13427
13522
  if (normalized.includes("await loop.confirm(")) {
13428
- const postConfirm = normalized.slice(normalized.indexOf("await loop.confirm("));
13429
- const hasPlaceholder = placeholderPatterns.some((pattern) => pattern.test(postConfirm));
13430
- const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(normalized, "await loop.confirm(");
13523
+ const postConfirm = normalized.slice(
13524
+ normalized.indexOf("await loop.confirm(")
13525
+ );
13526
+ const hasPlaceholder = placeholderPatterns.some(
13527
+ (pattern) => pattern.test(postConfirm)
13528
+ );
13529
+ const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(
13530
+ normalized,
13531
+ "await loop.confirm("
13532
+ );
13431
13533
  if (!hasSubstantiveAwait || hasPlaceholder) {
13432
13534
  issues.push({
13433
13535
  code: "placeholder_after_confirm",
@@ -13437,9 +13539,16 @@ function reviewGeneratedJobCode(code) {
13437
13539
  }
13438
13540
  }
13439
13541
  if (normalized.includes("await loop.ask_user(")) {
13440
- const postPrompt = normalized.slice(normalized.indexOf("await loop.ask_user("));
13441
- const hasPlaceholder = placeholderPatterns.some((pattern) => pattern.test(postPrompt));
13442
- const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(normalized, "await loop.ask_user(");
13542
+ const postPrompt = normalized.slice(
13543
+ normalized.indexOf("await loop.ask_user(")
13544
+ );
13545
+ const hasPlaceholder = placeholderPatterns.some(
13546
+ (pattern) => pattern.test(postPrompt)
13547
+ );
13548
+ const hasSubstantiveAwait = hasSubstantiveAwaitAfterPrompt(
13549
+ normalized,
13550
+ "await loop.ask_user("
13551
+ );
13443
13552
  if (hasPlaceholder && !hasSubstantiveAwait) {
13444
13553
  issues.push({
13445
13554
  code: "placeholder_after_ask_user",
@@ -13520,7 +13629,9 @@ function getLatestClosure(liveDoc) {
13520
13629
  if (!record) continue;
13521
13630
  closures.push({ ...record, closureId });
13522
13631
  }
13523
- closures.sort((left, right) => (Number(right.createdAt) || 0) - (Number(left.createdAt) || 0));
13632
+ closures.sort(
13633
+ (left, right) => (Number(right.createdAt) || 0) - (Number(left.createdAt) || 0)
13634
+ );
13524
13635
  return closures[0] || null;
13525
13636
  }
13526
13637
  function getWorkflowBoundary(liveDoc, options) {
@@ -13558,7 +13669,9 @@ function getJobRecords(liveDoc) {
13558
13669
  if (!record) continue;
13559
13670
  jobs.push({ ...record, jobId });
13560
13671
  }
13561
- jobs.sort((left, right) => getJobTimestamp(right) - getJobTimestamp(left));
13672
+ jobs.sort(
13673
+ (left, right) => getJobTimestamp(right) - getJobTimestamp(left)
13674
+ );
13562
13675
  return jobs;
13563
13676
  }
13564
13677
  function getPromptRecordsFromJobs(liveDoc) {
@@ -13566,7 +13679,9 @@ function getPromptRecordsFromJobs(liveDoc) {
13566
13679
  }
13567
13680
  function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
13568
13681
  const boundary = getWorkflowBoundary(liveDoc, options);
13569
- const jobs = getJobRecords(liveDoc).filter((job) => getJobTimestamp(job) >= boundary.timestamp).slice(0, 6).reverse();
13682
+ const jobs = getJobRecords(liveDoc).filter(
13683
+ (job) => getJobTimestamp(job) >= boundary.timestamp
13684
+ ).slice(0, 6).reverse();
13570
13685
  const actionSummaryLines = [];
13571
13686
  const variableNames = [];
13572
13687
  const listNames = [];
@@ -13618,7 +13733,9 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
13618
13733
  return updatedAt >= boundary.timestamp;
13619
13734
  }
13620
13735
  return status !== "completed" && status !== "canceled" ? true : updatedAt >= boundary.timestamp;
13621
- }).sort((left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0));
13736
+ }).sort(
13737
+ (left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0)
13738
+ );
13622
13739
  for (const task of tasks.slice(0, 4)) {
13623
13740
  if (typeof task.taskId === "string") {
13624
13741
  activeTaskIds.push(task.taskId);
@@ -13630,7 +13747,9 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
13630
13747
  return updatedAt >= boundary.timestamp;
13631
13748
  }
13632
13749
  return decision.status === "open" || updatedAt >= boundary.timestamp;
13633
- }).sort((left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0));
13750
+ }).sort(
13751
+ (left, right) => (Number(right.updatedAt) || Number(right.createdAt) || 0) - (Number(left.updatedAt) || Number(left.createdAt) || 0)
13752
+ );
13634
13753
  for (const decision of decisions.slice(0, 3)) {
13635
13754
  if (typeof decision.decisionId === "string" && decision.status === "open") {
13636
13755
  openDecisionIds.push(decision.decisionId);
@@ -13709,11 +13828,15 @@ function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
13709
13828
  const lines = [];
13710
13829
  lines.push("Workflow Boundary:");
13711
13830
  if (focus.boundaryReason === "request_start") {
13712
- lines.push("- Start from work recorded after the current user request began.");
13831
+ lines.push(
13832
+ "- Start from work recorded after the current user request began."
13833
+ );
13713
13834
  } else if (focus.boundaryReason === "last_closed_loop" && focus.latestClosureId) {
13714
13835
  lines.push(`- Start from work recorded after ${focus.latestClosureId}.`);
13715
13836
  } else {
13716
- lines.push("- No prior closed loop recorded; use the latest user request as the boundary.");
13837
+ lines.push(
13838
+ "- No prior closed loop recorded; use the latest user request as the boundary."
13839
+ );
13717
13840
  }
13718
13841
  lines.push("", "Recent Actions:");
13719
13842
  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 path of list?.paths || []) referencedPaths.add(path);
13908
14056
  }
13909
14057
  }
13910
14058
  for (const path of focusedEntryPaths) {
13911
14059
  referencedPaths.add(path);
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.