@granular-software/sdk 0.4.58 → 0.4.60
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 +38 -4
- package/dist/agent-evals.d.mts +2 -3
- package/dist/agent-evals.d.ts +2 -3
- package/dist/agent-evals.js +2910 -1329
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +2910 -1329
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/agent-harness.d.mts +3 -3
- package/dist/agent-harness.d.ts +3 -3
- package/dist/agent-harness.js +67 -131
- package/dist/agent-harness.js.map +1 -1
- package/dist/agent-harness.mjs +67 -131
- package/dist/agent-harness.mjs.map +1 -1
- package/dist/cli/index.js +2394 -1009
- package/dist/{client-DFaCRSto.d.mts → client-BzgQvNyG.d.ts} +90 -17
- package/dist/{client-UdGt44dF.d.ts → client-DSkGcmmX.d.mts} +90 -17
- package/dist/index.d.mts +33 -11
- package/dist/index.d.ts +33 -11
- package/dist/index.js +2840 -1252
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2826 -1253
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-czRX7l0X.d.mts → spend-CNe-Ff6U.d.mts} +670 -98
- package/dist/{spend-czRX7l0X.d.ts → spend-CNe-Ff6U.d.ts} +670 -98
- package/dist/spend.d.mts +1 -1
- package/dist/spend.d.ts +1 -1
- package/package.json +5 -2
package/dist/agent-harness.d.mts
CHANGED
|
@@ -111,7 +111,7 @@ interface BuildGranularAgentSystemPromptInput {
|
|
|
111
111
|
manualActionSummary?: string;
|
|
112
112
|
tools?: GranularAgentToolInfo[];
|
|
113
113
|
checkpoint?: GranularAgentExecutionCheckpoint | null;
|
|
114
|
-
outputMode?: "
|
|
114
|
+
outputMode?: "feed" | "returnValue";
|
|
115
115
|
}
|
|
116
116
|
type HarnessTemplateStatus = "draft" | "candidate" | "release-candidate" | "stable" | "deprecated";
|
|
117
117
|
interface HarnessTemplateManifest {
|
|
@@ -163,12 +163,12 @@ interface HarnessTemplateSelectionOptions {
|
|
|
163
163
|
strict?: boolean;
|
|
164
164
|
}
|
|
165
165
|
interface GeneratedJobCodeIssue {
|
|
166
|
-
code: "commonjs_require" | "process_exit" | "dynamic_import_in_job" | "syntax_error_in_job" | "nested_template_literal_in_job" | "undefined_template_identifier" | "object_spread_in_job" | "deprecated_runtime_import" | "
|
|
166
|
+
code: "commonjs_require" | "process_exit" | "dynamic_import_in_job" | "syntax_error_in_job" | "nested_template_literal_in_job" | "undefined_template_identifier" | "object_spread_in_job" | "deprecated_runtime_import" | "unknown_runtime_import" | "unsupported_runtime_helper" | "runtime_namespace_import" | "runtime_import_contract" | "runtime_api_contract" | "missing_runtime_import" | "missing_loop_import" | "bare_loop_helper_import" | "loop_helper_contract" | "stdout_json_reply" | "return_chat_payload";
|
|
167
167
|
severity: "error";
|
|
168
168
|
message: string;
|
|
169
169
|
}
|
|
170
170
|
interface ReviewGeneratedJobCodeOptions {
|
|
171
|
-
outputMode?: "
|
|
171
|
+
outputMode?: "feed" | "returnValue";
|
|
172
172
|
request?: string;
|
|
173
173
|
tools?: GranularAgentToolInfo[];
|
|
174
174
|
reply?: string;
|
package/dist/agent-harness.d.ts
CHANGED
|
@@ -111,7 +111,7 @@ interface BuildGranularAgentSystemPromptInput {
|
|
|
111
111
|
manualActionSummary?: string;
|
|
112
112
|
tools?: GranularAgentToolInfo[];
|
|
113
113
|
checkpoint?: GranularAgentExecutionCheckpoint | null;
|
|
114
|
-
outputMode?: "
|
|
114
|
+
outputMode?: "feed" | "returnValue";
|
|
115
115
|
}
|
|
116
116
|
type HarnessTemplateStatus = "draft" | "candidate" | "release-candidate" | "stable" | "deprecated";
|
|
117
117
|
interface HarnessTemplateManifest {
|
|
@@ -163,12 +163,12 @@ interface HarnessTemplateSelectionOptions {
|
|
|
163
163
|
strict?: boolean;
|
|
164
164
|
}
|
|
165
165
|
interface GeneratedJobCodeIssue {
|
|
166
|
-
code: "commonjs_require" | "process_exit" | "dynamic_import_in_job" | "syntax_error_in_job" | "nested_template_literal_in_job" | "undefined_template_identifier" | "object_spread_in_job" | "deprecated_runtime_import" | "
|
|
166
|
+
code: "commonjs_require" | "process_exit" | "dynamic_import_in_job" | "syntax_error_in_job" | "nested_template_literal_in_job" | "undefined_template_identifier" | "object_spread_in_job" | "deprecated_runtime_import" | "unknown_runtime_import" | "unsupported_runtime_helper" | "runtime_namespace_import" | "runtime_import_contract" | "runtime_api_contract" | "missing_runtime_import" | "missing_loop_import" | "bare_loop_helper_import" | "loop_helper_contract" | "stdout_json_reply" | "return_chat_payload";
|
|
167
167
|
severity: "error";
|
|
168
168
|
message: string;
|
|
169
169
|
}
|
|
170
170
|
interface ReviewGeneratedJobCodeOptions {
|
|
171
|
-
outputMode?: "
|
|
171
|
+
outputMode?: "feed" | "returnValue";
|
|
172
172
|
request?: string;
|
|
173
173
|
tools?: GranularAgentToolInfo[];
|
|
174
174
|
reply?: string;
|
package/dist/agent-harness.js
CHANGED
|
@@ -527,7 +527,7 @@ function hasNamedModuleImport(source, moduleName, name) {
|
|
|
527
527
|
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
528
528
|
const imports = source.matchAll(
|
|
529
529
|
new RegExp(
|
|
530
|
-
`import\\s*\\{([
|
|
530
|
+
`import\\s*\\{([^}]*)\\}\\s*from\\s*['"]${escapedModule}['"]`,
|
|
531
531
|
"g"
|
|
532
532
|
)
|
|
533
533
|
);
|
|
@@ -536,6 +536,22 @@ function hasNamedModuleImport(source, moduleName, name) {
|
|
|
536
536
|
}
|
|
537
537
|
return false;
|
|
538
538
|
}
|
|
539
|
+
function namedModuleImports(source, moduleName) {
|
|
540
|
+
const escapedModule = moduleName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
541
|
+
const names = /* @__PURE__ */ new Set();
|
|
542
|
+
for (const match of source.matchAll(
|
|
543
|
+
new RegExp(
|
|
544
|
+
`import\\s*\\{([^}]*)\\}\\s*from\\s*['"]${escapedModule}['"]`,
|
|
545
|
+
"g"
|
|
546
|
+
)
|
|
547
|
+
)) {
|
|
548
|
+
for (const specifier of match[1].split(",")) {
|
|
549
|
+
const imported = specifier.trim().replace(/^type\s+/, "").split(/\s+as\s+/)[0]?.trim();
|
|
550
|
+
if (imported) names.add(imported);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return [...names];
|
|
554
|
+
}
|
|
539
555
|
function hasNamedAgentImport(source, name) {
|
|
540
556
|
return hasNamedModuleImport(source, HARNESS_V3_AGENT_MODULE, name);
|
|
541
557
|
}
|
|
@@ -596,18 +612,54 @@ function reviewGeneratedJobCode(code, _options = {}) {
|
|
|
596
612
|
message: "Generated jobs must not call process.exit(...). Return from the job or emit a runtime message instead."
|
|
597
613
|
});
|
|
598
614
|
}
|
|
615
|
+
const supportedAgentExports = /* @__PURE__ */ new Set([
|
|
616
|
+
"actions",
|
|
617
|
+
"artifacts",
|
|
618
|
+
"feedback",
|
|
619
|
+
"formatBlockers",
|
|
620
|
+
"relativeTime",
|
|
621
|
+
"replyToUser",
|
|
622
|
+
"showAgentResponse",
|
|
623
|
+
"showObjects",
|
|
624
|
+
"table",
|
|
625
|
+
"transientFeedback"
|
|
626
|
+
]);
|
|
627
|
+
for (const imported of namedModuleImports(
|
|
628
|
+
normalized,
|
|
629
|
+
HARNESS_V3_AGENT_MODULE
|
|
630
|
+
)) {
|
|
631
|
+
if (!supportedAgentExports.has(imported)) {
|
|
632
|
+
issues.push({
|
|
633
|
+
code: "unknown_runtime_import",
|
|
634
|
+
severity: "error",
|
|
635
|
+
message: `Generated code imports unknown runtime value \`${imported}\` from ${HARNESS_V3_AGENT_MODULE}. Import only values listed in [Runtime Imports] and [Types].`
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const removedAgentHelpers = [
|
|
640
|
+
["agent", "text", "message"].join("_"),
|
|
641
|
+
["agent", "heap", "objects"].join("_"),
|
|
642
|
+
["agent", "message"].join("_")
|
|
643
|
+
];
|
|
644
|
+
for (const name of removedAgentHelpers) {
|
|
645
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
646
|
+
if (new RegExp(`\\b${escaped}\\s*\\(`).test(normalized)) {
|
|
647
|
+
issues.push({
|
|
648
|
+
code: "unsupported_runtime_helper",
|
|
649
|
+
severity: "error",
|
|
650
|
+
message: `Generated code calls removed runtime helper \`${name}\`. Import and call only the canonical feed helpers listed for ${HARNESS_V3_AGENT_MODULE}.`
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
599
654
|
for (const [name, replacement, pattern] of [
|
|
600
|
-
["agent_text_message", "replyToUser", /\bagent_text_message\s*\(/],
|
|
601
|
-
["agent_heap_objects", "showObjects", /\bagent_heap_objects\s*\(/],
|
|
602
|
-
["agent_message", "showAgentResponse", /\bagent_message\s*\(/],
|
|
603
655
|
["heap", "groundedObjects", /\bheap\./],
|
|
604
656
|
["loop", "userInteraction or work", /\bloop\./]
|
|
605
657
|
]) {
|
|
606
658
|
if (pattern.test(normalized)) {
|
|
607
659
|
issues.push({
|
|
608
|
-
code: "
|
|
660
|
+
code: "unsupported_runtime_helper",
|
|
609
661
|
severity: "error",
|
|
610
|
-
message: `Generated code uses
|
|
662
|
+
message: `Generated code uses unsupported runtime helper \`${name}\`. Use \`${replacement}\` from the modules listed in [Runtime Imports].`
|
|
611
663
|
});
|
|
612
664
|
}
|
|
613
665
|
}
|
|
@@ -776,116 +828,7 @@ function normalizeActionSummaryForPrompt(line) {
|
|
|
776
828
|
}
|
|
777
829
|
function collectConversationReferents(liveDoc) {
|
|
778
830
|
const conversation = asRecord(liveDoc?.conversation);
|
|
779
|
-
|
|
780
|
-
if (persistedReferents.length > 0) {
|
|
781
|
-
return persistedReferents.slice().sort((left, right) => (right.ts || 0) - (left.ts || 0));
|
|
782
|
-
}
|
|
783
|
-
const heap = asRecord(liveDoc?.heap);
|
|
784
|
-
const entriesByPath = asRecord(heap?.entriesByPath) || {};
|
|
785
|
-
const listsByName = asRecord(heap?.listsByName) || {};
|
|
786
|
-
const variablesByName = asRecord(heap?.variablesByName) || {};
|
|
787
|
-
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));
|
|
788
|
-
const referents = [];
|
|
789
|
-
const seen = /* @__PURE__ */ new Set();
|
|
790
|
-
const pushReferent = (referent) => {
|
|
791
|
-
if (!referent?.kind || !referent.ref) return;
|
|
792
|
-
const key = `${referent.kind}:${referent.ref}`;
|
|
793
|
-
if (seen.has(key)) return;
|
|
794
|
-
seen.add(key);
|
|
795
|
-
referents.push(referent);
|
|
796
|
-
};
|
|
797
|
-
for (const message of messages) {
|
|
798
|
-
if (message.role !== "assistant") continue;
|
|
799
|
-
const show = asRecord(message.show);
|
|
800
|
-
if (!show) continue;
|
|
801
|
-
const ts = Number(message.ts) || 0;
|
|
802
|
-
const messageId = typeof message.id === "string" ? message.id : void 0;
|
|
803
|
-
const jobId = typeof message.jobId === "string" ? message.jobId : void 0;
|
|
804
|
-
const entryPaths = uniqueStrings(asArray(show.entryPaths));
|
|
805
|
-
const entryClassCounts = /* @__PURE__ */ new Map();
|
|
806
|
-
const entryMetadata = entryPaths.map((entryPath) => {
|
|
807
|
-
const entry = asRecord(entriesByPath[entryPath]);
|
|
808
|
-
const className = typeof entry?.className === "string" ? entry.className : void 0;
|
|
809
|
-
if (className) {
|
|
810
|
-
entryClassCounts.set(
|
|
811
|
-
className,
|
|
812
|
-
(entryClassCounts.get(className) || 0) + 1
|
|
813
|
-
);
|
|
814
|
-
}
|
|
815
|
-
return { entryPath, entry, className };
|
|
816
|
-
});
|
|
817
|
-
const displayGroupId = entryMetadata.length > 1 ? `message:${messageId || jobId || ts}:entries` : void 0;
|
|
818
|
-
for (const [
|
|
819
|
-
index,
|
|
820
|
-
{ entryPath, entry, className }
|
|
821
|
-
] of entryMetadata.entries()) {
|
|
822
|
-
pushReferent({
|
|
823
|
-
id: `entry:${entryPath}`,
|
|
824
|
-
kind: "entry",
|
|
825
|
-
ref: entryPath,
|
|
826
|
-
role: "assistant",
|
|
827
|
-
source: "heap_objects",
|
|
828
|
-
entryPath,
|
|
829
|
-
recordId: typeof entry?.id === "string" ? entry.id : void 0,
|
|
830
|
-
className,
|
|
831
|
-
label: typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim() : typeof entry?.id === "string" && entry.id.trim() ? entry.id.trim() : entryPath,
|
|
832
|
-
...displayGroupId ? {
|
|
833
|
-
displayGroupId,
|
|
834
|
-
displayGroupIndex: index,
|
|
835
|
-
displayGroupSize: entryMetadata.length,
|
|
836
|
-
...className && (entryClassCounts.get(className) || 0) > 1 ? { displayGroupSameTypeSize: entryClassCounts.get(className) } : {}
|
|
837
|
-
} : {},
|
|
838
|
-
messageId,
|
|
839
|
-
jobId,
|
|
840
|
-
ts
|
|
841
|
-
});
|
|
842
|
-
}
|
|
843
|
-
for (const listName of uniqueStrings(asArray(show.listNames))) {
|
|
844
|
-
const list = asRecord(listsByName[listName]);
|
|
845
|
-
pushReferent({
|
|
846
|
-
id: `list:${listName}`,
|
|
847
|
-
kind: "list",
|
|
848
|
-
ref: listName,
|
|
849
|
-
role: "assistant",
|
|
850
|
-
source: "heap_objects",
|
|
851
|
-
listName,
|
|
852
|
-
className: typeof list?.className === "string" ? list.className : void 0,
|
|
853
|
-
count: Array.isArray(list?.paths) ? list.paths.length : null,
|
|
854
|
-
messageId,
|
|
855
|
-
jobId,
|
|
856
|
-
ts
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
for (const variableName of uniqueStrings(
|
|
860
|
-
asArray(show.variableNames)
|
|
861
|
-
)) {
|
|
862
|
-
const variable = asRecord(variablesByName[variableName]);
|
|
863
|
-
const entryPath = typeof variable?.entryPath === "string" ? variable.entryPath : void 0;
|
|
864
|
-
const listName = typeof variable?.listName === "string" ? variable.listName : void 0;
|
|
865
|
-
const entry = entryPath ? asRecord(entriesByPath[entryPath]) : null;
|
|
866
|
-
const list = listName ? asRecord(listsByName[listName]) : null;
|
|
867
|
-
pushReferent({
|
|
868
|
-
id: `variable:${variableName}`,
|
|
869
|
-
kind: "variable",
|
|
870
|
-
ref: variableName,
|
|
871
|
-
role: "assistant",
|
|
872
|
-
source: "heap_objects",
|
|
873
|
-
variableName,
|
|
874
|
-
variableKind: typeof variable?.kind === "string" ? variable.kind : void 0,
|
|
875
|
-
entryPath,
|
|
876
|
-
recordId: typeof entry?.id === "string" ? entry.id : void 0,
|
|
877
|
-
listName,
|
|
878
|
-
className: typeof variable?.className === "string" ? variable.className : typeof entry?.className === "string" ? entry.className : typeof list?.className === "string" ? list.className : void 0,
|
|
879
|
-
label: typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim() : typeof entry?.id === "string" && entry.id.trim() ? entry.id.trim() : null,
|
|
880
|
-
count: variable?.kind === "list" && Array.isArray(list?.paths) ? list.paths.length : null,
|
|
881
|
-
scalarValue: variable?.kind === "scalar" && (typeof variable.value === "string" || typeof variable.value === "number" || typeof variable.value === "boolean" || variable.value === null) ? variable.value : void 0,
|
|
882
|
-
messageId,
|
|
883
|
-
jobId,
|
|
884
|
-
ts
|
|
885
|
-
});
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
return referents;
|
|
831
|
+
return asArray(conversation?.referents).map((value) => asRecord(value)).filter((value) => Boolean(value)).slice().sort((left, right) => (right.ts || 0) - (left.ts || 0));
|
|
889
832
|
}
|
|
890
833
|
function projectConversationReferentFocus(liveDoc) {
|
|
891
834
|
const heap = asRecord(liveDoc?.heap);
|
|
@@ -1679,13 +1622,7 @@ function extractRuntimeContractExports(domainBlock) {
|
|
|
1679
1622
|
}
|
|
1680
1623
|
const actionPattern = /export\s+declare\s+function\s+([A-Za-z_$][\w$]*)/g;
|
|
1681
1624
|
for (const match of domainBlock.matchAll(actionPattern)) {
|
|
1682
|
-
|
|
1683
|
-
if (["agent_text_message", "agent_heap_objects", "agent_message"].includes(
|
|
1684
|
-
name
|
|
1685
|
-
)) {
|
|
1686
|
-
continue;
|
|
1687
|
-
}
|
|
1688
|
-
actions.add(name);
|
|
1625
|
+
actions.add(match[1]);
|
|
1689
1626
|
}
|
|
1690
1627
|
return {
|
|
1691
1628
|
classes: Array.from(classes).sort(),
|
|
@@ -1742,7 +1679,7 @@ function buildGranularAgentRuntimeImportsBlock(input) {
|
|
|
1742
1679
|
importStyle: "named ESM imports only",
|
|
1743
1680
|
exports: ["replyToUser", "showObjects", "showAgentResponse"],
|
|
1744
1681
|
contains: "User-facing Harness response helpers for text, grounded object displays, and combined responses.",
|
|
1745
|
-
rule: "Import reply/display helpers from this module;
|
|
1682
|
+
rule: "Import reply/display helpers from this module; only the listed exports are available."
|
|
1746
1683
|
},
|
|
1747
1684
|
[HARNESS_V3_SESSION_MODULE]: {
|
|
1748
1685
|
importStyle: "named ESM imports only",
|
|
@@ -2020,9 +1957,9 @@ function splitDomainDocumentation(domainDocumentation) {
|
|
|
2020
1957
|
return { types: normalized, docs: "" };
|
|
2021
1958
|
}
|
|
2022
1959
|
var DOMAIN_HELPER_FUNCTION_NAMES = /* @__PURE__ */ new Set([
|
|
2023
|
-
"
|
|
2024
|
-
"
|
|
2025
|
-
"
|
|
1960
|
+
"replyToUser",
|
|
1961
|
+
"showAgentResponse",
|
|
1962
|
+
"showObjects"
|
|
2026
1963
|
]);
|
|
2027
1964
|
function inferGlobalActionToolsFromDomainTypes(domainTypes) {
|
|
2028
1965
|
const inferred = [];
|
|
@@ -2158,7 +2095,7 @@ function buildKnownFactsFromCheckpoint(checkpoint) {
|
|
|
2158
2095
|
return facts.slice(0, 8);
|
|
2159
2096
|
}
|
|
2160
2097
|
function buildGranularAgentSystemPrompt(input) {
|
|
2161
|
-
const outputMode = input.outputMode || "
|
|
2098
|
+
const outputMode = input.outputMode || "feed";
|
|
2162
2099
|
const promptCapabilities = resolvePromptCapabilities(input.capabilities);
|
|
2163
2100
|
const domainSections = splitDomainDocumentation(input.domainDocumentation);
|
|
2164
2101
|
const promptTools = resolvePromptTools(input.tools, domainSections.types);
|
|
@@ -2191,7 +2128,7 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
2191
2128
|
- For multi-record display, prefer a saved list/listName so the UI can render a table; use entryPaths for a few individual records.
|
|
2192
2129
|
- When the user asks to show, list, display, open, or "show them" for records you found, include those grounded records in \`show\`; do not answer only with a count or text summary.
|
|
2193
2130
|
- For count-only questions such as "how many", "how many X do I have", or "what is the total number of X", call the entity \`.count(...)\` or use page \`totalCount\` only when a page is already needed for other reasons. Answer with text only. Do not fetch, save, or display sample records just to ground a numeric count.
|
|
2194
|
-
- Use \`replyToUser(...)\`, \`showObjects(...)\`, or \`showAgentResponse(...)\` from \`@granular/agent\` when the host exposes job output helpers
|
|
2131
|
+
- Use \`replyToUser(...)\`, \`showObjects(...)\`, or \`showAgentResponse(...)\` from \`@granular/agent\` when the host exposes job output helpers.` : `- End every user-facing job by returning a short natural-language string.` : promptCapabilities.showRecords ? `- Every job that answers the user must emit \`replyToUser(...)\`, \`showObjects(...)\`, and/or \`showAgentResponse(...)\` from \`@granular/agent\`.
|
|
2195
2132
|
- \`replyToUser(...)\` displays text directly to the user in the host UI. Treat it as the user-facing progress and reply channel, not as a debug log.
|
|
2196
2133
|
- For long-running or multi-step jobs, send several short \`replyToUser(...)\` updates as useful milestones are reached so the user can see what is happening instead of waiting in silence.
|
|
2197
2134
|
- Write \`replyToUser(...)\` content in a friendly, readable product-assistant style: concrete, concise, and natural. Avoid robotic status dumps, raw implementation names, and unexplained IDs unless the ID helps the user.
|
|
@@ -2201,11 +2138,10 @@ function buildGranularAgentSystemPrompt(input) {
|
|
|
2201
2138
|
- Do not use \`showObjects({ entries: [...] })\` or \`showObjects({ saveAs, entries })\`. Save ordered pages, queues, search results, or ranked lists with \`groundedObjects.save(...)\`, then call \`showObjects({ variableNames: [...] })\` once.
|
|
2202
2139
|
- Use \`showAgentResponse({ reply, show: [record, action] })\` when one assistant message should combine text, grounded records, files, prepared actions, or action suggestions. The \`action\` can be a state handle such as \`record.lifecycle.approved\` or an action handle such as \`record.lifecycle.approved.reach()\`.
|
|
2203
2140
|
- Pass grounded records directly in \`show\` when the default record presentation answers the request. When the user asks for particular columns, comparisons, or computed values, import \`table\` (and \`relativeTime\` when useful) from \`@granular/agent\` and call \`showAgentResponse({ reply, show: table(records, [{ label: "Object", value: record => record.label }, { label: "When", value: record => relativeTime(record.timestamp) }]) })\`. Column callbacks must be synchronous and return a scalar, \`Date\`, or \`relativeTime(...)\`; they run inside the job and only resolved cells are persisted.
|
|
2204
|
-
- Do not use deprecated side-channel helpers such as \`agent_text_message(...)\`, \`agent_heap_objects(...)\`, or \`agent_message(...)\` unless the generated types expose no Harness v3 helper alternative.
|
|
2205
2141
|
- When the user asks to show, list, display, open, or "show them" for records you found, call \`showObjects(...)\`; do not answer only with a count or text summary.
|
|
2206
2142
|
- For count-only questions such as "how many", "how many X do I have", or "what is the total number of X", call the entity \`.count(...)\` or use page \`totalCount\` only when a page is already needed for other reasons. Answer with \`replyToUser(...)\` only. Do not call \`showObjects(...)\`, \`saveAs\`, or \`groundedObjects.save(...)\` unless the user also asked to see records or a later requested action needs a reusable record selection.
|
|
2207
2143
|
- Use stable saved list names that preserve identity and ordering so later references such as "the second item" or "back on the first slice" resolve to the correct earlier slice, not merely the most recent record.
|
|
2208
|
-
- Do not rely on the final return value for UI output. Do not return ad-hoc \`reply\` / \`show\` payloads instead of explicit
|
|
2144
|
+
- Do not rely on the final return value for UI output. Do not return ad-hoc \`reply\` / \`show\` payloads instead of explicit canonical feed calls.` : `- Every job that answers the user must emit \`replyToUser(...)\` from \`@granular/agent\`.
|
|
2209
2145
|
- \`replyToUser(...)\` displays text directly to the user in the host UI. Treat it as the user-facing progress and reply channel, not as a debug log.
|
|
2210
2146
|
- For long-running or multi-step jobs, send several short \`replyToUser(...)\` updates as useful milestones are reached so the user can see what is happening instead of waiting in silence.
|
|
2211
2147
|
- Write \`replyToUser(...)\` content in a friendly, readable product-assistant style: concrete, concise, and natural. Avoid robotic status dumps, raw implementation names, and unexplained IDs unless the ID helps the user.
|