@granular-software/sdk 0.4.57 → 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.
@@ -525,7 +525,7 @@ function hasNamedModuleImport(source, moduleName, name) {
525
525
  const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
526
526
  const imports = source.matchAll(
527
527
  new RegExp(
528
- `import\\s*\\{([\\s\\S]*?)\\}\\s*from\\s*['"]${escapedModule}['"]`,
528
+ `import\\s*\\{([^}]*)\\}\\s*from\\s*['"]${escapedModule}['"]`,
529
529
  "g"
530
530
  )
531
531
  );
@@ -534,6 +534,22 @@ function hasNamedModuleImport(source, moduleName, name) {
534
534
  }
535
535
  return false;
536
536
  }
537
+ function namedModuleImports(source, moduleName) {
538
+ const escapedModule = moduleName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
539
+ const names = /* @__PURE__ */ new Set();
540
+ for (const match of source.matchAll(
541
+ new RegExp(
542
+ `import\\s*\\{([^}]*)\\}\\s*from\\s*['"]${escapedModule}['"]`,
543
+ "g"
544
+ )
545
+ )) {
546
+ for (const specifier of match[1].split(",")) {
547
+ const imported = specifier.trim().replace(/^type\s+/, "").split(/\s+as\s+/)[0]?.trim();
548
+ if (imported) names.add(imported);
549
+ }
550
+ }
551
+ return [...names];
552
+ }
537
553
  function hasNamedAgentImport(source, name) {
538
554
  return hasNamedModuleImport(source, HARNESS_V3_AGENT_MODULE, name);
539
555
  }
@@ -594,18 +610,54 @@ function reviewGeneratedJobCode(code, _options = {}) {
594
610
  message: "Generated jobs must not call process.exit(...). Return from the job or emit a runtime message instead."
595
611
  });
596
612
  }
