@granular-software/sdk 0.4.58 → 0.4.59

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.
@@ -111,7 +111,7 @@ interface BuildGranularAgentSystemPromptInput {
111
111
  manualActionSummary?: string;
112
112
  tools?: GranularAgentToolInfo[];
113
113
  checkpoint?: GranularAgentExecutionCheckpoint | null;
114
- outputMode?: "agentMessages" | "returnValue";
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" | "deprecated_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";
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?: "agentMessages" | "returnValue";
171
+ outputMode?: "feed" | "returnValue";
172
172
  request?: string;
173
173
  tools?: GranularAgentToolInfo[];
174
174
  reply?: string;
@@ -111,7 +111,7 @@ interface BuildGranularAgentSystemPromptInput {
111
111
  manualActionSummary?: string;
112
112
  tools?: GranularAgentToolInfo[];
113
113
  checkpoint?: GranularAgentExecutionCheckpoint | null;
114
- outputMode?: "agentMessages" | "returnValue";
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" | "deprecated_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";
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?: "agentMessages" | "returnValue";
171
+ outputMode?: "feed" | "returnValue";
172
172
  request?: string;
173
173
  tools?: GranularAgentToolInfo[];
174
174
  reply?: string;
@@ -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*\\{([\\s\\S]*?)\\}\\s*from\\s*['"]${escapedModule}['"]`,
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: "deprecated_runtime_helper",
660
+ code: "unsupported_runtime_helper",
609
661
  severity: "error",
610
- message: `Generated code uses legacy runtime helper \`${name}\`. Use Harness v3 helper \`${replacement}\` from the modules listed in [Runtime Imports].`
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
- const persistedReferents = asArray(conversation?.referents).map((value) => asRecord(value)).filter((value) => Boolean(value));
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
- const name = match[1];
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; do not use deprecated side-channel helpers."
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
- "agent_heap_objects",
2024
- "agent_message",
2025
- "agent_text_message"
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 || "agentMessages";
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; do not call deprecated side-channel 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\`.
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 agent message calls.` : `- Every job that answers the user must emit \`replyToUser(...)\` from \`@granular/agent\`.
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.