@granular-software/sdk 0.4.28 → 0.4.30

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.
@@ -121,21 +121,6 @@ function reviewGeneratedJobCode(code) {
121
121
  message: "After await loop.ask_user(...) returns a usable answer, continue the workflow in the same resumed run instead of stopping with placeholder text about doing the work later."
122
122
  });
123
123
  }
124
- const askUserCalls = normalized.match(/await\s+loop\.ask_user\s*\(\s*\{[\s\S]*?\}\s*\)/g) || [];
125
- for (const call of askUserCalls) {
126
- const usesChoiceType = /type\s*:\s*['"]choice['"]/.test(call);
127
- const usesInputType = /type\s*:\s*['"]input['"]/.test(call);
128
- const hasDisambiguationLanguage = /(which|choose|pick|select)/i.test(call) && /(invoice|order|shipment|request|case|work[\s_-]?order)/i.test(call);
129
- const includesShortlistOptions = /options\s*:\s*\[/.test(call);
130
- if (!usesChoiceType && (usesInputType || hasDisambiguationLanguage || includesShortlistOptions)) {
131
- issues.push({
132
- code: "disambiguation_requires_choice",
133
- severity: "error",
134
- message: "When asking the user to choose between known concrete records such as invoices, orders, shipments, requests, cases, or work orders, use loop.ask_user({ type: 'choice', options: [...] }) with a short explicit shortlist instead of a free-text input."
135
- });
136
- break;
137
- }
138
- }
139
124
  }
140
125
  const hasConversationalReturn = /return\s+[`'"]/.test(normalized) || /\breply\s*:/.test(normalized) || /\bagent_message\s*\(/.test(normalized) || /\bagent_text_message\s*\(/.test(normalized);
141
126
  const returnsObjectLiteral = /return\s+\{[\s\S]*?\}/.test(normalized);
@@ -181,6 +166,184 @@ function extractFocusHintsFromActionSummary(actionSummaryLines) {
181
166
  entryPaths: uniqueStrings(entryPaths, 8)
182
167
  };
183
168
  }
169
+ function normalizeActionSummaryForPrompt(line) {
170
+ return line.replace(/\blimit=/g, "perPage=").replace(/\blimit:/g, "perPage:");
171
+ }
172
+ function collectConversationReferents(liveDoc) {
173
+ const conversation = asRecord(liveDoc?.conversation);
174
+ const persistedReferents = asArray(conversation?.referents).map((value) => asRecord(value)).filter((value) => Boolean(value));
175
+ if (persistedReferents.length > 0) {
176
+ return persistedReferents.slice().sort((left, right) => (right.ts || 0) - (left.ts || 0));
177
+ }
178
+ const heap = asRecord(liveDoc?.heap);
179
+ const entriesByPath = asRecord(heap?.entriesByPath) || {};
180
+ const listsByName = asRecord(heap?.listsByName) || {};
181
+ const variablesByName = asRecord(heap?.variablesByName) || {};
182
+ const messages = asArray(conversation?.messages).map((value) => asRecord(value)).filter((value) => Boolean(value)).slice().sort((left, right) => (Number(right.ts) || 0) - (Number(left.ts) || 0));
183
+ const referents = [];
184
+ const seen = /* @__PURE__ */ new Set();
185
+ const pushReferent = (referent) => {
186
+ if (!referent?.kind || !referent.ref) return;
187
+ const key = `${referent.kind}:${referent.ref}`;
188
+ if (seen.has(key)) return;
189
+ seen.add(key);
190
+ referents.push(referent);
191
+ };
192
+ for (const message of messages) {
193
+ if (message.role !== "assistant") continue;
194
+ const show = asRecord(message.show);
195
+ if (!show) continue;
196
+ const ts = Number(message.ts) || 0;
197
+ const messageId = typeof message.id === "string" ? message.id : void 0;
198
+ const jobId = typeof message.jobId === "string" ? message.jobId : void 0;
199
+ for (const entryPath of uniqueStrings(asArray(show.entryPaths))) {
200
+ const entry = asRecord(entriesByPath[entryPath]);
201
+ pushReferent({
202
+ id: `entry:${entryPath}`,
203
+ kind: "entry",
204
+ ref: entryPath,
205
+ entryPath,
206
+ className: typeof entry?.className === "string" ? entry.className : void 0,
207
+ label: typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim() : typeof entry?.id === "string" && entry.id.trim() ? entry.id.trim() : entryPath,
208
+ messageId,
209
+ jobId,
210
+ ts
211
+ });
212
+ }
213
+ for (const listName of uniqueStrings(asArray(show.listNames))) {
214
+ const list = asRecord(listsByName[listName]);
215
+ pushReferent({
216
+ id: `list:${listName}`,
217
+ kind: "list",
218
+ ref: listName,
219
+ listName,
220
+ className: typeof list?.className === "string" ? list.className : void 0,
221
+ count: Array.isArray(list?.paths) ? list.paths.length : null,
222
+ messageId,
223
+ jobId,
224
+ ts
225
+ });
226
+ }
227
+ for (const variableName of uniqueStrings(
228
+ asArray(show.variableNames)
229
+ )) {
230
+ const variable = asRecord(variablesByName[variableName]);
231
+ const entryPath = typeof variable?.entryPath === "string" ? variable.entryPath : void 0;
232
+ const listName = typeof variable?.listName === "string" ? variable.listName : void 0;
233
+ const entry = entryPath ? asRecord(entriesByPath[entryPath]) : null;
234
+ const list = listName ? asRecord(listsByName[listName]) : null;
235
+ pushReferent({
236
+ id: `variable:${variableName}`,
237
+ kind: "variable",
238
+ ref: variableName,
239
+ variableName,
240
+ variableKind: typeof variable?.kind === "string" ? variable.kind : void 0,
241
+ entryPath,
242
+ listName,
243
+ className: typeof variable?.className === "string" ? variable.className : typeof entry?.className === "string" ? entry.className : typeof list?.className === "string" ? list.className : void 0,
244
+ label: typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim() : typeof entry?.id === "string" && entry.id.trim() ? entry.id.trim() : null,
245
+ count: variable?.kind === "list" && Array.isArray(list?.paths) ? list.paths.length : null,
246
+ scalarValue: variable?.kind === "scalar" && (typeof variable.value === "string" || typeof variable.value === "number" || typeof variable.value === "boolean" || variable.value === null) ? variable.value : void 0,
247
+ messageId,
248
+ jobId,
249
+ ts
250
+ });
251
+ }
252
+ }
253
+ return referents;
254
+ }
255
+ function projectConversationReferentFocus(liveDoc) {
256
+ const heap = asRecord(liveDoc?.heap);
257
+ const listsByName = asRecord(heap?.listsByName) || {};
258
+ const referents = collectConversationReferents(liveDoc);
259
+ const entryPaths = [];
260
+ const listNames = [];
261
+ const variableNames = [];
262
+ for (const referent of referents.slice(0, 8)) {
263
+ if (referent.kind === "entry" && typeof referent.entryPath === "string") {
264
+ entryPaths.push(referent.entryPath);
265
+ continue;
266
+ }
267
+ if (referent.kind === "list" && typeof referent.listName === "string") {
268
+ listNames.push(referent.listName);
269
+ const list = asRecord(listsByName[referent.listName]);
270
+ entryPaths.push(...asArray(list?.paths).slice(0, 4));
271
+ continue;
272
+ }
273
+ if (referent.kind === "variable" && typeof referent.variableName === "string") {
274
+ variableNames.push(referent.variableName);
275
+ if (typeof referent.entryPath === "string") {
276
+ entryPaths.push(referent.entryPath);
277
+ }
278
+ if (typeof referent.listName === "string") {
279
+ listNames.push(referent.listName);
280
+ const list = asRecord(listsByName[referent.listName]);
281
+ entryPaths.push(...asArray(list?.paths).slice(0, 4));
282
+ }
283
+ }
284
+ }
285
+ return {
286
+ entryPaths: uniqueStrings(entryPaths, 8),
287
+ listNames: uniqueStrings(listNames, 4),
288
+ variableNames: uniqueStrings(variableNames, 4)
289
+ };
290
+ }
291
+ function projectConversationReferentSummary(liveDoc) {
292
+ const referents = collectConversationReferents(liveDoc).slice(0, 8);
293
+ if (referents.length === 0) {
294
+ return "No recent referents recorded from prior assistant replies.";
295
+ }
296
+ const entryLines = [];
297
+ const listLines = [];
298
+ const variableLines = [];
299
+ for (const referent of referents) {
300
+ if (referent.kind === "entry" && referent.entryPath) {
301
+ const label = referent.label || referent.entryPath;
302
+ const classLabel = referent.className || "unknown";
303
+ entryLines.push(`- ${label} <${referent.entryPath}> [${classLabel}]`);
304
+ continue;
305
+ }
306
+ if (referent.kind === "list" && referent.listName) {
307
+ const classLabel = referent.className || "unknown";
308
+ const countLabel = typeof referent.count === "number" ? referent.count : "?";
309
+ listLines.push(
310
+ `- ${referent.listName}: list<${classLabel}> -> ${countLabel} item(s)`
311
+ );
312
+ continue;
313
+ }
314
+ if (referent.kind === "variable" && referent.variableName) {
315
+ if (referent.variableKind === "entry" && referent.entryPath && referent.className) {
316
+ const label = referent.label || referent.entryPath;
317
+ variableLines.push(
318
+ `- ${referent.variableName}: entry<${referent.className}> -> ${label} <${referent.entryPath}>`
319
+ );
320
+ continue;
321
+ }
322
+ if (referent.variableKind === "list" && referent.listName && referent.className) {
323
+ const countLabel = typeof referent.count === "number" ? referent.count : "?";
324
+ variableLines.push(
325
+ `- ${referent.variableName}: list<${referent.className}> -> ${countLabel} item(s) via ${referent.listName}`
326
+ );
327
+ continue;
328
+ }
329
+ if (referent.variableKind === "scalar") {
330
+ variableLines.push(
331
+ `- ${referent.variableName}: scalar = ${formatScalar(referent.scalarValue)}`
332
+ );
333
+ continue;
334
+ }
335
+ variableLines.push(`- ${referent.variableName}`);
336
+ }
337
+ }
338
+ const lines = [];
339
+ lines.push("Entries:");
340
+ lines.push(...entryLines.length > 0 ? entryLines : ["- none"]);
341
+ lines.push("", "Lists:");
342
+ lines.push(...listLines.length > 0 ? listLines : ["- none"]);
343
+ lines.push("", "Variables:");
344
+ lines.push(...variableLines.length > 0 ? variableLines : ["- none"]);
345
+ return lines.join("\n");
346
+ }
184
347
  function getCurrentClosureId(liveDoc) {
185
348
  const loop = asRecord(liveDoc?.loop);
186
349
  return typeof loop?.currentClosureId === "string" ? loop.currentClosureId : null;
@@ -393,7 +556,9 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
393
556
  variableNames: uniqueStrings(variableNames, 4),
394
557
  listNames: uniqueStrings(listNames, 4),
395
558
  entryPaths: uniqueStrings(entryPaths, 6),
396
- recentActionSummary: uniqueStrings(actionSummaryLines, 8)
559
+ recentActionSummary: uniqueStrings(actionSummaryLines, 8).map(
560
+ normalizeActionSummaryForPrompt
561
+ )
397
562
  };
398
563
  }
399
564
  function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
@@ -790,17 +955,14 @@ ${resultPreview}` : null
790
955
  ].filter(Boolean).join("\n\n");
791
956
  }
792
957
  function buildGranularAgentDomainBlock(domainDocumentation) {
793
- return domainDocumentation?.trim() || "No domain types available. The graph may not be ready yet.";
958
+ return domainDocumentation?.trim() || "No domain reference available. The graph may not be ready yet.";
794
959
  }
795
960
  function buildGranularAgentSessionBlock(sessionContext) {
796
961
  if (!sessionContext) return "No session metadata available.";
797
962
  const rows = [
798
963
  ["sandboxId", sessionContext.sandboxId],
799
964
  ["environmentId", sessionContext.environmentId],
800
- ["userId", sessionContext.userId],
801
- ["granularId", sessionContext.granularId],
802
- ["userName", sessionContext.userName],
803
- ["domainRevision", sessionContext.domainRevision]
965
+ ["userName", sessionContext.userName]
804
966
  ];
805
967
  const activeRows = rows.filter(([, value]) => Boolean(value));
806
968
  if (activeRows.length === 0) return "No session metadata available.";
@@ -809,6 +971,9 @@ function buildGranularAgentSessionBlock(sessionContext) {
809
971
  function buildGranularAgentHeapBlock(heapSummary) {
810
972
  return heapSummary?.trim() || "Heap is empty for this session.";
811
973
  }
974
+ function buildGranularAgentReferentBlock(referentSummary) {
975
+ return referentSummary?.trim() || "No recent referents recorded from prior assistant replies.";
976
+ }
812
977
  function buildGranularAgentLoopBlock(loopSummary) {
813
978
  return loopSummary?.trim() || "No active loop state recorded for this session.";
814
979
  }
@@ -832,7 +997,7 @@ function buildGranularAgentToolBlock(tools) {
832
997
  (tool) => Boolean(tool.className && !tool.static)
833
998
  );
834
999
  const lines = [
835
- "Treat this block as the planning map. Use DOMAIN TYPES below for exact signatures."
1000
+ "Treat this block as the planning map. Use DOMAIN REFERENCE below for exact signatures and query examples."
836
1001
  ];
837
1002
  const appendGroup = (title, group) => {
838
1003
  lines.push(`- ${title}:`);
@@ -880,7 +1045,10 @@ function buildGranularAgentCheckpointBlock(checkpoint) {
880
1045
  if (Array.isArray(checkpoint.latestActionSummary) && checkpoint.latestActionSummary.length > 0) {
881
1046
  lines.push("latestActionSummary:");
882
1047
  for (const line of checkpoint.latestActionSummary.slice(0, 8)) {
883
- lines.push(line.startsWith("- ") ? line : `- ${line}`);
1048
+ const normalizedLine = normalizeActionSummaryForPrompt(line);
1049
+ lines.push(
1050
+ normalizedLine.startsWith("- ") ? normalizedLine : `- ${normalizedLine}`
1051
+ );
884
1052
  }
885
1053
  }
886
1054
  if (checkpoint.latestJobResult?.trim()) {
@@ -896,9 +1064,10 @@ function buildGranularAgentSystemPrompt(input) {
896
1064
  const workflowBlock = buildGranularAgentWorkflowBlock(input.workflowSummary);
897
1065
  const checkpointBlock = buildGranularAgentCheckpointBlock(input.checkpoint);
898
1066
  const heapBlock = buildGranularAgentHeapBlock(input.heapSummary);
1067
+ const referentBlock = buildGranularAgentReferentBlock(input.referentSummary);
899
1068
  const loopBlock = buildGranularAgentLoopBlock(input.loopSummary);
900
1069
  return `You are an AI assistant for a live Granular session.
901
- You can help the user understand the domain, answer questions, or generate and execute TypeScript code.
1070
+ You can help the user understand the domain, answer questions, or generate and execute code against the live session.
902
1071
  Your tone must be natural and human-like.
903
1072
 
904
1073
  Call the \`execute_code\` effect ONLY when the user's intent matches the domain's capabilities and requires executing code against the live session. If the user is just asking a general question or if their request doesn't match the available effects or domain types, respond with text to explain.
@@ -907,6 +1076,8 @@ When you call \`execute_code\`, additional assistant text must be either:
907
1076
  - a brief summary of the actions the generated code will perform.
908
1077
  Do not include any other kind of commentary when calling \`execute_code\`.
909
1078
  - If the next step needs to create or update workflow state in the live session, you must call \`execute_code\`. This includes \`loop.ask_user(...)\`, \`loop.confirm(...)\`, \`loop.open_decision(...)\`, \`loop.close_decision(...)\`, \`loop.create_task(...)\`, \`loop.update_task(...)\`, \`loop.complete_task(...)\`, and \`loop.close_loop(...)\`.
1079
+ - If the next step is an interactive clarification that should be resumable in the live workflow, you must call \`execute_code\`. A missing preference, rule, metric, target, or option selection is not a plain-text reply when the answer should drive the next live step.
1080
+ - If you can offer a short grounded shortlist, that clarification should usually be \`loop.ask_user({ type: 'choice', ... })\` instead of a plain-text question with bullet options.
910
1081
  - Never simulate a live prompt, confirmation, decision, task change, or loop closure in plain text. Plain-text replies are only for conversational answers that do not need to mutate session state.
911
1082
 
912
1083
  \u2500\u2500\u2500 STREAMING COMMENT RULES \u2500\u2500\u2500
@@ -926,6 +1097,7 @@ Do not include any other kind of commentary when calling \`execute_code\`.
926
1097
  - Do not say "sandbox" in user-facing text unless the user is explicitly asking about the runtime environment itself.
927
1098
  - If you need clarification, ask in everyday language.
928
1099
  - If the missing information should pause the live workflow for later continuation, ask through \`loop.ask_user(...)\` in generated code rather than with a plain-text question.
1100
+ - If you are asking the user to pick from explicit options, prefer a live \`loop.ask_user({ type: 'choice', ... })\` prompt over a direct reply that lists those options in text.
929
1101
  - Keep replies concise and clear.
930
1102
  - This is a conversation UI, not an API console. Favor human answers over machine-shaped payloads.
931
1103
 
@@ -935,9 +1107,9 @@ ${sessionBlock}
935
1107
  \u2500\u2500\u2500 CAPABILITY SNAPSHOT \u2500\u2500\u2500
936
1108
  ${toolBlock}
937
1109
 
938
- \u2500\u2500\u2500 DOMAIN TYPES (TypeScript declarations from ./sandbox-tools) \u2500\u2500\u2500
1110
+ \u2500\u2500\u2500 DOMAIN REFERENCE (from ./sandbox-tools) \u2500\u2500\u2500
939
1111
  Import classes and effect functions from \`./sandbox-tools\` in generated code.
940
- Published effects appear as instance or static methods on the classes below, or as top-level \`export declare function\` entries for global effects.
1112
+ Use the TypeScript declarations for exact signatures. When present, the generated usage notes below them show query patterns and examples.
941
1113
 
942
1114
  ${domainBlock}
943
1115
 
@@ -947,6 +1119,9 @@ ${checkpointBlock}
947
1119
  \u2500\u2500\u2500 WORKFLOW SNAPSHOT \u2500\u2500\u2500
948
1120
  ${workflowBlock}
949
1121
 
1122
+ \u2500\u2500\u2500 RECENT REFERENTS \u2500\u2500\u2500
1123
+ ${referentBlock}
1124
+
950
1125
  \u2500\u2500\u2500 SESSION HEAP \u2500\u2500\u2500
951
1126
  ${heapBlock}
952
1127
 
@@ -954,112 +1129,57 @@ ${heapBlock}
954
1129
  ${loopBlock}
955
1130
 
956
1131
  \u2500\u2500\u2500 LOOP PLAYBOOK \u2500\u2500\u2500
957
- - A single user request may span several assistant turns and several jobs. Continue from the latest structured session state instead of restarting.
958
- - Treat WORKFLOW SNAPSHOT, EXECUTION CHECKPOINT, SESSION HEAP, and AGENT LOOP STATE as the authoritative working memory for the current request.
959
- - Use CAPABILITY SNAPSHOT to choose the next step quickly, then use DOMAIN TYPES to write exact valid code.
960
- - Use WORKFLOW SNAPSHOT to understand the current boundary, recent actions, and working set before fetching more data.
961
- - If the user names a concrete customer, case, order, shipment, or other record that is not already in the heap, fetch it from the graph. "Not in the current context" is not a sufficient reason to stop.
962
- - Treat user-provided names as human references, not exact database keys. If the user says "Northwind", "the Alpine compressor case", or another shorthand, prefer sensible case-insensitive partial matching across likely records before concluding that nothing matches.
963
- - If exactly one strong partial-name match exists, use it. If several plausible partial matches exist, ask the user to choose instead of failing on an exact-equality lookup.
964
- - Take the minimum next step that directly advances the user's request. Do not do speculative cleanup, enrichment, or bookkeeping.
965
- - Reuse exact \`taskId\`, \`decisionId\`, and \`closureId\` values from the AGENT LOOP STATE block. Never guess or slugify IDs.
966
- - If the request is ambiguous or clearly multi-step, start by creating 2-4 meaningful user-visible tasks. Do not create a detailed internal checklist.
967
- - Keep tasks updated as the workflow advances. Complete tasks as soon as they are actually done.
968
- - Write the smallest straightforward code that fits the current step. Avoid defensive branches for hypothetical states that are not currently true.
969
- - Before asking a new question, check whether the answer is already present in the current heap, open decisions, or checkpoint.
970
- - If the previous step made no progress, prefer a different concrete action, a narrower fetch, or a user question instead of repeating equivalent code.
971
- - Use \`loop.open_decision(...)\` in one job to store grounded candidates, then \`loop.close_decision(...)\` in a later job to pick one stored candidate with \`selectedId\`.
972
- - Use \`loop.ask_user({ type: 'input', ... })\` only for open-ended preferences or missing free-form text that cannot be represented as a short explicit shortlist.
973
- - If you already have a short concrete shortlist, usually 2-7 candidates, generate code and call \`loop.ask_user({ type: 'choice', ... })\`. Do not downgrade that to a text input.
974
- - If multiple concrete records match a singular user reference such as "the invoice", "the order", "the shipment", or "the request", do not silently choose one by heuristic. Ask the user to choose unless the request already uniquely identifies the record.
975
- - For disambiguation between concrete known records, prefer \`type: 'choice'\` over \`type: 'input'\`. This is especially important for invoices, orders, shipments, requests, work orders, and cases.
976
- - For \`type: 'choice'\` prompts, make the options directly pickable by a human: use a stable value and a readable label that includes the identifier or title they are likely to recognize.
977
- - If a shortlist already exists, do not ask the user to type an exact database key or identifier manually. Present the shortlist as clickable choices instead.
978
- - When reasoning about free-form status strings, do not use brittle substring checks such as \`status.includes("paid")\` because values like \`"unpaid"\` would be misclassified. Prefer explicit positive matches such as \`unpaid\`, \`open\`, or \`overdue\`, or exact normalized comparisons.
979
- - When multiple concrete records match and \`loop.ask_user(...)\` is available, do not stop with a plain-text question like "Which invoice do you mean?". Persist the live workflow and ask through \`await loop.ask_user(...)\` instead.
980
- - If the user could reasonably answer with a partial identifier such as \`abcd\` for \`INV-abcd\`, that is another sign the question should be a \`type: 'choice'\` prompt with visible options rather than a free-text input.
981
- - Never ask for approval in plain text when \`loop.confirm(...)\` is available. Use \`loop.confirm(...)\` for consequential approval.
982
- - When the correct next step is a loop helper action, generate code and call that helper. Do not replace it with a conversational reply.
983
- - If you ask the user a new question in the current job, do not also call \`loop.close_loop(...)\` in that same job.
984
- - When you need user input or approval, await \`loop.ask_user(...)\` or \`loop.confirm(...)\`. The job will pause until the user answers, then resume from that awaited call.
985
- - It is valid to branch on the value returned by \`await loop.ask_user(...)\` or \`await loop.confirm(...)\` after the job resumes.
986
- - After \`await loop.ask_user(...)\` returns a concrete choice, continue the workflow in the same resumed job whenever that answer is enough to act. Do not stop with placeholder text such as "I can do that next" or "I'm ready to continue".
987
- - After \`await loop.confirm(...)\` returns \`true\`, execute the approved mutation in that same resumed job before returning. Do not end with placeholder text like "Approved, ready to make the change next."
988
- - Only stop immediately after a resumed prompt when the user declined, the workflow is now blocked, or you truly still need another missing piece of information.
989
- - If the user says stop, enough, or no further action, close the loop and end cleanly without asking another question.
990
- - If one clear item is already selected and the next step matters, prefer \`loop.confirm(...)\` over another exploratory question.
991
- - If one clear item is already selected and the only missing input is approval to proceed, use \`loop.confirm(...)\` rather than \`loop.ask_user(...)\`.
992
- - If the user already gave a usable scheduling window such as "Tuesday morning", treat that as enough to choose a reasonable concrete slot. Do not open another menu just to choose between nearby sub-slots unless a real conflict or hard business rule forces that follow-up.
993
- - For schedule changes, prefer one grounded recommendation plus one approval prompt. Avoid a second prompt for optional time-window micro-choices when you can pick a sensible default that still satisfies the user request.
994
- - If the user explicitly instructs you to perform a consequential action now, that instruction counts as approval. Do not add an extra confirmation step unless the user expressed hesitation, ambiguity, or asked you not to execute yet.
995
- - Direct imperatives such as "cancel this order", "send the reminder now", "approve this refund", or "charge it now" already authorize that exact step. Execute them directly instead of inserting \`loop.confirm(...)\`.
996
- - If the user says not to do anything irreversible yet, stop at recommendation, review, or approval. Do not collect checkout-only details like quantity, delivery notes, gift message, or optional preferences unless the user explicitly asks to move closer to purchase.
997
- - Once you have one solid recommendation, prefer summarizing it and asking for approval over gathering more optional preferences.
998
- - Prefer asking the user for the next missing input over fetching extra related data they did not ask for yet.
999
- - Avoid serial menus. After one clarifying choice, prefer acting on it, asking one short text question, or confirming rather than opening another menu.
1000
- - When the user asks for a summary "including" concrete records such as unpaid invoices, open cases, orders, or shipments, include the actual identifiers or titles of those records in the reply, not just aggregate counts.
1001
- - Call \`loop.close_loop(...)\` before stopping whenever the current workflow is completed, canceled, or clearly blocked.
1132
+ - Continue from the latest structured state. Treat WORKFLOW SNAPSHOT, EXECUTION CHECKPOINT, RECENT REFERENTS, SESSION HEAP, and AGENT LOOP STATE as the working memory for this request.
1133
+ - Use CAPABILITY SNAPSHOT to choose the next step, then use DOMAIN REFERENCE for exact signatures and query shapes.
1134
+ - Take the minimum next step that directly helps the user. Avoid duplicate work, speculative cleanup, or extra fetching that is not needed yet.
1135
+ - If the user names a record that is not already in the heap, fetch it from the graph instead of saying it is not in context.
1136
+ - Treat user-provided names as human references, not exact keys. If one strong partial match exists, use it. If several plausible matches exist, ask the user to choose.
1137
+ - Use RECENT REFERENTS to resolve follow-up references across turns, such as "that invoice", "that customer", "those products", or "the other one".
1138
+ - If the request has more than one reasonable interpretation, ask the user to clarify instead of guessing.
1139
+ - For comparisons, rankings, selections, or summaries, first identify the rule you are using. If that rule is not clear from the user request and DOMAIN REFERENCE, ask the user before choosing anything.
1140
+ - When the ranking, comparison, or selection rule is unclear, the minimum next step is the clarification itself. Do not run a placeholder query for a provisional winner before asking.
1141
+ - Reuse exact \`taskId\`, \`decisionId\`, and \`closureId\` values from AGENT LOOP STATE. Never invent or rewrite them.
1142
+ - If the request is ambiguous or clearly multi-step, create 2-4 short user-visible tasks and keep them updated as the workflow advances.
1143
+ - Use \`loop.ask_user({ type: 'choice', options: [...] })\` when you have a short, grounded shortlist the user can choose from. Otherwise use \`type: 'input'\`.
1144
+ - For an unclear ranking, comparison, or selection rule, prefer \`type: 'choice'\` when you can offer a short grounded list of plausible interpretations from the domain or nearby context.
1145
+ - When \`type: 'choice'\` fits, do not ask the same question as plain text with bullets such as "Common options:" or "Choose one of these:".
1146
+ - Use \`loop.confirm(...)\` for consequential approval unless the user already clearly instructed you to perform that exact action now.
1147
+ - Await \`loop.ask_user(...)\` and \`loop.confirm(...)\`. After the job resumes, continue in the same job whenever the answer is enough to act.
1148
+ - Use \`loop.open_decision(...)\` to persist grounded candidates, \`loop.close_decision(...)\` to resolve one, and \`loop.close_loop(...)\` when the workflow is completed, canceled, or blocked.
1149
+ - If you ask a new question in the current job, do not also close the loop in that same job.
1002
1150
 
1003
1151
  \u2500\u2500\u2500 CODE RULES \u2500\u2500\u2500
1004
1152
  - Import from \`./sandbox-tools\`.
1005
- - If you use \`heap\`, \`loop\`, \`agent_text_message\`, \`agent_heap_objects\`, or legacy \`agent_message\`, import them explicitly from \`./sandbox-tools\`.
1153
+ - If you use \`heap\`, \`loop\`, \`agent_text_message\`, or \`agent_heap_objects\`, import them explicitly from \`./sandbox-tools\`.
1006
1154
  - Write top-level executable code with \`await\` at top level.
1007
- - The generated job body must be plain runnable JavaScript. The DOMAIN TYPES block is only a reference for shapes and available methods.
1008
- - Do not write TypeScript-only syntax in executable code: no type annotations, no interfaces, no enums, no \`as Type\` casts, no \`satisfies\`, and no generic type parameters in code.
1009
- - Generated code must be valid against the DOMAIN TYPES block above.
1010
- - Only use classes, methods, and parameter shapes that are explicitly declared in those typedefs.
1011
- - Never invent helper methods such as \`find(...)\` or unsupported parameters such as \`id\` when the typedefs require \`path\`.
1012
- - Use \`ClassName.get({ path })\` only when you already know an object's graph path.
1013
- - Use \`ClassName.count()\` when you only need a total.
1014
- - Use \`ClassName.page({ page, perPage, saveAs })\` when you need both records and pagination metadata like \`totalCount\` or \`hasMore\`. \`perPage\` defaults to \`100\` and larger values are clamped to \`100\`.
1015
- - Use \`ClassName.list({ page, perPage, saveAs })\` to load one typed page of records. \`limit\` is only a legacy alias for \`perPage\`, \`perPage\` defaults to \`100\`, and larger values are clamped to \`100\`.
1016
- - Use \`for await (const item of ClassName.iterate({ perPage, maxItems }))\` for large batch jobs so you do not materialize the whole result set at once. \`perPage\` defaults to \`100\` and larger values are clamped to \`100\`.
1017
- - Instance methods: \`await instance.method_name(params)\`.
1018
- - Static methods: \`await ClassName.static_method(params)\`.
1019
- - Global effects: \`await effect_name(params)\`.
1020
- - When a child record has sparse fields, identify it through nearby graph context instead of only string-matching that child\u2019s local fields. Prefer traversing linked customer, case, work order, part request, and shipment records over broad guesswork.
1021
- - For blocker, delay, ETA, or "what is holding this up?" questions, do not stop at a parent status like \`in_progress\` or \`scheduled\` if linked dependencies exist. Trace into the likely dependency chain first: approval -> work order -> part request -> shipment -> carrier update.
1022
- - A generic parent status is not a sufficient blocker explanation when a linked part request, approval, shipment, customs hold, or vendor delay may be the real cause.
1023
- - If a case summary or latest customer message mentions a part, shipment, ETA, customs, vendor, approval, regulator, kit, or delay, treat that as a strong hint to inspect the linked dependency records before answering.
1024
- - When you already found the correct parent case, inspect its linked child records even if the child summaries use different wording. Do not require a work order, part request, or shipment description to repeat the exact phrase that identified the parent case.
1025
- - Status fields are free-form operational strings, not strict enums. Normalize spelling mentally and do not rely on brittle hard-coded sets that miss variants like \`in-progress\`, \`in_progress\`, \`awaiting-part\`, or \`approval-submitted\`.
1026
- - Do not discard a case, work order, part request, or shipment only because its status string does not match your preferred "open" spelling. If the record is otherwise the clear match, inspect it.
1027
- - Reuse \`heap.getVar(name)\`, \`heap.setVar(name, value)\`, and \`heap.deleteVar(name)\` only when it clearly helps the next step. Do not mirror data into the heap just for completeness.
1028
- - Prefer \`heap.setVar(name, value)\` for scalars or one selected instance. Prefer \`ClassName.list({ page, perPage, saveAs })\` for reusable list pages instead of \`heap.setVar(name, array)\`.
1029
- - Never write an empty array into the heap. If a filtered list is empty, keep it local or clear the previous heap value with \`heap.deleteVar(name)\`.
1030
- - Prefer heap-backed state that represents the current choice or recommendation. Avoid storing extra scalar bookkeeping unless it is needed for the next concrete step.
1031
- - Only store true sandbox instances, typed lists of sandbox instances, or scalars in the heap. Results returned by static effects like availability/search helpers are often plain JSON, not sandbox instances.
1032
- - If a helper returns plain JSON candidates, keep them local, store only a scalar like the chosen id, or resolve the matching sandbox instance before writing it into the heap.
1033
- - When reading heap values, prefer generated generic typings such as \`await heap.getVar<Book[]>("my_books")\` or \`await heap.getVar<Book>("selected_book")\`.
1034
- - If a focused heap variable already points to a known class, read it with that exact generic type and act on it directly. Do not use \`heap.getVar<any>(...)\` or cast through \`any\` when the class is already clear from the prompt. For example, prefer \`await heap.getVar<Order>("selected_order")\` over \`await heap.getVar<any>("selected_order")\`.
1035
- - Use the injected \`loop\` helpers when you need to manage the workflow itself:
1036
- \`loop.ask_user(...)\`, \`loop.confirm(...)\`, \`loop.open_decision(...)\`, \`loop.close_decision(...)\`,
1037
- \`loop.create_task(...)\`, \`loop.update_task(...)\`, \`loop.complete_task(...)\`, and \`loop.close_loop(...)\`.
1038
- - Loop helper semantics:
1039
- - \`loop.open_decision(...)\`: store explicit candidates from the current job so a later job can revisit the same decision. Keep and reuse the returned \`decisionId\`.
1040
- - \`loop.close_decision(...)\`: resolve an open decision by choosing one stored candidate with \`selectedId\` and recording why. Candidates may be any JSON objects, but each one must have an \`id\`.
1041
- - \`loop.ask_user(...)\`: pause the job and ask the user for missing input. Default to \`type: 'input'\`; use \`type: 'choice'\` only for a short explicit shortlist. Write \`const answer = await loop.ask_user(...)\`, then continue the same job once the user answers.
1042
- - \`loop.confirm(...)\`: pause the job for approval before a consequential action. Do not simulate confirmation in plain text. Write \`const approved = await loop.confirm(...)\`, then branch on that approval once the job resumes.
1043
- - \`loop.create_task(...)\`, \`loop.update_task(...)\`, \`loop.complete_task(...)\`: keep a short task list that later jobs can continue and finish.
1044
- - \`loop.close_loop(...)\`: record the current workflow outcome with a short summary before stopping. Do not call it in the same job that opens a new user prompt unless the workflow is explicitly blocked. This does not end the session forever.
1045
- - Avoid \`as any\` and other broad casts when the DOMAIN TYPES block already tells you the correct class or list type.
1046
- - Prefer manipulating heap-backed instances and typed lists instead of returning raw JSON blobs or object IDs unless the user explicitly asks for them.
1047
- - Every job that intends to answer the user must emit at least one explicit UI message with \`agent_text_message(...)\` and/or \`agent_heap_objects(...)\`.
1048
- - Use \`agent_text_message("...")\` for all user-visible text shown in the UI.
1049
- - Use \`agent_heap_objects(...)\` only when you want the UI to render heap-backed records or lists.
1050
- - If you want to show both text and records, call \`agent_text_message(...)\` and \`agent_heap_objects(...)\` separately in whatever order fits the interaction.
1051
- - \`agent_text_message(...)\` should be used with a plain text string in normal generated code.
1052
- - \`agent_heap_objects(...)\` should point at heap-backed values: explicit \`entryPaths\` / \`listNames\` / \`variableNames\`, a named list saved with \`saveAs\`, or values read back from \`heap.getVar(...)\`.
1053
- - If you just fetched records and want to show them in the UI, save or reference them through the heap first, then call \`agent_heap_objects(...)\`. Do not try to hand-build UI payloads in job code.
1054
- - Do not assume heap changes will be displayed automatically. If records should appear in the UI, you must call \`agent_heap_objects(...)\`.
1055
- - Never write \`return { reply, show }\` or \`return { show: ... }\` for UI. If you want the UI to render records or lists, call \`agent_heap_objects(...)\` instead.
1056
- - Do not rely on the final return value for user-visible output. A plain return value is not considered a displayed UI answer.
1057
- - \`agent_message(...)\` remains available as a legacy compatibility alias, but prefer \`agent_text_message(...)\` and \`agent_heap_objects(...)\` in new code.
1058
- - Do not return bare structured JSON, low-level diagnostics, or database-shaped payloads as the final answer unless the user explicitly asks for them.
1059
- - Prefer simple executable JavaScript over clever interpolation. Avoid nested template literals or unusually dense inline expressions when a small temporary variable or string concatenation would be clearer and safer.
1155
+ - The generated job body must be plain runnable JavaScript. Do not use TypeScript-only syntax.
1156
+ - Follow the exact classes, methods, and parameter shapes in DOMAIN REFERENCE. Do not invent helpers or unsupported arguments.
1157
+ - Use \`ClassName.get({ path })\` only for known graph paths when you want a direct graph fetch.
1158
+ - Use \`ClassName.count()\` for totals, \`ClassName.page({ page, perPage, saveAs })\` when you need \`items\` plus \`totalCount\` or \`hasMore\`, \`ClassName.list({ page, perPage, saveAs })\` for one page of records, and \`ClassName.iterate({ perPage, maxItems })\` for large scans.
1159
+ - \`perPage\` defaults to \`100\` and is capped at \`100\`.
1160
+ - Push \`filter\`, \`search\`, and \`sort\` into graph queries instead of fetching a page and processing it locally.
1161
+ - A property appearing on a record does not make it valid in \`filter\` or \`sort\`; only use fields and operators that are explicitly exposed in DOMAIN REFERENCE.
1162
+ - Choose \`sort.field\` verbatim from the sortable fields listed in DOMAIN REFERENCE. Do not sort by relationship names, related-record collections, counts, totals, or other derived metrics unless they are explicitly listed as sortable.
1163
+ - If ordering alone answers the request, use \`sort\` without inventing a \`filter\`.
1164
+ - Do not invent proxy metrics, fallback heuristics, or made-up tie-breakers to resolve ambiguity. If the rule is unclear, ask the user with \`loop.ask_user(...)\`.
1165
+ - Do not fetch, sort, or show a provisional record just to have something to display while the real ranking or selection rule is still ambiguous.
1166
+ - Call instance methods on instances, static methods on classes, and global effects by name.
1167
+ - Use \`heap.getEntry(path)\` for remembered heap entries, \`heap.getList(name)\` for remembered lists, and \`heap.getVar(name)\` only for named variables.
1168
+ - Use \`heap.setVar(...)\` and \`heap.deleteVar(...)\` only when they help the next step.
1169
+ - Prefer \`heap.setVar(...)\` for scalars or one selected instance. Prefer \`ClassName.list({ saveAs })\` for reusable typed lists. Empty arrays are allowed.
1170
+ - Only store sandbox instances, typed lists, or scalars in the heap. If a helper returns plain JSON, keep it local or store only the chosen scalar.
1171
+ - Use the \`loop\` helpers to manage workflow state: \`ask_user\`, \`confirm\`, \`open_decision\`, \`close_decision\`, \`create_task\`, \`update_task\`, \`complete_task\`, and \`close_loop\`.
1172
+ - Use \`type: 'choice'\` only for short grounded options. Use \`type: 'input'\` when the answer should stay open-ended.
1173
+ - \`loop.confirm(...)\` is for consequential approval. Do not ask for approval in plain text.
1174
+ - After \`await loop.ask_user(...)\` or \`await loop.confirm(...)\`, continue in the same resumed job when the answer is enough to act.
1175
+ - Every job that answers the user must emit \`agent_text_message(...)\` and/or \`agent_heap_objects(...)\`.
1176
+ - Use \`agent_text_message(...)\` for user-visible text.
1177
+ - Use \`agent_heap_objects(...)\` for user-visible records. You may pass sandbox instances directly, or heap-backed \`entryPaths\`, \`listNames\`, and \`variableNames\` when you already have them. Use \`saveAs\` or \`heap.setVar(...)\` when you need a reusable named selection.
1178
+ - Do not rely on the final return value for UI output. Do not return ad-hoc \`reply\` / \`show\` payloads instead of explicit agent message calls.
1179
+ - Keep the code small and direct. Avoid speculative branches, broad casts, and raw JSON dumps unless the user asked for them.
1060
1180
  - Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
1061
1181
  }
1062
1182
 
1063
- export { buildContinuationInstruction, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, createHarnessVerifierSnapshot, evaluateContinuation, getCurrentClosureId, getExclusivePromptTarget, hasOpenPrompt, projectHeapSummary, projectLoopSummary, projectWorkflowFocus, projectWorkflowSummary, reviewGeneratedJobCode };
1183
+ export { buildContinuationInstruction, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentReferentBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, createHarnessVerifierSnapshot, evaluateContinuation, getCurrentClosureId, getExclusivePromptTarget, hasOpenPrompt, projectConversationReferentFocus, projectConversationReferentSummary, projectHeapSummary, projectLoopSummary, projectWorkflowFocus, projectWorkflowSummary, reviewGeneratedJobCode };
1064
1184
  //# sourceMappingURL=agent-harness.mjs.map
1065
1185
  //# sourceMappingURL=agent-harness.mjs.map