@salesforce/graphiti 10.10.2

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.
Files changed (282) hide show
  1. package/AGENT_GUIDE.md +424 -0
  2. package/CHANGELOG.md +448 -0
  3. package/LICENSE.txt +82 -0
  4. package/README.md +204 -0
  5. package/TASK.md +249 -0
  6. package/dist/cli.d.ts +7 -0
  7. package/dist/cli.js +683 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/args.d.ts +13 -0
  10. package/dist/commands/args.js +207 -0
  11. package/dist/commands/args.js.map +1 -0
  12. package/dist/commands/build.d.ts +11 -0
  13. package/dist/commands/build.js +209 -0
  14. package/dist/commands/build.js.map +1 -0
  15. package/dist/commands/connect.d.ts +8 -0
  16. package/dist/commands/connect.js +55 -0
  17. package/dist/commands/connect.js.map +1 -0
  18. package/dist/commands/describe.d.ts +6 -0
  19. package/dist/commands/describe.js +229 -0
  20. package/dist/commands/describe.js.map +1 -0
  21. package/dist/commands/meta.d.ts +9 -0
  22. package/dist/commands/meta.js +140 -0
  23. package/dist/commands/meta.js.map +1 -0
  24. package/dist/commands/navigate.d.ts +14 -0
  25. package/dist/commands/navigate.js +105 -0
  26. package/dist/commands/navigate.js.map +1 -0
  27. package/dist/commands/query-helpers.d.ts +80 -0
  28. package/dist/commands/query-helpers.js +865 -0
  29. package/dist/commands/query-helpers.js.map +1 -0
  30. package/dist/commands/query.d.ts +26 -0
  31. package/dist/commands/query.js +901 -0
  32. package/dist/commands/query.js.map +1 -0
  33. package/dist/commands/review.d.ts +18 -0
  34. package/dist/commands/review.js +533 -0
  35. package/dist/commands/review.js.map +1 -0
  36. package/dist/commands/session-mgmt.d.ts +25 -0
  37. package/dist/commands/session-mgmt.js +206 -0
  38. package/dist/commands/session-mgmt.js.map +1 -0
  39. package/dist/commands/type.d.ts +6 -0
  40. package/dist/commands/type.js +342 -0
  41. package/dist/commands/type.js.map +1 -0
  42. package/dist/commands/validate-input.d.ts +6 -0
  43. package/dist/commands/validate-input.js +32 -0
  44. package/dist/commands/validate-input.js.map +1 -0
  45. package/dist/intent/build-aggregate.d.ts +33 -0
  46. package/dist/intent/build-aggregate.js +134 -0
  47. package/dist/intent/build-aggregate.js.map +1 -0
  48. package/dist/intent/build-create.d.ts +14 -0
  49. package/dist/intent/build-create.js +16 -0
  50. package/dist/intent/build-create.js.map +1 -0
  51. package/dist/intent/build-delete.d.ts +30 -0
  52. package/dist/intent/build-delete.js +53 -0
  53. package/dist/intent/build-delete.js.map +1 -0
  54. package/dist/intent/build-detail.d.ts +32 -0
  55. package/dist/intent/build-detail.js +80 -0
  56. package/dist/intent/build-detail.js.map +1 -0
  57. package/dist/intent/build-discover.d.ts +30 -0
  58. package/dist/intent/build-discover.js +149 -0
  59. package/dist/intent/build-discover.js.map +1 -0
  60. package/dist/intent/build-list.d.ts +28 -0
  61. package/dist/intent/build-list.js +75 -0
  62. package/dist/intent/build-list.js.map +1 -0
  63. package/dist/intent/build-mutation.d.ts +23 -0
  64. package/dist/intent/build-mutation.js +54 -0
  65. package/dist/intent/build-mutation.js.map +1 -0
  66. package/dist/intent/build-output.d.ts +27 -0
  67. package/dist/intent/build-output.js +60 -0
  68. package/dist/intent/build-output.js.map +1 -0
  69. package/dist/intent/build-raw.d.ts +23 -0
  70. package/dist/intent/build-raw.js +54 -0
  71. package/dist/intent/build-raw.js.map +1 -0
  72. package/dist/intent/build-update.d.ts +14 -0
  73. package/dist/intent/build-update.js +16 -0
  74. package/dist/intent/build-update.js.map +1 -0
  75. package/dist/intent/get-schema-with-priming.d.ts +26 -0
  76. package/dist/intent/get-schema-with-priming.js +32 -0
  77. package/dist/intent/get-schema-with-priming.js.map +1 -0
  78. package/dist/intent/select-child-relationship.d.ts +19 -0
  79. package/dist/intent/select-child-relationship.js +38 -0
  80. package/dist/intent/select-child-relationship.js.map +1 -0
  81. package/dist/intent/types.d.ts +159 -0
  82. package/dist/intent/types.js +21 -0
  83. package/dist/intent/types.js.map +1 -0
  84. package/dist/lib/apply-command.d.ts +15 -0
  85. package/dist/lib/apply-command.js +238 -0
  86. package/dist/lib/apply-command.js.map +1 -0
  87. package/dist/lib/auth.d.ts +38 -0
  88. package/dist/lib/auth.js +113 -0
  89. package/dist/lib/auth.js.map +1 -0
  90. package/dist/lib/codegen.d.ts +32 -0
  91. package/dist/lib/codegen.js +700 -0
  92. package/dist/lib/codegen.js.map +1 -0
  93. package/dist/lib/command-registry.d.ts +59 -0
  94. package/dist/lib/command-registry.js +366 -0
  95. package/dist/lib/command-registry.js.map +1 -0
  96. package/dist/lib/formatter.d.ts +76 -0
  97. package/dist/lib/formatter.js +419 -0
  98. package/dist/lib/formatter.js.map +1 -0
  99. package/dist/lib/fs-utils.d.ts +23 -0
  100. package/dist/lib/fs-utils.js +46 -0
  101. package/dist/lib/fs-utils.js.map +1 -0
  102. package/dist/lib/graphql-name.d.ts +27 -0
  103. package/dist/lib/graphql-name.js +32 -0
  104. package/dist/lib/graphql-name.js.map +1 -0
  105. package/dist/lib/interactive.d.ts +6 -0
  106. package/dist/lib/interactive.js +562 -0
  107. package/dist/lib/interactive.js.map +1 -0
  108. package/dist/lib/introspect.d.ts +40 -0
  109. package/dist/lib/introspect.js +280 -0
  110. package/dist/lib/introspect.js.map +1 -0
  111. package/dist/lib/object-info.d.ts +79 -0
  112. package/dist/lib/object-info.js +313 -0
  113. package/dist/lib/object-info.js.map +1 -0
  114. package/dist/lib/path-selection.d.ts +50 -0
  115. package/dist/lib/path-selection.js +146 -0
  116. package/dist/lib/path-selection.js.map +1 -0
  117. package/dist/lib/prime-schema.d.ts +59 -0
  118. package/dist/lib/prime-schema.js +158 -0
  119. package/dist/lib/prime-schema.js.map +1 -0
  120. package/dist/lib/query-builder.d.ts +10 -0
  121. package/dist/lib/query-builder.js +168 -0
  122. package/dist/lib/query-builder.js.map +1 -0
  123. package/dist/lib/query-commands.d.ts +19 -0
  124. package/dist/lib/query-commands.js +262 -0
  125. package/dist/lib/query-commands.js.map +1 -0
  126. package/dist/lib/session.d.ts +205 -0
  127. package/dist/lib/session.js +1075 -0
  128. package/dist/lib/session.js.map +1 -0
  129. package/dist/lib/tokenize.d.ts +12 -0
  130. package/dist/lib/tokenize.js +48 -0
  131. package/dist/lib/tokenize.js.map +1 -0
  132. package/dist/lib/uiapi.d.ts +109 -0
  133. package/dist/lib/uiapi.js +159 -0
  134. package/dist/lib/uiapi.js.map +1 -0
  135. package/dist/lib/validator.d.ts +27 -0
  136. package/dist/lib/validator.js +100 -0
  137. package/dist/lib/validator.js.map +1 -0
  138. package/dist/lib/variable-promotion.d.ts +69 -0
  139. package/dist/lib/variable-promotion.js +95 -0
  140. package/dist/lib/variable-promotion.js.map +1 -0
  141. package/dist/lib/walker.d.ts +147 -0
  142. package/dist/lib/walker.js +723 -0
  143. package/dist/lib/walker.js.map +1 -0
  144. package/dist/mcp/server.d.ts +7 -0
  145. package/dist/mcp/server.js +34 -0
  146. package/dist/mcp/server.js.map +1 -0
  147. package/dist/mcp/stdio.d.ts +7 -0
  148. package/dist/mcp/stdio.js +25 -0
  149. package/dist/mcp/stdio.js.map +1 -0
  150. package/dist/mcp/tools/echo.d.ts +7 -0
  151. package/dist/mcp/tools/echo.js +17 -0
  152. package/dist/mcp/tools/echo.js.map +1 -0
  153. package/dist/mcp/tools/sf-gql-aggregate.d.ts +11 -0
  154. package/dist/mcp/tools/sf-gql-aggregate.js +75 -0
  155. package/dist/mcp/tools/sf-gql-aggregate.js.map +1 -0
  156. package/dist/mcp/tools/sf-gql-create.d.ts +11 -0
  157. package/dist/mcp/tools/sf-gql-create.js +35 -0
  158. package/dist/mcp/tools/sf-gql-create.js.map +1 -0
  159. package/dist/mcp/tools/sf-gql-delete.d.ts +11 -0
  160. package/dist/mcp/tools/sf-gql-delete.js +31 -0
  161. package/dist/mcp/tools/sf-gql-delete.js.map +1 -0
  162. package/dist/mcp/tools/sf-gql-detail.d.ts +11 -0
  163. package/dist/mcp/tools/sf-gql-detail.js +58 -0
  164. package/dist/mcp/tools/sf-gql-detail.js.map +1 -0
  165. package/dist/mcp/tools/sf-gql-discover.d.ts +9 -0
  166. package/dist/mcp/tools/sf-gql-discover.js +51 -0
  167. package/dist/mcp/tools/sf-gql-discover.js.map +1 -0
  168. package/dist/mcp/tools/sf-gql-list.d.ts +11 -0
  169. package/dist/mcp/tools/sf-gql-list.js +53 -0
  170. package/dist/mcp/tools/sf-gql-list.js.map +1 -0
  171. package/dist/mcp/tools/sf-gql-raw.d.ts +11 -0
  172. package/dist/mcp/tools/sf-gql-raw.js +39 -0
  173. package/dist/mcp/tools/sf-gql-raw.js.map +1 -0
  174. package/dist/mcp/tools/sf-gql-update.d.ts +11 -0
  175. package/dist/mcp/tools/sf-gql-update.js +35 -0
  176. package/dist/mcp/tools/sf-gql-update.js.map +1 -0
  177. package/dist/mcp/tools/shared/zod-schemas.d.ts +49 -0
  178. package/dist/mcp/tools/shared/zod-schemas.js +46 -0
  179. package/dist/mcp/tools/shared/zod-schemas.js.map +1 -0
  180. package/package.json +36 -0
  181. package/ralph-loop.sh +120 -0
  182. package/scripts/smoke-orderby.sh +190 -0
  183. package/src/__tests__/helpers/prime-deps.ts +46 -0
  184. package/src/__tests__/helpers/schema.ts +73 -0
  185. package/src/__tests__/helpers/stdout.ts +33 -0
  186. package/src/__tests__/setup.ts +19 -0
  187. package/src/cli.ts +764 -0
  188. package/src/commands/__tests__/query.spec.ts +137 -0
  189. package/src/commands/args.ts +306 -0
  190. package/src/commands/build.ts +288 -0
  191. package/src/commands/connect.ts +60 -0
  192. package/src/commands/describe.ts +246 -0
  193. package/src/commands/meta.ts +171 -0
  194. package/src/commands/navigate.ts +134 -0
  195. package/src/commands/query-helpers.ts +1202 -0
  196. package/src/commands/query.ts +1085 -0
  197. package/src/commands/review.ts +670 -0
  198. package/src/commands/session-mgmt.ts +256 -0
  199. package/src/commands/type.ts +437 -0
  200. package/src/commands/validate-input.ts +38 -0
  201. package/src/intent/__tests__/build-aggregate.spec.ts +931 -0
  202. package/src/intent/__tests__/build-create-validation.spec.ts +135 -0
  203. package/src/intent/__tests__/build-delete.spec.ts +121 -0
  204. package/src/intent/__tests__/build-detail.spec.ts +333 -0
  205. package/src/intent/__tests__/build-discover.spec.ts +432 -0
  206. package/src/intent/__tests__/build-list.spec.ts +284 -0
  207. package/src/intent/__tests__/build-mutation.spec.ts +108 -0
  208. package/src/intent/__tests__/build-output.spec.ts +55 -0
  209. package/src/intent/__tests__/build-raw.spec.ts +153 -0
  210. package/src/intent/__tests__/build-update-validation.spec.ts +134 -0
  211. package/src/intent/build-aggregate.ts +182 -0
  212. package/src/intent/build-create.ts +19 -0
  213. package/src/intent/build-delete.ts +62 -0
  214. package/src/intent/build-detail.ts +95 -0
  215. package/src/intent/build-discover.ts +199 -0
  216. package/src/intent/build-list.ts +91 -0
  217. package/src/intent/build-mutation.ts +75 -0
  218. package/src/intent/build-output.ts +72 -0
  219. package/src/intent/build-raw.ts +61 -0
  220. package/src/intent/build-update.ts +19 -0
  221. package/src/intent/get-schema-with-priming.ts +43 -0
  222. package/src/intent/select-child-relationship.ts +48 -0
  223. package/src/intent/types.ts +181 -0
  224. package/src/lib/__tests__/apply-command.parity.spec.ts +97 -0
  225. package/src/lib/__tests__/apply-command.spec.ts +171 -0
  226. package/src/lib/__tests__/auth.spec.ts +292 -0
  227. package/src/lib/__tests__/formatter.spec.ts +86 -0
  228. package/src/lib/__tests__/graphql-name.spec.ts +64 -0
  229. package/src/lib/__tests__/introspect.spec.ts +399 -0
  230. package/src/lib/__tests__/object-info.spec.ts +124 -0
  231. package/src/lib/__tests__/path-selection.spec.ts +219 -0
  232. package/src/lib/__tests__/prime-schema.spec.ts +269 -0
  233. package/src/lib/__tests__/query-builder.spec.ts +95 -0
  234. package/src/lib/__tests__/query-commands.spec.ts +74 -0
  235. package/src/lib/__tests__/session.spec.ts +292 -0
  236. package/src/lib/__tests__/tokenize.spec.ts +33 -0
  237. package/src/lib/__tests__/uiapi.spec.ts +192 -0
  238. package/src/lib/__tests__/variable-promotion.spec.ts +211 -0
  239. package/src/lib/__tests__/walker.spec.ts +250 -0
  240. package/src/lib/apply-command.ts +286 -0
  241. package/src/lib/auth.ts +157 -0
  242. package/src/lib/codegen.ts +899 -0
  243. package/src/lib/command-registry.ts +434 -0
  244. package/src/lib/formatter.ts +587 -0
  245. package/src/lib/fs-utils.ts +46 -0
  246. package/src/lib/graphql-name.ts +35 -0
  247. package/src/lib/interactive.ts +634 -0
  248. package/src/lib/introspect.ts +320 -0
  249. package/src/lib/object-info.ts +409 -0
  250. package/src/lib/path-selection.ts +162 -0
  251. package/src/lib/prime-schema.ts +195 -0
  252. package/src/lib/query-builder.ts +193 -0
  253. package/src/lib/query-commands.ts +290 -0
  254. package/src/lib/session.ts +1304 -0
  255. package/src/lib/tokenize.ts +43 -0
  256. package/src/lib/uiapi.ts +176 -0
  257. package/src/lib/validator.ts +143 -0
  258. package/src/lib/variable-promotion.ts +145 -0
  259. package/src/lib/walker.ts +975 -0
  260. package/src/mcp/__tests__/server.spec.ts +155 -0
  261. package/src/mcp/server.ts +38 -0
  262. package/src/mcp/stdio.ts +29 -0
  263. package/src/mcp/tools/__tests__/sf-gql-aggregate.spec.ts +173 -0
  264. package/src/mcp/tools/__tests__/sf-gql-create.spec.ts +235 -0
  265. package/src/mcp/tools/__tests__/sf-gql-delete.spec.ts +194 -0
  266. package/src/mcp/tools/__tests__/sf-gql-detail.spec.ts +246 -0
  267. package/src/mcp/tools/__tests__/sf-gql-discover.spec.ts +320 -0
  268. package/src/mcp/tools/__tests__/sf-gql-list.spec.ts +128 -0
  269. package/src/mcp/tools/__tests__/sf-gql-raw.spec.ts +141 -0
  270. package/src/mcp/tools/__tests__/sf-gql-update.spec.ts +207 -0
  271. package/src/mcp/tools/echo.ts +24 -0
  272. package/src/mcp/tools/sf-gql-aggregate.ts +102 -0
  273. package/src/mcp/tools/sf-gql-create.ts +55 -0
  274. package/src/mcp/tools/sf-gql-delete.ts +49 -0
  275. package/src/mcp/tools/sf-gql-detail.ts +85 -0
  276. package/src/mcp/tools/sf-gql-discover.ts +67 -0
  277. package/src/mcp/tools/sf-gql-list.ts +73 -0
  278. package/src/mcp/tools/sf-gql-raw.ts +56 -0
  279. package/src/mcp/tools/sf-gql-update.ts +55 -0
  280. package/src/mcp/tools/shared/zod-schemas.ts +55 -0
  281. package/tsconfig.json +18 -0
  282. package/vitest.config.ts +14 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import { buildOutput } from "./build-output.js";
