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