@granular-software/sdk 0.4.29 → 0.4.31
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/README.md +25 -20
- package/dist/agent-evals.d.mts +7 -6
- package/dist/agent-evals.d.ts +7 -6
- package/dist/agent-evals.js +1002 -525
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +1002 -525
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/agent-harness.d.mts +11 -2
- package/dist/agent-harness.d.ts +11 -2
- package/dist/agent-harness.js +248 -125
- package/dist/agent-harness.js.map +1 -1
- package/dist/agent-harness.mjs +246 -126
- package/dist/agent-harness.mjs.map +1 -1
- package/dist/cli/index.js +773 -394
- package/dist/{client-CigjaeO8.d.mts → client-CTH1hfwe.d.mts} +255 -117
- package/dist/{client-CigjaeO8.d.ts → client-CTH1hfwe.d.ts} +255 -117
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +938 -435
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +934 -436
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/agent-harness.js
CHANGED
|
@@ -123,21 +123,6 @@ function reviewGeneratedJobCode(code) {
|
|
|
123
123
|
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."
|
|
124
124
|
});
|
|
125
125
|
}
|
|
126
|
-
const askUserCalls = normalized.match(/await\s+loop\.ask_user\s*\(\s*\{[\s\S]*?\}\s*\)/g) || [];
|
|
127
|
-
for (const call of askUserCalls) {
|
|
128
|
-
const usesChoiceType = /type\s*:\s*['"]choice['"]/.test(call);
|
|
129
|
-
const usesInputType = /type\s*:\s*['"]input['"]/.test(call);
|
|
130
|
-
const hasDisambiguationLanguage = /(which|choose|pick|select)/i.test(call) && /(invoice|order|shipment|request|case|work[\s_-]?order)/i.test(call);
|
|
131
|
-
const includesShortlistOptions = /options\s*:\s*\[/.test(call);
|
|
132
|
-
if (!usesChoiceType && (usesInputType || hasDisambiguationLanguage || includesShortlistOptions)) {
|
|
133
|
-
issues.push({
|
|
134
|
-
code: "disambiguation_requires_choice",
|
|
135
|
-
severity: "error",
|
|
136
|
-
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."
|
|
137
|
-
});
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
126
|
}
|
|
142
127
|
const hasConversationalReturn = /return\s+[`'"]/.test(normalized) || /\breply\s*:/.test(normalized) || /\bagent_message\s*\(/.test(normalized) || /\bagent_text_message\s*\(/.test(normalized);
|
|
143
128
|
const returnsObjectLiteral = /return\s+\{[\s\S]*?\}/.test(normalized);
|
|
@@ -183,6 +168,184 @@ function extractFocusHintsFromActionSummary(actionSummaryLines) {
|
|
|
183
168
|
entryPaths: uniqueStrings(entryPaths, 8)
|
|
184
169
|
};
|
|
185
170
|
}
|
|
171
|
+
function normalizeActionSummaryForPrompt(line) {
|
|
172
|
+
return line.replace(/\blimit=/g, "perPage=").replace(/\blimit:/g, "perPage:");
|
|
173
|
+
}
|
|
174
|
+
function collectConversationReferents(liveDoc) {
|
|
175
|
+
const conversation = asRecord(liveDoc?.conversation);
|
|
176
|
+
const persistedReferents = asArray(conversation?.referents).map((value) => asRecord(value)).filter((value) => Boolean(value));
|
|
177
|
+
if (persistedReferents.length > 0) {
|
|
178
|
+
return persistedReferents.slice().sort((left, right) => (right.ts || 0) - (left.ts || 0));
|
|
179
|
+
}
|
|
180
|
+
const heap = asRecord(liveDoc?.heap);
|
|
181
|
+
const entriesByPath = asRecord(heap?.entriesByPath) || {};
|
|
182
|
+
const listsByName = asRecord(heap?.listsByName) || {};
|
|
183
|
+
const variablesByName = asRecord(heap?.variablesByName) || {};
|
|
184
|
+
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));
|
|
185
|
+
const referents = [];
|
|
186
|
+
const seen = /* @__PURE__ */ new Set();
|
|
187
|
+
const pushReferent = (referent) => {
|
|
188
|
+
if (!referent?.kind || !referent.ref) return;
|
|
189
|
+
const key = `${referent.kind}:${referent.ref}`;
|
|
190
|
+
if (seen.has(key)) return;
|
|
191
|
+
seen.add(key);
|
|
192
|
+
referents.push(referent);
|
|
193
|
+
};
|
|
194
|
+
for (const message of messages) {
|
|
195
|
+
if (message.role !== "assistant") continue;
|
|
196
|
+
const show = asRecord(message.show);
|
|
197
|
+
if (!show) continue;
|
|
198
|
+
const ts = Number(message.ts) || 0;
|
|
199
|
+
const messageId = typeof message.id === "string" ? message.id : void 0;
|
|
200
|
+
const jobId = typeof message.jobId === "string" ? message.jobId : void 0;
|
|
201
|
+
for (const entryPath of uniqueStrings(asArray(show.entryPaths))) {
|
|
202
|
+
const entry = asRecord(entriesByPath[entryPath]);
|
|
203
|
+
pushReferent({
|
|
204
|
+
id: `entry:${entryPath}`,
|
|
205
|
+
kind: "entry",
|
|
206
|
+
ref: entryPath,
|
|
207
|
+
entryPath,
|
|
208
|
+
className: typeof entry?.className === "string" ? entry.className : void 0,
|
|
209
|
+
label: typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim() : typeof entry?.id === "string" && entry.id.trim() ? entry.id.trim() : entryPath,
|
|
210
|
+
messageId,
|
|
211
|
+
jobId,
|
|
212
|
+
ts
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
for (const listName of uniqueStrings(asArray(show.listNames))) {
|
|
216
|
+
const list = asRecord(listsByName[listName]);
|
|
217
|
+
pushReferent({
|
|
218
|
+
id: `list:${listName}`,
|
|
219
|
+
kind: "list",
|
|
220
|
+
ref: listName,
|
|
221
|
+
listName,
|
|
222
|
+
className: typeof list?.className === "string" ? list.className : void 0,
|
|
223
|
+
count: Array.isArray(list?.paths) ? list.paths.length : null,
|
|
224
|
+
messageId,
|
|
225
|
+
jobId,
|
|
226
|
+
ts
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
for (const variableName of uniqueStrings(
|
|
230
|
+
asArray(show.variableNames)
|
|
231
|
+
)) {
|
|
232
|
+
const variable = asRecord(variablesByName[variableName]);
|
|
233
|
+
const entryPath = typeof variable?.entryPath === "string" ? variable.entryPath : void 0;
|
|
234
|
+
const listName = typeof variable?.listName === "string" ? variable.listName : void 0;
|
|
235
|
+
const entry = entryPath ? asRecord(entriesByPath[entryPath]) : null;
|
|
236
|
+
const list = listName ? asRecord(listsByName[listName]) : null;
|
|
237
|
+
pushReferent({
|
|
238
|
+
id: `variable:${variableName}`,
|
|
239
|
+
kind: "variable",
|
|
240
|
+
ref: variableName,
|
|
241
|
+
variableName,
|
|
242
|
+
variableKind: typeof variable?.kind === "string" ? variable.kind : void 0,
|
|
243
|
+
entryPath,
|
|
244
|
+
listName,
|
|
245
|
+
className: typeof variable?.className === "string" ? variable.className : typeof entry?.className === "string" ? entry.className : typeof list?.className === "string" ? list.className : void 0,
|
|
246
|
+
label: typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim() : typeof entry?.id === "string" && entry.id.trim() ? entry.id.trim() : null,
|
|
247
|
+
count: variable?.kind === "list" && Array.isArray(list?.paths) ? list.paths.length : null,
|
|
248
|
+
scalarValue: variable?.kind === "scalar" && (typeof variable.value === "string" || typeof variable.value === "number" || typeof variable.value === "boolean" || variable.value === null) ? variable.value : void 0,
|
|
249
|
+
messageId,
|
|
250
|
+
jobId,
|
|
251
|
+
ts
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return referents;
|
|
256
|
+
}
|
|
257
|
+
function projectConversationReferentFocus(liveDoc) {
|
|
258
|
+
const heap = asRecord(liveDoc?.heap);
|
|
259
|
+
const listsByName = asRecord(heap?.listsByName) || {};
|
|
260
|
+
const referents = collectConversationReferents(liveDoc);
|
|
261
|
+
const entryPaths = [];
|
|
262
|
+
const listNames = [];
|
|
263
|
+
const variableNames = [];
|
|
264
|
+
for (const referent of referents.slice(0, 8)) {
|
|
265
|
+
if (referent.kind === "entry" && typeof referent.entryPath === "string") {
|
|
266
|
+
entryPaths.push(referent.entryPath);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (referent.kind === "list" && typeof referent.listName === "string") {
|
|
270
|
+
listNames.push(referent.listName);
|
|
271
|
+
const list = asRecord(listsByName[referent.listName]);
|
|
272
|
+
entryPaths.push(...asArray(list?.paths).slice(0, 4));
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (referent.kind === "variable" && typeof referent.variableName === "string") {
|
|
276
|
+
variableNames.push(referent.variableName);
|
|
277
|
+
if (typeof referent.entryPath === "string") {
|
|
278
|
+
entryPaths.push(referent.entryPath);
|
|
279
|
+
}
|
|
280
|
+
if (typeof referent.listName === "string") {
|
|
281
|
+
listNames.push(referent.listName);
|
|
282
|
+
const list = asRecord(listsByName[referent.listName]);
|
|
283
|
+
entryPaths.push(...asArray(list?.paths).slice(0, 4));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
entryPaths: uniqueStrings(entryPaths, 8),
|
|
289
|
+
listNames: uniqueStrings(listNames, 4),
|
|
290
|
+
variableNames: uniqueStrings(variableNames, 4)
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function projectConversationReferentSummary(liveDoc) {
|
|
294
|
+
const referents = collectConversationReferents(liveDoc).slice(0, 8);
|
|
295
|
+
if (referents.length === 0) {
|
|
296
|
+
return "No recent referents recorded from prior assistant replies.";
|
|
297
|
+
}
|
|
298
|
+
const entryLines = [];
|
|
299
|
+
const listLines = [];
|
|
300
|
+
const variableLines = [];
|
|
301
|
+
for (const referent of referents) {
|
|
302
|
+
if (referent.kind === "entry" && referent.entryPath) {
|
|
303
|
+
const label = referent.label || referent.entryPath;
|
|
304
|
+
const classLabel = referent.className || "unknown";
|
|
305
|
+
entryLines.push(`- ${label} <${referent.entryPath}> [${classLabel}]`);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (referent.kind === "list" && referent.listName) {
|
|
309
|
+
const classLabel = referent.className || "unknown";
|
|
310
|
+
const countLabel = typeof referent.count === "number" ? referent.count : "?";
|
|
311
|
+
listLines.push(
|
|
312
|
+
`- ${referent.listName}: list<${classLabel}> -> ${countLabel} item(s)`
|
|
313
|
+
);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (referent.kind === "variable" && referent.variableName) {
|
|
317
|
+
if (referent.variableKind === "entry" && referent.entryPath && referent.className) {
|
|
318
|
+
const label = referent.label || referent.entryPath;
|
|
319
|
+
variableLines.push(
|
|
320
|
+
`- ${referent.variableName}: entry<${referent.className}> -> ${label} <${referent.entryPath}>`
|
|
321
|
+
);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (referent.variableKind === "list" && referent.listName && referent.className) {
|
|
325
|
+
const countLabel = typeof referent.count === "number" ? referent.count : "?";
|
|
326
|
+
variableLines.push(
|
|
327
|
+
`- ${referent.variableName}: list<${referent.className}> -> ${countLabel} item(s) via ${referent.listName}`
|
|
328
|
+
);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (referent.variableKind === "scalar") {
|
|
332
|
+
variableLines.push(
|
|
333
|
+
`- ${referent.variableName}: scalar = ${formatScalar(referent.scalarValue)}`
|
|
334
|
+
);
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
variableLines.push(`- ${referent.variableName}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const lines = [];
|
|
341
|
+
lines.push("Entries:");
|
|
342
|
+
lines.push(...entryLines.length > 0 ? entryLines : ["- none"]);
|
|
343
|
+
lines.push("", "Lists:");
|
|
344
|
+
lines.push(...listLines.length > 0 ? listLines : ["- none"]);
|
|
345
|
+
lines.push("", "Variables:");
|
|
346
|
+
lines.push(...variableLines.length > 0 ? variableLines : ["- none"]);
|
|
347
|
+
return lines.join("\n");
|
|
348
|
+
}
|
|
186
349
|
function getCurrentClosureId(liveDoc) {
|
|
187
350
|
const loop = asRecord(liveDoc?.loop);
|
|
188
351
|
return typeof loop?.currentClosureId === "string" ? loop.currentClosureId : null;
|
|
@@ -395,7 +558,9 @@ function projectWorkflowFocus(liveDoc, pendingPrompts = [], options) {
|
|
|
395
558
|
variableNames: uniqueStrings(variableNames, 4),
|
|
396
559
|
listNames: uniqueStrings(listNames, 4),
|
|
397
560
|
entryPaths: uniqueStrings(entryPaths, 6),
|
|
398
|
-
recentActionSummary: uniqueStrings(actionSummaryLines, 8)
|
|
561
|
+
recentActionSummary: uniqueStrings(actionSummaryLines, 8).map(
|
|
562
|
+
normalizeActionSummaryForPrompt
|
|
563
|
+
)
|
|
399
564
|
};
|
|
400
565
|
}
|
|
401
566
|
function projectWorkflowSummary(liveDoc, pendingPrompts = [], options) {
|
|
@@ -792,17 +957,14 @@ ${resultPreview}` : null
|
|
|
792
957
|
].filter(Boolean).join("\n\n");
|
|
793
958
|
}
|
|
794
959
|
function buildGranularAgentDomainBlock(domainDocumentation) {
|
|
795
|
-
return domainDocumentation?.trim() || "No domain
|
|
960
|
+
return domainDocumentation?.trim() || "No domain reference available. The graph may not be ready yet.";
|
|
796
961
|
}
|
|
797
962
|
function buildGranularAgentSessionBlock(sessionContext) {
|
|
798
963
|
if (!sessionContext) return "No session metadata available.";
|
|
799
964
|
const rows = [
|
|
800
965
|
["sandboxId", sessionContext.sandboxId],
|
|
801
966
|
["environmentId", sessionContext.environmentId],
|
|
802
|
-
["
|
|
803
|
-
["granularId", sessionContext.granularId],
|
|
804
|
-
["userName", sessionContext.userName],
|
|
805
|
-
["domainRevision", sessionContext.domainRevision]
|
|
967
|
+
["userName", sessionContext.userName]
|
|
806
968
|
];
|
|
807
969
|
const activeRows = rows.filter(([, value]) => Boolean(value));
|
|
808
970
|
if (activeRows.length === 0) return "No session metadata available.";
|
|
@@ -811,6 +973,9 @@ function buildGranularAgentSessionBlock(sessionContext) {
|
|
|
811
973
|
function buildGranularAgentHeapBlock(heapSummary) {
|
|
812
974
|
return heapSummary?.trim() || "Heap is empty for this session.";
|
|
813
975
|
}
|
|
976
|
+
function buildGranularAgentReferentBlock(referentSummary) {
|
|
977
|
+
return referentSummary?.trim() || "No recent referents recorded from prior assistant replies.";
|
|
978
|
+
}
|
|
814
979
|
function buildGranularAgentLoopBlock(loopSummary) {
|
|
815
980
|
return loopSummary?.trim() || "No active loop state recorded for this session.";
|
|
816
981
|
}
|
|
@@ -834,7 +999,7 @@ function buildGranularAgentToolBlock(tools) {
|
|
|
834
999
|
(tool) => Boolean(tool.className && !tool.static)
|
|
835
1000
|
);
|
|
836
1001
|
const lines = [
|
|
837
|
-
"Treat this block as the planning map. Use DOMAIN
|
|
1002
|
+
"Treat this block as the planning map. Use DOMAIN REFERENCE below for exact signatures and query examples."
|
|
838
1003
|
];
|
|
839
1004
|
const appendGroup = (title, group) => {
|
|
840
1005
|
lines.push(`- ${title}:`);
|
|
@@ -882,7 +1047,10 @@ function buildGranularAgentCheckpointBlock(checkpoint) {
|
|
|
882
1047
|
if (Array.isArray(checkpoint.latestActionSummary) && checkpoint.latestActionSummary.length > 0) {
|
|
883
1048
|
lines.push("latestActionSummary:");
|
|
884
1049
|
for (const line of checkpoint.latestActionSummary.slice(0, 8)) {
|
|
885
|
-
|
|
1050
|
+
const normalizedLine = normalizeActionSummaryForPrompt(line);
|
|
1051
|
+
lines.push(
|
|
1052
|
+
normalizedLine.startsWith("- ") ? normalizedLine : `- ${normalizedLine}`
|
|
1053
|
+
);
|
|
886
1054
|
}
|
|
887
1055
|
}
|
|
888
1056
|
if (checkpoint.latestJobResult?.trim()) {
|
|
@@ -898,9 +1066,10 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
898
1066
|
const workflowBlock = buildGranularAgentWorkflowBlock(input.workflowSummary);
|
|
899
1067
|
const checkpointBlock = buildGranularAgentCheckpointBlock(input.checkpoint);
|
|
900
1068
|
const heapBlock = buildGranularAgentHeapBlock(input.heapSummary);
|
|
1069
|
+
const referentBlock = buildGranularAgentReferentBlock(input.referentSummary);
|
|
901
1070
|
const loopBlock = buildGranularAgentLoopBlock(input.loopSummary);
|
|
902
1071
|
return `You are an AI assistant for a live Granular session.
|
|
903
|
-
You can help the user understand the domain, answer questions, or generate and execute
|
|
1072
|
+
You can help the user understand the domain, answer questions, or generate and execute code against the live session.
|
|
904
1073
|
Your tone must be natural and human-like.
|
|
905
1074
|
|
|
906
1075
|
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.
|
|
@@ -909,6 +1078,8 @@ When you call \`execute_code\`, additional assistant text must be either:
|
|
|
909
1078
|
- a brief summary of the actions the generated code will perform.
|
|
910
1079
|
Do not include any other kind of commentary when calling \`execute_code\`.
|
|
911
1080
|
- 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(...)\`.
|
|
1081
|
+
- 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.
|
|
1082
|
+
- 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.
|
|
912
1083
|
- 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.
|
|
913
1084
|
|
|
914
1085
|
\u2500\u2500\u2500 STREAMING COMMENT RULES \u2500\u2500\u2500
|
|
@@ -928,6 +1099,7 @@ Do not include any other kind of commentary when calling \`execute_code\`.
|
|
|
928
1099
|
- Do not say "sandbox" in user-facing text unless the user is explicitly asking about the runtime environment itself.
|
|
929
1100
|
- If you need clarification, ask in everyday language.
|
|
930
1101
|
- 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.
|
|
1102
|
+
- 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.
|
|
931
1103
|
- Keep replies concise and clear.
|
|
932
1104
|
- This is a conversation UI, not an API console. Favor human answers over machine-shaped payloads.
|
|
933
1105
|
|
|
@@ -937,9 +1109,9 @@ ${sessionBlock}
|
|
|
937
1109
|
\u2500\u2500\u2500 CAPABILITY SNAPSHOT \u2500\u2500\u2500
|
|
938
1110
|
${toolBlock}
|
|
939
1111
|
|
|
940
|
-
\u2500\u2500\u2500 DOMAIN
|
|
1112
|
+
\u2500\u2500\u2500 DOMAIN REFERENCE (from ./sandbox-tools) \u2500\u2500\u2500
|
|
941
1113
|
Import classes and effect functions from \`./sandbox-tools\` in generated code.
|
|
942
|
-
|
|
1114
|
+
Use the TypeScript declarations for exact signatures. When present, the generated usage notes below them show query patterns and examples.
|
|
943
1115
|
|
|
944
1116
|
${domainBlock}
|
|
945
1117
|
|
|
@@ -949,6 +1121,9 @@ ${checkpointBlock}
|
|
|
949
1121
|
\u2500\u2500\u2500 WORKFLOW SNAPSHOT \u2500\u2500\u2500
|
|
950
1122
|
${workflowBlock}
|
|
951
1123
|
|
|
1124
|
+
\u2500\u2500\u2500 RECENT REFERENTS \u2500\u2500\u2500
|
|
1125
|
+
${referentBlock}
|
|
1126
|
+
|
|
952
1127
|
\u2500\u2500\u2500 SESSION HEAP \u2500\u2500\u2500
|
|
953
1128
|
${heapBlock}
|
|
954
1129
|
|
|
@@ -956,109 +1131,54 @@ ${heapBlock}
|
|
|
956
1131
|
${loopBlock}
|
|
957
1132
|
|
|
958
1133
|
\u2500\u2500\u2500 LOOP PLAYBOOK \u2500\u2500\u2500
|
|
959
|
-
-
|
|
960
|
-
-
|
|
961
|
-
-
|
|
962
|
-
-
|
|
963
|
-
-
|
|
964
|
-
-
|
|
965
|
-
- If
|
|
966
|
-
-
|
|
967
|
-
-
|
|
968
|
-
-
|
|
969
|
-
-
|
|
970
|
-
-
|
|
971
|
-
-
|
|
972
|
-
-
|
|
973
|
-
- Use \`loop.
|
|
974
|
-
-
|
|
975
|
-
-
|
|
976
|
-
- If
|
|
977
|
-
- 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.
|
|
978
|
-
- 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.
|
|
979
|
-
- 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.
|
|
980
|
-
- 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.
|
|
981
|
-
- 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.
|
|
982
|
-
- 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.
|
|
983
|
-
- Never ask for approval in plain text when \`loop.confirm(...)\` is available. Use \`loop.confirm(...)\` for consequential approval.
|
|
984
|
-
- When the correct next step is a loop helper action, generate code and call that helper. Do not replace it with a conversational reply.
|
|
985
|
-
- If you ask the user a new question in the current job, do not also call \`loop.close_loop(...)\` in that same job.
|
|
986
|
-
- 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.
|
|
987
|
-
- It is valid to branch on the value returned by \`await loop.ask_user(...)\` or \`await loop.confirm(...)\` after the job resumes.
|
|
988
|
-
- 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".
|
|
989
|
-
- 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."
|
|
990
|
-
- 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.
|
|
991
|
-
- If the user says stop, enough, or no further action, close the loop and end cleanly without asking another question.
|
|
992
|
-
- If one clear item is already selected and the next step matters, prefer \`loop.confirm(...)\` over another exploratory question.
|
|
993
|
-
- If one clear item is already selected and the only missing input is approval to proceed, use \`loop.confirm(...)\` rather than \`loop.ask_user(...)\`.
|
|
994
|
-
- 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.
|
|
995
|
-
- 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.
|
|
996
|
-
- 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.
|
|
997
|
-
- 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(...)\`.
|
|
998
|
-
- 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.
|
|
999
|
-
- Once you have one solid recommendation, prefer summarizing it and asking for approval over gathering more optional preferences.
|
|
1000
|
-
- Prefer asking the user for the next missing input over fetching extra related data they did not ask for yet.
|
|
1001
|
-
- Avoid serial menus. After one clarifying choice, prefer acting on it, asking one short text question, or confirming rather than opening another menu.
|
|
1002
|
-
- 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.
|
|
1003
|
-
- Call \`loop.close_loop(...)\` before stopping whenever the current workflow is completed, canceled, or clearly blocked.
|
|
1134
|
+
- 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.
|
|
1135
|
+
- Use CAPABILITY SNAPSHOT to choose the next step, then use DOMAIN REFERENCE for exact signatures and query shapes.
|
|
1136
|
+
- Take the minimum next step that directly helps the user. Avoid duplicate work, speculative cleanup, or extra fetching that is not needed yet.
|
|
1137
|
+
- 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.
|
|
1138
|
+
- 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.
|
|
1139
|
+
- Use RECENT REFERENTS to resolve follow-up references across turns, such as "that invoice", "that customer", "those products", or "the other one".
|
|
1140
|
+
- If the request has more than one reasonable interpretation, ask the user to clarify instead of guessing.
|
|
1141
|
+
- 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.
|
|
1142
|
+
- 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.
|
|
1143
|
+
- Reuse exact \`taskId\`, \`decisionId\`, and \`closureId\` values from AGENT LOOP STATE. Never invent or rewrite them.
|
|
1144
|
+
- If the request is ambiguous or clearly multi-step, create 2-4 short user-visible tasks and keep them updated as the workflow advances.
|
|
1145
|
+
- Use \`loop.ask_user({ type: 'choice', options: [...] })\` when you have a short, grounded shortlist the user can choose from. Otherwise use \`type: 'input'\`.
|
|
1146
|
+
- 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.
|
|
1147
|
+
- When \`type: 'choice'\` fits, do not ask the same question as plain text with bullets such as "Common options:" or "Choose one of these:".
|
|
1148
|
+
- Use \`loop.confirm(...)\` for consequential approval unless the user already clearly instructed you to perform that exact action now.
|
|
1149
|
+
- Await \`loop.ask_user(...)\` and \`loop.confirm(...)\`. After the job resumes, continue in the same job whenever the answer is enough to act.
|
|
1150
|
+
- 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.
|
|
1151
|
+
- If you ask a new question in the current job, do not also close the loop in that same job.
|
|
1004
1152
|
|
|
1005
1153
|
\u2500\u2500\u2500 CODE RULES \u2500\u2500\u2500
|
|
1006
1154
|
- Import from \`./sandbox-tools\`.
|
|
1007
|
-
- If you use \`heap\`, \`loop\`, \`agent_text_message\`,
|
|
1155
|
+
- If you use \`heap\`, \`loop\`, \`agent_text_message\`, or \`agent_heap_objects\`, import them explicitly from \`./sandbox-tools\`.
|
|
1008
1156
|
- Write top-level executable code with \`await\` at top level.
|
|
1009
|
-
- The generated job body must be plain runnable JavaScript.
|
|
1010
|
-
-
|
|
1011
|
-
-
|
|
1012
|
-
-
|
|
1013
|
-
-
|
|
1014
|
-
-
|
|
1015
|
-
-
|
|
1016
|
-
-
|
|
1017
|
-
-
|
|
1018
|
-
-
|
|
1019
|
-
-
|
|
1020
|
-
-
|
|
1021
|
-
-
|
|
1022
|
-
-
|
|
1023
|
-
-
|
|
1024
|
-
-
|
|
1025
|
-
-
|
|
1026
|
-
-
|
|
1027
|
-
-
|
|
1028
|
-
-
|
|
1029
|
-
-
|
|
1030
|
-
-
|
|
1031
|
-
-
|
|
1032
|
-
-
|
|
1033
|
-
-
|
|
1034
|
-
- 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.
|
|
1035
|
-
- When reading heap values, prefer generated generic typings such as \`await heap.getVar<Book[]>("my_books")\` or \`await heap.getVar<Book>("selected_book")\`.
|
|
1036
|
-
- 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")\`.
|
|
1037
|
-
- Use the injected \`loop\` helpers when you need to manage the workflow itself:
|
|
1038
|
-
\`loop.ask_user(...)\`, \`loop.confirm(...)\`, \`loop.open_decision(...)\`, \`loop.close_decision(...)\`,
|
|
1039
|
-
\`loop.create_task(...)\`, \`loop.update_task(...)\`, \`loop.complete_task(...)\`, and \`loop.close_loop(...)\`.
|
|
1040
|
-
- Loop helper semantics:
|
|
1041
|
-
- \`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\`.
|
|
1042
|
-
- \`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\`.
|
|
1043
|
-
- \`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.
|
|
1044
|
-
- \`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.
|
|
1045
|
-
- \`loop.create_task(...)\`, \`loop.update_task(...)\`, \`loop.complete_task(...)\`: keep a short task list that later jobs can continue and finish.
|
|
1046
|
-
- \`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.
|
|
1047
|
-
- Avoid \`as any\` and other broad casts when the DOMAIN TYPES block already tells you the correct class or list type.
|
|
1048
|
-
- Prefer manipulating heap-backed instances and typed lists instead of returning raw JSON blobs or object IDs unless the user explicitly asks for them.
|
|
1049
|
-
- 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(...)\`.
|
|
1050
|
-
- Use \`agent_text_message("...")\` for all user-visible text shown in the UI.
|
|
1051
|
-
- Use \`agent_heap_objects(...)\` only when you want the UI to render heap-backed records or lists.
|
|
1052
|
-
- If you want to show both text and records, call \`agent_text_message(...)\` and \`agent_heap_objects(...)\` separately in whatever order fits the interaction.
|
|
1053
|
-
- \`agent_text_message(...)\` should be used with a plain text string in normal generated code.
|
|
1054
|
-
- \`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(...)\`.
|
|
1055
|
-
- 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.
|
|
1056
|
-
- Do not assume heap changes will be displayed automatically. If records should appear in the UI, you must call \`agent_heap_objects(...)\`.
|
|
1057
|
-
- 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.
|
|
1058
|
-
- Do not rely on the final return value for user-visible output. A plain return value is not considered a displayed UI answer.
|
|
1059
|
-
- \`agent_message(...)\` remains available as a legacy compatibility alias, but prefer \`agent_text_message(...)\` and \`agent_heap_objects(...)\` in new code.
|
|
1060
|
-
- Do not return bare structured JSON, low-level diagnostics, or database-shaped payloads as the final answer unless the user explicitly asks for them.
|
|
1061
|
-
- 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.
|
|
1157
|
+
- The generated job body must be plain runnable JavaScript. Do not use TypeScript-only syntax.
|
|
1158
|
+
- Follow the exact classes, methods, and parameter shapes in DOMAIN REFERENCE. Do not invent helpers or unsupported arguments.
|
|
1159
|
+
- Use \`ClassName.get({ path })\` only for known graph paths when you want a direct graph fetch.
|
|
1160
|
+
- 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.
|
|
1161
|
+
- \`perPage\` defaults to \`100\` and is capped at \`100\`.
|
|
1162
|
+
- Push \`filter\`, \`search\`, and \`sort\` into graph queries instead of fetching a page and processing it locally.
|
|
1163
|
+
- 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.
|
|
1164
|
+
- 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.
|
|
1165
|
+
- If ordering alone answers the request, use \`sort\` without inventing a \`filter\`.
|
|
1166
|
+
- 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(...)\`.
|
|
1167
|
+
- 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.
|
|
1168
|
+
- Call instance methods on instances, static methods on classes, and global effects by name.
|
|
1169
|
+
- Use \`heap.getEntry(path)\` for remembered heap entries, \`heap.getList(name)\` for remembered lists, and \`heap.getVar(name)\` only for named variables.
|
|
1170
|
+
- Use \`heap.setVar(...)\` and \`heap.deleteVar(...)\` only when they help the next step.
|
|
1171
|
+
- Prefer \`heap.setVar(...)\` for scalars or one selected instance. Prefer \`ClassName.list({ saveAs })\` for reusable typed lists. Empty arrays are allowed.
|
|
1172
|
+
- 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.
|
|
1173
|
+
- Use the \`loop\` helpers to manage workflow state: \`ask_user\`, \`confirm\`, \`open_decision\`, \`close_decision\`, \`create_task\`, \`update_task\`, \`complete_task\`, and \`close_loop\`.
|
|
1174
|
+
- Use \`type: 'choice'\` only for short grounded options. Use \`type: 'input'\` when the answer should stay open-ended.
|
|
1175
|
+
- \`loop.confirm(...)\` is for consequential approval. Do not ask for approval in plain text.
|
|
1176
|
+
- After \`await loop.ask_user(...)\` or \`await loop.confirm(...)\`, continue in the same resumed job when the answer is enough to act.
|
|
1177
|
+
- Every job that answers the user must emit \`agent_text_message(...)\` and/or \`agent_heap_objects(...)\`.
|
|
1178
|
+
- Use \`agent_text_message(...)\` for user-visible text.
|
|
1179
|
+
- 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.
|
|
1180
|
+
- 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.
|
|
1181
|
+
- Keep the code small and direct. Avoid speculative branches, broad casts, and raw JSON dumps unless the user asked for them.
|
|
1062
1182
|
- Use \`console.log()\` only for intermediate diagnostics, not for the final user-facing answer.`;
|
|
1063
1183
|
}
|
|
1064
1184
|
|
|
@@ -1067,6 +1187,7 @@ exports.buildGranularAgentCheckpointBlock = buildGranularAgentCheckpointBlock;
|
|
|
1067
1187
|
exports.buildGranularAgentDomainBlock = buildGranularAgentDomainBlock;
|
|
1068
1188
|
exports.buildGranularAgentHeapBlock = buildGranularAgentHeapBlock;
|
|
1069
1189
|
exports.buildGranularAgentLoopBlock = buildGranularAgentLoopBlock;
|
|
1190
|
+
exports.buildGranularAgentReferentBlock = buildGranularAgentReferentBlock;
|
|
1070
1191
|
exports.buildGranularAgentSessionBlock = buildGranularAgentSessionBlock;
|
|
1071
1192
|
exports.buildGranularAgentSystemPrompt = buildGranularAgentSystemPrompt;
|
|
1072
1193
|
exports.buildGranularAgentToolBlock = buildGranularAgentToolBlock;
|
|
@@ -1076,6 +1197,8 @@ exports.evaluateContinuation = evaluateContinuation;
|
|
|
1076
1197
|
exports.getCurrentClosureId = getCurrentClosureId;
|
|
1077
1198
|
exports.getExclusivePromptTarget = getExclusivePromptTarget;
|
|
1078
1199
|
exports.hasOpenPrompt = hasOpenPrompt;
|
|
1200
|
+
exports.projectConversationReferentFocus = projectConversationReferentFocus;
|
|
1201
|
+
exports.projectConversationReferentSummary = projectConversationReferentSummary;
|
|
1079
1202
|
exports.projectHeapSummary = projectHeapSummary;
|
|
1080
1203
|
exports.projectLoopSummary = projectLoopSummary;
|
|
1081
1204
|
exports.projectWorkflowFocus = projectWorkflowFocus;
|