8
+ import { getSchemaWithPriming } from "./get-schema-with-priming.js";
9
+ import { type DeleteSpec, type ToolOutput } from "./types.js";
10
+ import { assertGraphqlName } from "../lib/graphql-name.js";
11
+ import { selectDottedFieldPath } from "../lib/path-selection.js";
12
+ import { type PrimeDeps } from "../lib/prime-schema.js";
13
+ import { addVariable, createSession, deepSetArg } from "../lib/session.js";
14
+ import { mutationFieldPath } from "../lib/uiapi.js";
15
+
16
+ /**
17
+ * Build a UIAPI delete mutation against a Salesforce org. Implements
18
+ * `sf_gql_delete` intent for the graphiti MCP server (FR-5.7).
19
+ *
20
+ * Unlike create/update, delete is uniform across SObjects:
21
+ * - The input type is the schema-wide `RecordDeleteInput!`, NOT an
22
+ * `<Object>`-specific input. Every `<Object>Delete` mutation field
23
+ * accepts the same `RecordDeleteInput!` carrying the record `Id`.
24
+ * - The result is a `RecordDeletePayload` exposing only `Id` (a plain
25
+ * `ID`, not a value wrapper), so the selection is always `Id` and
26
+ * there is no `Record` sub-path — hence no `returnFields`.
27
+ *
28
+ * Implicit behaviors not visible in the signature:
29
+ * - `inputVariable` defaults to `"input"`, declared as
30
+ * `$<name>: RecordDeleteInput!`. A leading `$` is stripped.
31
+ * - Operation name defaults to `Delete<Object>` (e.g. `DeleteAccount`).
32
+ *
33
+ * Throws on invalid `object`, `inputVariable`, or `operationName` (must be
34
+ * valid GraphQL Names), auth-missing, or introspection failure (via
35
+ * `getSchemaWithPriming`); never throws on validation or codegen
36
+ * failure (those surface as `warnings[]`).
37
+ */
38
+ export async function buildDelete(spec: DeleteSpec, deps?: PrimeDeps): Promise<ToolOutput> {
39
+ assertGraphqlName(spec.object, "buildDelete", "object");
40
+
41
+ const inputVar = (spec.inputVariable ?? "input").replace(/^\$/, "");
42
+ assertGraphqlName(inputVar, "buildDelete", "inputVariable");
43
+
44
+ const { schema, primingNote, instanceUrl } = await getSchemaWithPriming(spec.org, deps);
45
+
46
+ const session = createSession(spec.org, "mutation", instanceUrl);
47
+ session.operationName = spec.operationName ?? "Delete" + spec.object;
48
+ assertGraphqlName(session.operationName, "buildDelete", "operationName");
49
+
50
+ const fieldPath = mutationFieldPath(spec.object, "Delete");
51
+ addVariable(session, inputVar, "RecordDeleteInput!");
52
+ deepSetArg(session, fieldPath, "input", [], "$" + inputVar);
53
+
54
+ // Delete payloads expose only `Id` (a plain ID — never a value wrapper).
55
+ // The selection is a literal single segment on the mutation field, so
56
+ // `selectDottedFieldPath` cannot realistically throw here: there is no
57
+ // user-supplied dotted path (unlike `buildMutation`'s `returnFields`) and
58
+ // no top-level mutation-arg context that raises `MutationContextError`.
59
+ selectDottedFieldPath(session, schema, fieldPath, "Id");
60
+
61
+ return buildOutput(session, schema, primingNote, []);
62
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import { buildOutput } from "./build-output.js";
8
+ import { getSchemaWithPriming } from "./get-schema-with-priming.js";
9
+ import { selectChildRelationship } from "./select-child-relationship.js";
10
+ import { type DetailSpec, type ToolOutput } from "./types.js";
11
+ import { assertGraphqlName } from "../lib/graphql-name.js";
12
+ import { selectDottedFieldPath } from "../lib/path-selection.js";
13
+ import { type PrimeDeps } from "../lib/prime-schema.js";
14
+ import { addVariable, createSession, deepSetArg } from "../lib/session.js";
15
+ import { connectionNodePath, connectionPath } from "../lib/uiapi.js";
16
+
17
+ /**
18
+ * Build a UIAPI single-record detail query against a Salesforce org. Implements
19
+ * `sf_gql_detail` intent for the graphiti MCP server (FR-5.5).
20
+ *
21
+ * Implicit behaviors not visible in the signature:
22
+ * - Always declares `<idVariable>: ID!` (default name `id`) and binds it via
23
+ * `where: { Id: { eq: $<idVariable> } }`, with `first: 1` set on the
24
+ * connection. Single-record by Id is the whole point of this tool.
25
+ * - Unlike `sf_gql_list`: no `$after` cursor, no `pageInfo` selection, and no
26
+ * top-level `filter` / `orderBy` / `scope`.
27
+ * - Scalar fields auto-wrap with `{ value }` per UIAPI; `Id` is selected bare.
28
+ * - Dotted paths in `fields` / `parentFields` expand polymorphic unions into
29
+ * per-member inline fragments; members lacking the field are silently skipped.
30
+ * - `childRelationships` render as `<rel> { edges { node { ... } } }`
31
+ * connections with their own `first` / `filter` / `orderBy` (and `$varName`
32
+ * leaves promote the same way they do on `sf_gql_list`).
33
+ * - Operation name defaults to `<Object>Detail`.
34
+ *
35
+ * Throws on invalid `object` or `operationName` (must be valid GraphQL Names),
36
+ * auth-missing or introspection failure (via `getSchemaWithPriming`), on FR-5.5
37
+ * spec violations (empty `idVariable`, collision with a child-filter `$varName`
38
+ * of a different type); never throws on validation or codegen failure (those
39
+ * surface as `warnings[]`).
40
+ */
41
+ export async function buildDetail(spec: DetailSpec, deps?: PrimeDeps): Promise<ToolOutput> {
42
+ if (spec.idVariable !== undefined && spec.idVariable.length === 0) {
43
+ throw new Error("buildDetail: idVariable must be a non-empty string (FR-5.5)");
44
+ }
45
+ assertGraphqlName(spec.object, "buildDetail", "object");
46
+
47
+ const { schema, primingNote, instanceUrl } = await getSchemaWithPriming(spec.org, deps);
48
+
49
+ const session = createSession(spec.org, "query", instanceUrl);
50
+ session.operationName = spec.operationName ?? `${spec.object}Detail`;
51
+ assertGraphqlName(session.operationName, "buildDetail", "operationName");
52
+
53
+ const connection = connectionPath(spec.object);
54
+ const node = connectionNodePath(connection);
55
+
56
+ for (const field of spec.fields) {
57
+ selectDottedFieldPath(session, schema, node, field);
58
+ }
59
+
60
+ if (spec.parentFields) {
61
+ for (const pf of spec.parentFields) {
62
+ selectDottedFieldPath(session, schema, node, pf);
63
+ }
64
+ }
65
+
66
+ if (spec.childRelationships) {
67
+ for (const child of spec.childRelationships) {
68
+ selectChildRelationship(session, schema, node, child);
69
+ }
70
+ }
71
+
72
+ // FR-5.5: <idVariable>: ID! + where: { Id: { eq: $<idVariable> } } + first: 1.
73
+ // Per-leaf deepSetArg is the cleanest form for a hardcoded shape: session.ts
74
+ // preserves the leading `$` raw, and query-builder.ts emits it as an unquoted
75
+ // variable reference. Same machinery the aggregate builder uses for groupBy.
76
+ const idVar = spec.idVariable ?? "id";
77
+
78
+ // Children promote variables before the ID var is added; if a child filter
79
+ // already declared a variable with this name AND a different type, we'd
80
+ // silently overwrite it to `ID!` — corrupting the user's filter binding.
81
+ // Detect and throw rather than mask the conflict.
82
+ const existing = session.variables.find((v) => v.name === idVar);
83
+ if (existing && existing.type !== "ID!") {
84
+ throw new Error(
85
+ `buildDetail: idVariable "${idVar}" collides with a $${idVar} reference in childRelationships ` +
86
+ `(declared as ${existing.type}); choose a different idVariable.`,
87
+ );
88
+ }
89
+
90
+ addVariable(session, idVar, "ID!");
91
+ deepSetArg(session, connection, "where", ["Id", "eq"], `$${idVar}`);
92
+ deepSetArg(session, connection, "first", [], "1");
93
+
94
+ return buildOutput(session, schema, primingNote);
95
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import { type GraphQLSchema } from "graphql";
8
+ import { getSchemaWithPriming } from "./get-schema-with-priming.js";
9
+ import {
10
+ type DiscoverOutput,
11
+ type DiscoverSpec,
12
+ type FieldDescription,
13
+ type ObjectDescription,
14
+ } from "./types.js";
15
+ import { getOrgAuth as realGetOrgAuth, type OrgAuth } from "../lib/auth.js";
16
+ import {
17
+ type FieldMetadata,
18
+ getObjectInfo as realGetObjectInfo,
19
+ getRequiredCreateFields,
20
+ type ObjectInfoResult,
21
+ } from "../lib/object-info.js";
22
+ import { type PrimeDeps } from "../lib/prime-schema.js";
23
+ import { resolvePath } from "../lib/walker.js";
24
+
25
+ export interface DiscoverDeps {
26
+ primeDeps?: PrimeDeps;
27
+ getOrgAuth?: (orgAlias: string) => Promise<OrgAuth>;
28
+ getObjectInfo?: (
29
+ auth: OrgAuth,
30
+ orgAlias: string,
31
+ sObjectName: string,
32
+ ) => Promise<ObjectInfoResult>;
33
+ }
34
+
35
+ /**
36
+ * Build the `sf_gql_discover` payload. Three modes:
37
+ *
38
+ * - `list_objects`: enumerate queryable SObjects from the schema's
39
+ * `uiapi.query` selection. Optional `search` substring-filters names.
40
+ * - `describe_object`: fetch ObjectInfo for `spec.object` and project it
41
+ * onto the spec-defined `ObjectDescription` shape.
42
+ * - `describe_field`: same as `describe_object`, narrowed to one field.
43
+ *
44
+ * Schema priming follows FR-13.3 — first call against an unprimed org
45
+ * triggers introspection and surfaces a `Note: Primed schema cache ...`
46
+ * warning. ObjectInfo carries its own per-object cache inside
47
+ * `lib/object-info.ts` (1-hour TTL), so describe_* modes do not
48
+ * re-introspect on every call.
49
+ */
50
+ export async function buildDiscover(
51
+ spec: DiscoverSpec,
52
+ deps: DiscoverDeps = {},
53
+ ): Promise<DiscoverOutput> {
54
+ const { schema, primingNote } = await getSchemaWithPriming(spec.org, deps.primeDeps);
55
+ const warnings = primingNote ? [primingNote] : [];
56
+
57
+ if (spec.mode === "list_objects") {
58
+ const objects = listQueryableObjects(schema, spec.search);
59
+ return { mode: "list_objects", objects, ...(warnings.length ? { warnings } : {}) };
60
+ }
61
+
62
+ if (!spec.object) {
63
+ throw new Error(`sf_gql_discover mode "${spec.mode}" requires "object".`);
64
+ }
65
+
66
+ const getAuth = deps.getOrgAuth ?? realGetOrgAuth;
67
+ const fetchObjectInfo = deps.getObjectInfo ?? realGetObjectInfo;
68
+ const auth = await getAuth(spec.org);
69
+ // ObjectInfo failures (auth expired, network blip, object missing) propagate
70
+ // to the caller. Earlier work tried a schema-only fallback that returned
71
+ // field names from the cached GraphQL schema, but that response cannot
72
+ // satisfy ObjectDescription (no picklists, no filterable/sortable flags,
73
+ // no requiredOnCreate) and silently invites consumers to treat absent
74
+ // metadata as authoritative absence. A loud error forces the right
75
+ // recovery (refresh auth, prime schema, retry).
76
+ const info = await fetchObjectInfo(auth, spec.org, spec.object);
77
+
78
+ if (spec.mode === "describe_field") {
79
+ if (!spec.field) {
80
+ throw new Error('sf_gql_discover mode "describe_field" requires "field".');
81
+ }
82
+ const field = info.fields.find((f) => f.apiName === spec.field);
83
+ if (!field) {
84
+ throw new Error(
85
+ `Field "${spec.field}" not found on "${spec.object}". Use mode "describe_object" to list fields.`,
86
+ );
87
+ }
88
+ return {
89
+ mode: "describe_field",
90
+ field: toFieldDescription(field, info),
91
+ ...(warnings.length ? { warnings } : {}),
92
+ };
93
+ }
94
+
95
+ if (spec.mode === "describe_object") {
96
+ return {
97
+ mode: "describe_object",
98
+ object: toObjectDescription(spec.object, info),
99
+ ...(warnings.length ? { warnings } : {}),
100
+ };
101
+ }
102
+
103
+ // Exhaustiveness guard: any new DiscoverMode added to types.ts must
104
+ // surface as a TS error here instead of silently falling through to
105
+ // describe_object.
106
+ throw new Error(`Unhandled discover mode: ${exhaustive(spec.mode)}`);
107
+ }
108
+
109
+ function exhaustive(value: never): never {
110
+ throw new Error(`Unexpected value: ${String(value)}`);
111
+ }
112
+
113
+ function listQueryableObjects(
114
+ schema: GraphQLSchema,
115
+ search?: string,
116
+ ): { name: string; label?: string }[] {
117
+ let result: { name: string; label?: string }[];
118
+ try {
119
+ const walker = resolvePath(schema, "query", ["uiapi", "query"]);
120
+ // uiapi.query exposes per-SObject Connection fields (Account, Contact, etc.)
121
+ // alongside non-SObject helpers (search, aggregate). Filter to fields whose
122
+ // return type follows the *Connection convention so list_objects matches
123
+ // the spec contract — "queryable SObjects" — instead of leaking helpers.
124
+ result = walker.fields
125
+ .filter((f) => /Connection$/.test(f.typeName.replace(/[![\]]/g, "")))
126
+ .map((f) => ({
127
+ name: f.name,
128
+ ...(f.description ? { label: f.description } : {}),
129
+ }));
130
+ } catch {
131
+ return [];
132
+ }
133
+
134
+ if (search && search.trim().length > 0) {
135
+ const needle = search.toLowerCase();
136
+ result = result.filter((o) => o.name.toLowerCase().includes(needle));
137
+ }
138
+ result.sort((a, b) => a.name.localeCompare(b.name));
139
+ return result;
140
+ }
141
+
142
+ function toFieldDescription(field: FieldMetadata, info: ObjectInfoResult): FieldDescription {
143
+ const picklist = info.picklists.find((p) => p.apiName === field.apiName);
144
+ // `parseObjectInfoResponse` already drops null-valued entries, but the
145
+ // PicklistValue type still allows null — filter narrows to string[].
146
+ const picklistValues =
147
+ picklist?.values.map((v) => v.value).filter((v): v is string => v !== null) ?? [];
148
+
149
+ return {
150
+ name: field.apiName,
151
+ label: field.label ?? field.apiName,
152
+ type: field.dataType ?? "UNKNOWN",
153
+ filterable: field.filterable,
154
+ sortable: field.sortable,
155
+ nameField: field.nameField,
156
+ compound: field.compound,
157
+ defaultedOnCreate: field.defaultedOnCreate,
158
+ ...(picklistValues.length > 0 ? { picklistValues } : {}),
159
+ };
160
+ }
161
+
162
+ function toObjectDescription(name: string, info: ObjectInfoResult): ObjectDescription {
163
+ const fields = info.fields.map((f) => toFieldDescription(f, info));
164
+
165
+ const childRelationships = info.childRelationships
166
+ .filter((cr) => cr.relationshipName !== null)
167
+ .map((cr) => ({
168
+ relationshipName: cr.relationshipName as string,
169
+ childObject: cr.childObjectApiName,
170
+ }));
171
+
172
+ const parentReferences = info.fields
173
+ .filter((f) => f.reference && f.referenceToInfos.length > 0)
174
+ .map((f) => ({
175
+ field: f.apiName,
176
+ targetObjects: f.referenceToInfos.map((r) => r.apiName),
177
+ }));
178
+
179
+ const filterableFields = info.fields
180
+ .filter((f) => f.filterable && !f.compound)
181
+ .map((f) => f.apiName);
182
+
183
+ const sortable = info.fields.find((f) => f.sortable && !f.compound);
184
+ const orderByExample: Record<string, unknown> = sortable
185
+ ? { [sortable.apiName]: { order: "DESC" } }
186
+ : {};
187
+
188
+ const requiredOnCreate = getRequiredCreateFields(info).map((f) => f.apiName);
189
+
190
+ return {
191
+ name,
192
+ fields,
193
+ childRelationships,
194
+ parentReferences,
195
+ filterableFields,
196
+ orderByExample,
197
+ requiredOnCreate,
198
+ };
199
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import { buildOutput } from "./build-output.js";
8
+ import { getSchemaWithPriming } from "./get-schema-with-priming.js";
9
+ import { selectChildRelationship } from "./select-child-relationship.js";
10
+ import { type ListSpec, type ToolOutput } from "./types.js";
11
+ import { assertGraphqlName } from "../lib/graphql-name.js";
12
+ import { selectDottedFieldPath } from "../lib/path-selection.js";
13
+ import { type PrimeDeps } from "../lib/prime-schema.js";
14
+ import { addVariable, createSession, deepSetArg, selectLeaf } from "../lib/session.js";
15
+ import { normalizeOrderBy, promoteVariables } from "../lib/variable-promotion.js";
16
+
17
+ /**
18
+ * Build a UIAPI list query against a Salesforce org. Implements `sf_gql_list`
19
+ * intent for the graphiti MCP server.
20
+ *
21
+ * Implicit behaviors not visible in the signature:
22
+ * - Scalar fields are auto-wrapped with `{ value }` per UIAPI; `Id` is selected bare.
23
+ * - Dotted paths in `fields` / `parentFields` expand polymorphic unions into
24
+ * per-member inline fragments; members lacking the field are silently skipped.
25
+ * - `childRelationships` render as `<rel> { edges { node { ... } } }` connections.
26
+ * - `$varName` leaves anywhere in `filter` / `orderBy` / `scope` promote to typed
27
+ * query variables (inferred from the schema; nullable; `String` on inference miss).
28
+ * - `orderBy` arrays are collapsed to the first element (UIAPI expects a singleton).
29
+ * - Cursor pagination is always on: declares `$after: String`, selects
30
+ * `pageInfo { hasNextPage endCursor }`, defaults `first: 10`.
31
+ * - Operation name defaults to `<Object>List`.
32
+ *
33
+ * Throws on invalid `object` or `operationName` (must be valid GraphQL Names),
34
+ * auth-missing, or introspection failure (via `getSchemaWithPriming`); never
35
+ * throws on validation or codegen failure (those surface as `warnings[]`).
36
+ */
37
+ export async function buildList(spec: ListSpec, deps?: PrimeDeps): Promise<ToolOutput> {
38
+ assertGraphqlName(spec.object, "buildList", "object");
39
+
40
+ const { schema, primingNote, instanceUrl } = await getSchemaWithPriming(spec.org, deps);
41
+
42
+ const session = createSession(spec.org, "query", instanceUrl);
43
+ session.operationName = spec.operationName ?? `${spec.object}List`;
44
+ assertGraphqlName(session.operationName, "buildList", "operationName");
45
+
46
+ const connectionPath = ["uiapi", "query", spec.object];
47
+ const nodePath = [...connectionPath, "edges", "node"];
48
+
49
+ for (const field of spec.fields) {
50
+ selectDottedFieldPath(session, schema, nodePath, field);
51
+ }
52
+
53
+ if (spec.parentFields) {
54
+ for (const pf of spec.parentFields) {
55
+ selectDottedFieldPath(session, schema, nodePath, pf);
56
+ }
57
+ }
58
+
59
+ if (spec.childRelationships) {
60
+ for (const child of spec.childRelationships) {
61
+ selectChildRelationship(session, schema, nodePath, child);
62
+ }
63
+ }
64
+
65
+ const first = spec.first ?? 10;
66
+ deepSetArg(session, connectionPath, "first", [], String(first));
67
+
68
+ addVariable(session, "after", "String");
69
+ deepSetArg(session, connectionPath, "after", [], "$after");
70
+
71
+ selectLeaf(session, [...connectionPath, "pageInfo", "hasNextPage"]);
72
+ selectLeaf(session, [...connectionPath, "pageInfo", "endCursor"]);
73
+
74
+ if (spec.filter) {
75
+ promoteVariables(session, schema, connectionPath, "where", spec.filter);
76
+ deepSetArg(session, connectionPath, "where", [], JSON.stringify(spec.filter));
77
+ }
78
+
79
+ const orderBy = normalizeOrderBy(spec.orderBy);
80
+ if (orderBy) {
81
+ promoteVariables(session, schema, connectionPath, "orderBy", orderBy);
82
+ deepSetArg(session, connectionPath, "orderBy", [], JSON.stringify(orderBy));
83
+ }
84
+
85
+ if (spec.scope) {
86
+ promoteVariables(session, schema, connectionPath, "scope", spec.scope);
87
+ deepSetArg(session, connectionPath, "scope", [], spec.scope);
88
+ }
89
+
90
+ return buildOutput(session, schema, primingNote);
91
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import { buildOutput } from "./build-output.js";
8
+ import { getSchemaWithPriming } from "./get-schema-with-priming.js";
9
+ import { type CreateSpec, type ToolOutput, type UpdateSpec } from "./types.js";
10
+ import { assertGraphqlName } from "../lib/graphql-name.js";
11
+ import { selectDottedFieldPath } from "../lib/path-selection.js";
12
+ import { type PrimeDeps } from "../lib/prime-schema.js";
13
+ import { addVariable, createSession, deepSetArg } from "../lib/session.js";
14
+ import {
15
+ createInputTypeName,
16
+ mutationFieldPath,
17
+ mutationRecordPath,
18
+ updateInputTypeName,
19
+ } from "../lib/uiapi.js";
20
+
21
+ export type MutationOp = "Create" | "Update";
22
+
23
+ /**
24
+ * Build a UIAPI Create/Update mutation against a Salesforce org. Shared
25
+ * implementation for `sf_gql_create` and `sf_gql_update` (FR-5.6).
26
+ *
27
+ * Implicit behaviors:
28
+ * - `returnFields` defaults to `["Id"]`.
29
+ * - `inputVariable` defaults to `"input"`; a leading `$` is stripped.
30
+ * - Operation name defaults to `<Op><Object>` (e.g. `CreateAccount`, `UpdateAccount`).
31
+ * - The `input` argument on the mutation field is bound to the declared variable.
32
+ *
33
+ * Throws on invalid `object`/`inputVariable`/`operationName` GraphQL Names, empty
34
+ * `returnFields`, auth-missing, or introspection failure. Never throws on
35
+ * validation/codegen failure (those surface as `warnings[]`).
36
+ */
37
+ export async function buildMutation(
38
+ spec: CreateSpec | UpdateSpec,
39
+ op: MutationOp,
40
+ deps?: PrimeDeps,
41
+ ): Promise<ToolOutput> {
42
+ assertGraphqlName(spec.object, "buildMutation", "object");
43
+
44
+ const inputVar = (spec.inputVariable ?? "input").replace(/^\$/, "");
45
+ assertGraphqlName(inputVar, "buildMutation", "inputVariable");
46
+
47
+ if (spec.returnFields !== undefined && spec.returnFields.length === 0) {
48
+ throw new Error("buildMutation: returnFields must contain at least one field");
49
+ }
50
+
51
+ const { schema, primingNote, instanceUrl } = await getSchemaWithPriming(spec.org, deps);
52
+
53
+ const session = createSession(spec.org, "mutation", instanceUrl);
54
+ session.operationName = spec.operationName ?? op + spec.object;
55
+ assertGraphqlName(session.operationName, "buildMutation", "operationName");
56
+
57
+ const fieldPath = mutationFieldPath(spec.object, op);
58
+ const recordPath = mutationRecordPath(spec.object, op);
59
+ const inputTypeName =
60
+ op === "Create" ? createInputTypeName(spec.object) : updateInputTypeName(spec.object);
61
+ addVariable(session, inputVar, inputTypeName + "!");
62
+ deepSetArg(session, fieldPath, "input", [], "$" + inputVar);
63
+
64
+ const extraWarnings: string[] = [];
65
+ for (const field of spec.returnFields ?? ["Id"]) {
66
+ try {
67
+ selectDottedFieldPath(session, schema, recordPath, field);
68
+ } catch (err) {
69
+ const msg = err instanceof Error ? err.message : String(err);
70
+ extraWarnings.push(`returnFields: ${msg}`);
71
+ }
72
+ }
73
+
74
+ return buildOutput(session, schema, primingNote, extraWarnings);
75
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import { type GraphQLSchema } from "graphql";
8
+ import { type ToolOutput, type VariableInfo } from "./types.js";
9
+ import { generateTypes } from "../lib/codegen.js";
10
+ import { renderQuery } from "../lib/query-builder.js";
11
+ import { type QuerySession } from "../lib/session.js";
12
+ import { validateQuery } from "../lib/validator.js";
13
+
14
+ const SCHEMA_LEVEL_ERROR_MARKERS = ["must define one or more fields"];
15
+
16
+ /**
17
+ * Render → validate → codegen → assemble. Shared finalizer for every typed
18
+ * intent function (`buildList`, `buildDetail`, …).
19
+ *
20
+ * Never throws. Failure modes surface as entries in `warnings[]`:
21
+ * - `Validation: <msg>` — non-schema-level errors from `graphql-js validate()`.
22
+ * - `Validation: schema check skipped (<msg>)` — `validate()` itself crashed.
23
+ * - `Codegen: <msg>` — `generateTypes()` threw; `types` becomes `// Type generation failed: <msg>`.
24
+ *
25
+ * Schema-level errors (e.g. "Input Object type X must define one or more fields"
26
+ * raised by malformed UIAPI schemas, not by the user's query) are filtered out
27
+ * per FR-9.2.
28
+ *
29
+ * `primingNote` (if provided) is prepended to `warnings[]` so callers can surface
30
+ * the FR-13.3 lazy-prime notification without a separate channel. `extraWarnings`
31
+ * are appended after schema-validation warnings — used by intent builders to
32
+ * surface non-validator findings (e.g. malformed `$var` placeholders).
33
+ */
34
+ export function buildOutput(
35
+ session: QuerySession,
36
+ schema: GraphQLSchema,
37
+ primingNote?: string,
38
+ extraWarnings: string[] = [],
39
+ ): ToolOutput {
40
+ const query = renderQuery(session);
41
+ const warnings: string[] = primingNote ? [primingNote] : [];
42
+
43
+ try {
44
+ const errors = validateQuery(schema, query);
45
+ for (const err of errors) {
46
+ if (SCHEMA_LEVEL_ERROR_MARKERS.some((m) => err.message.includes(m))) continue;
47
+ warnings.push(`Validation: ${err.message}`);
48
+ }
49
+ } catch (err) {
50
+ const message = err instanceof Error ? err.message : String(err);
51
+ warnings.push(`Validation: schema check skipped (${message})`);
52
+ }
53
+
54
+ let types: string;
55
+ try {
56
+ types = generateTypes(session, schema);
57
+ } catch (err) {
58
+ const message = err instanceof Error ? err.message : String(err);
59
+ types = `// Type generation failed: ${message}`;
60
+ warnings.push(`Codegen: ${message}`);
61
+ }
62
+
63
+ const variables: VariableInfo[] = session.variables.map((v) => ({
64
+ name: v.name,
65
+ type: v.type,
66
+ required: v.type.endsWith("!"),
67
+ }));
68
+
69
+ warnings.push(...extraWarnings);
70
+
71
+ return { query, variables, types, warnings };
72
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import { buildOutput } from "./build-output.js";
8
+ import { getSchemaWithPriming } from "./get-schema-with-priming.js";
9
+ import { type RawSpec, type RawOperation, type ToolOutput } from "./types.js";
10
+ import { applyCommand } from "../lib/apply-command.js";
11
+ import { assertGraphqlName } from "../lib/graphql-name.js";
12
+ import { type PrimeDeps } from "../lib/prime-schema.js";
13
+ import { createSession } from "../lib/session.js";
14
+
15
+ const DEFAULT_OP_NAME: Record<RawOperation, string> = {
16
+ query: "RawQuery",
17
+ mutation: "RawMutation",
18
+ aggregate: "RawAggregate",
19
+ };
20
+
21
+ /**
22
+ * Build an arbitrary UIAPI operation from CLI-style commands. Implements
23
+ * `sf_gql_raw` — the low-level escape hatch (FR-12) for operations the typed
24
+ * tools do not model (cross-union selections, custom mutations).
25
+ *
26
+ * `operation` selects the session root (query/mutation/aggregate); the agent
27
+ * hand-drives every selection. No typed-tool sugar (no aggregations[] handling,
28
+ * no auto-pagination, no Id defaulting).
29
+ *
30
+ * Fails fast: the first command that cannot be applied throws
31
+ * `command <i> (<cmd>): <why>`. Throws on an invalid `typeName` (must be a valid
32
+ * GraphQL Name), auth-missing / introspection failure (via getSchemaWithPriming)
33
+ * and on empty `commands`. Never throws on validation or codegen failure (those
34
+ * surface as warnings[]).
35
+ */
36
+ export async function buildRaw(spec: RawSpec, deps?: PrimeDeps): Promise<ToolOutput> {
37
+ if (!spec.commands || spec.commands.length === 0) {
38
+ throw new Error("buildRaw: commands must contain at least one command");
39
+ }
40
+
41
+ const operation: RawOperation = spec.operation ?? "query";
42
+ const { schema, primingNote, instanceUrl } = await getSchemaWithPriming(spec.org, deps);
43
+
44
+ const session = createSession(spec.org, operation, instanceUrl);
45
+ // `typeName` lands only in the rendered operation-name position (buildOutput
46
+ // runs codegen without a typeName option), so it carries the GraphQL Name
47
+ // constraint just like the typed builders' `operationName`.
48
+ session.operationName = spec.typeName ?? DEFAULT_OP_NAME[operation];
49
+ assertGraphqlName(session.operationName, "buildRaw", "typeName");
50
+
51
+ spec.commands.forEach((cmd, i) => {
52
+ try {
53
+ applyCommand(session, schema, cmd);
54
+ } catch (err) {
55
+ const msg = err instanceof Error ? err.message : String(err);
56
+ throw new Error(`command ${i} (${cmd}): ${msg}`);
57
+ }
58
+ });
59
+
60
+ return buildOutput(session, schema, primingNote);
61
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import { buildMutation } from "./build-mutation.js";
8
+ import { type ToolOutput, type UpdateSpec } from "./types.js";
9
+ import { type PrimeDeps } from "../lib/prime-schema.js";
10
+
11
+ /**
12
+ * Build a UIAPI update mutation against a Salesforce org. Implements
13
+ * `sf_gql_update` intent for the graphiti MCP server (FR-5.6). Thin
14
+ * wrapper around `buildMutation` with `op = "Update"`; see that helper
15
+ * for behavior details.
16
+ */
17
+ export async function buildUpdate(spec: UpdateSpec, deps?: PrimeDeps): Promise<ToolOutput> {
18
+ return buildMutation(spec, "Update", deps);
19
+ }