613
+ const supportedAgentExports = /* @__PURE__ */ new Set([
614
+ "actions",
615
+ "artifacts",
616
+ "feedback",
617
+ "formatBlockers",
618
+ "relativeTime",
619
+ "replyToUser",
620
+ "showAgentResponse",
621
+ "showObjects",
622
+ "table",
623
+ "transientFeedback"
624
+ ]);
625
+ for (const imported of namedModuleImports(
626
+ normalized,
627
+ HARNESS_V3_AGENT_MODULE
628
+ )) {
629
+ if (!supportedAgentExports.has(imported)) {
630
+ issues.push({
631
+ code: "unknown_runtime_import",
632
+ severity: "error",
633
+ message: `Generated code imports unknown runtime value \`${imported}\` from ${HARNESS_V3_AGENT_MODULE}. Import only values listed in [Runtime Imports] and [Types].`
634
+ });
635
+ }
636
+ }
637
+ const removedAgentHelpers = [
638
+ ["agent", "text", "message"].join("_"),
639
+ ["agent", "heap", "objects"].join("_"),
640
+ ["agent", "message"].join("_")
641
+ ];
642
+ for (const name of removedAgentHelpers) {
643
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
644
+ if (new RegExp(`\\b${escaped}\\s*\\(`).test(normalized)) {
645
+ issues.push({
646
+ code: "unsupported_runtime_helper",
647
+ severity: "error",
648
+ message: `Generated code calls removed runtime helper \`${name}\`. Import and call only the canonical feed helpers listed for ${HARNESS_V3_AGENT_MODULE}.`
649
+ });
650
+ }
651
+ }
597
652
  for (const [name, replacement, pattern] of [
598
- ["agent_text_message", "replyToUser", /\bagent_text_message\s*\(/],
599
- ["agent_heap_objects", "showObjects", /\bagent_heap_objects\s*\(/],
600
- ["agent_message", "showAgentResponse", /\bagent_message\s*\(/],
601
653
  ["heap", "groundedObjects", /\bheap\./],
602
654
  ["loop", "userInteraction or work", /\bloop\./]
603
655
  ]) {
604
656
  if (pattern.test(normalized)) {
605
657
  issues.push({
606
- code: "deprecated_runtime_helper",
658
+ code: "unsupported_runtime_helper",
607
659
  severity: "error",
608
- message: `Generated code uses legacy runtime helper \`${name}\`. Use Harness v3 helper \`${replacement}\` from the modules listed in [Runtime Imports].`
660
+ message: `Generated code uses unsupported runtime helper \`${name}\`. Use \`${replacement}\` from the modules listed in [Runtime Imports].`
609
661
  });
610
662
  }
611
663
  }
@@ -774,116 +826,7 @@ function normalizeActionSummaryForPrompt(line) {
774
826
  }
775
827
  function collectConversationReferents(liveDoc) {
776
828
  const conversation = asRecord(liveDoc?.conversation);
777
- const persistedReferents = asArray(conversation?.referents).map((value) => asRecord(value)).filter((value) => Boolean(value));
778
- if (persistedReferents.length > 0) {
779
- return persistedReferents.slice().sort((left, right) => (right.ts || 0) - (left.ts || 0));
780
- }
781
- const heap = asRecord(liveDoc?.heap);
782
- const entriesByPath = asRecord(heap?.entriesByPath) || {};
783
- const listsByName = asRecord(heap?.listsByName) || {};
784
- const variablesByName = asRecord(heap?.variablesByName) || {};
785
- 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));
786
- const referents = [];
787
- const seen = /* @__PURE__ */ new Set();
788
- const pushReferent = (referent) => {
789
- if (!referent?.kind || !referent.ref) return;
790
- const key = `${referent.kind}:${referent.ref}`;
791
- if (seen.has(key)) return;
792
- seen.add(key);
793
- referents.push(referent);
794
- };
795
- for (const message of messages) {
796
- if (message.role !== "assistant") continue;
797
- const show = asRecord(message.show);
798
- if (!show) continue;
799
- const ts = Number(message.ts) || 0;
800
- const messageId = typeof message.id === "string" ? message.id : void 0;
801
- const jobId = typeof message.jobId === "string" ? message.jobId : void 0;
802
- const entryPaths = uniqueStrings(asArray(show.entryPaths));
803
- const entryClassCounts = /* @__PURE__ */ new Map();
804
- const entryMetadata = entryPaths.map((entryPath) => {
805
- const entry = asRecord(entriesByPath[entryPath]);
806
- const className = typeof entry?.className === "string" ? entry.className : void 0;
807
- if (className) {
808
- entryClassCounts.set(
809
- className,
810
- (entryClassCounts.get(className) || 0) + 1
811
- );
812
- }
813
- return { entryPath, entry, className };
814
- });
815
- const displayGroupId = entryMetadata.length > 1 ? `message:${messageId || jobId || ts}:entries` : void 0;
816
- for (const [
817
- index,
818
- { entryPath, entry, className }
819
- ] of entryMetadata.entries()) {
820
- pushReferent({
821
- id: `entry:${entryPath}`,
822
- kind: "entry",
823
- ref: entryPath,
824
- role: "assistant",
825
- source: "heap_objects",
826
- entryPath,
827
- recordId: typeof entry?.id === "string" ? entry.id : void 0,
828
- className,
829
- label: typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim() : typeof entry?.id === "string" && entry.id.trim() ? entry.id.trim() : entryPath,
830
- ...displayGroupId ? {
831
- displayGroupId,
832
- displayGroupIndex: index,
833
- displayGroupSize: entryMetadata.length,
834
- ...className && (entryClassCounts.get(className) || 0) > 1 ? { displayGroupSameTypeSize: entryClassCounts.get(className) } : {}
835
- } : {},
836
- messageId,
837
- jobId,
838
- ts
839
- });
840
- }
841
- for (const listName of uniqueStrings(asArray(show.listNames))) {
842
- const list = asRecord(listsByName[listName]);
843
- pushReferent({
844
- id: `list:${listName}`,
845
- kind: "list",
846
- ref: listName,
847
- role: "assistant",
848
- source: "heap_objects",
849
- listName,
850
- className: typeof list?.className === "string" ? list.className : void 0,
851
- count: Array.isArray(list?.paths) ? list.paths.length : null,
852
- messageId,
853
- jobId,
854
- ts
855
- });
856
- }
857
- for (const variableName of uniqueStrings(
858
- asArray(show.variableNames)
859
- )) {
860
- const variable = asRecord(variablesByName[variableName]);
861
- const entryPath = typeof variable?.entryPath === "string" ? variable.entryPath : void 0;
862
- const listName = typeof variable?.listName === "string" ? variable.listName : void 0;
863
- const entry = entryPath ? asRecord(entriesByPath[entryPath]) : null;
864
- const list = listName ? asRecord(listsByName[listName]) : null;
865
- pushReferent({
866
- id: `variable:${variableName}`,
867
- kind: "variable",
868
- ref: variableName,
869
- role: "assistant",
870
- source: "heap_objects",
871
- variableName,
872
- variableKind: typeof variable?.kind === "string" ? variable.kind : void 0,
873
- entryPath,
874
- recordId: typeof entry?.id === "string" ? entry.id : void 0,
875
- listName,
876
- className: typeof variable?.className === "string" ? variable.className : typeof entry?.className === "string" ? entry.className : typeof list?.className === "string" ? list.className : void 0,
877
- label: typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim() : typeof entry?.id === "string" && entry.id.trim() ? entry.id.trim() : null,
878
- count: variable?.kind === "list" && Array.isArray(list?.paths) ? list.paths.length : null,
879
- scalarValue: variable?.kind === "scalar" && (typeof variable.value === "string" || typeof variable.value === "number" || typeof variable.value === "boolean" || variable.value === null) ? variable.value : void 0,
880
- messageId,
881
- jobId,
882
- ts
883
- });
884
- }
885
- }
886
- return referents;
829
+ return asArray(conversation?.referents).map((value) => asRecord(value)).filter((value) => Boolean(value)).slice().sort((left, right) => (right.ts || 0) - (left.ts || 0));
887
830
  }
888
831
  function projectConversationReferentFocus(liveDoc) {
889
832
  const heap = asRecord(liveDoc?.heap);
@@ -1677,13 +1620,7 @@ function extractRuntimeContractExports(domainBlock) {
1677
1620
  }
1678
1621
  const actionPattern = /export\s+declare\s+function\s+([A-Za-z_$][\w$]*)/g;
1679
1622
  for (const match of domainBlock.matchAll(actionPattern)) {
1680
- const name = match[1];
1681
- if (["agent_text_message", "agent_heap_objects", "agent_message"].includes(
1682
- name
1683
- )) {
1684
- continue;
1685
- }
1686
- actions.add(name);
1623
+ actions.add(match[1]);
1687
1624
  }
1688
1625
  return {
1689
1626
  classes: Array.from(classes).sort(),
@@ -1740,7 +1677,7 @@ function buildGranularAgentRuntimeImportsBlock(input) {
1740
1677
  importStyle: "named ESM imports only",
1741
1678
  exports: ["replyToUser", "showObjects", "showAgentResponse"],
1742
1679
  contains: "User-facing Harness response helpers for text, grounded object displays, and combined responses.",
1743
- rule: "Import reply/display helpers from this module; do not use deprecated side-channel helpers."
1680
+ rule: "Import reply/display helpers from this module; only the listed exports are available."
1744
1681
  },
1745
1682
  [HARNESS_V3_SESSION_MODULE]: {
1746
1683
  importStyle: "named ESM imports only",
@@ -2018,9 +1955,9 @@ function splitDomainDocumentation(domainDocumentation) {
2018
1955
  return { types: normalized, docs: "" };
2019
1956
  }
2020
1957
  var DOMAIN_HELPER_FUNCTION_NAMES = /* @__PURE__ */ new Set([
2021
- "agent_heap_objects",
2022
- "agent_message",
2023
- "agent_text_message"
1958
+ "replyToUser",
1959
+ "showAgentResponse",
1960
+ "showObjects"
2024
1961
  ]);
2025
1962
  function inferGlobalActionToolsFromDomainTypes(domainTypes) {
2026
1963
  const inferred = [];
@@ -2156,7 +2093,7 @@ function buildKnownFactsFromCheckpoint(checkpoint) {
2156
2093
  return facts.slice(0, 8);
2157
2094
  }
2158
2095
  function buildGranularAgentSystemPrompt(input) {
2159
- const outputMode = input.outputMode || "agentMessages";
2096
+ const outputMode = input.outputMode || "feed";
2160
2097
  const promptCapabilities = resolvePromptCapabilities(input.capabilities);
2161
2098
  const domainSections = splitDomainDocumentation(input.domainDocumentation);
2162
2099
  const promptTools = resolvePromptTools(input.tools, domainSections.types);
@@ -2189,7 +2126,7 @@ function buildGranularAgentSystemPrompt(input) {
2189
2126
  - For multi-record display, prefer a saved list/listName so the UI can render a table; use entryPaths for a few individual records.
2190
2127
  - 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.
2191
2128
  - 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.
2192
- - 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\`.
2129
+ - 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\`.
2193
2130
  - \`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.
2194
2131
  - 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.
2195
2132
  - 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.
@@ -2199,11 +2136,10 @@ function buildGranularAgentSystemPrompt(input) {
2199
2136
  - 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.
2200
2137
  - 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()\`.
2201
2138
  - 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.
2202
- - 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.
2203
2139
  - 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.
2204
2140
  - 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.
2205
2141
  - 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.
2206
- - 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\`.
2142
+ - 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\`.
2207
2143
  - \`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.
2208
2144
  - 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.
2209
2145
  - 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.