@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,39 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { z } from "zod";
7
+ import { buildRaw } from "../../intent/build-raw.js";
8
+ const inputSchema = {
9
+ org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
10
+ commands: z
11
+ .array(z.string())
12
+ .min(1)
13
+ .describe("CLI-style commands applied in order to a transient session. Supported verbs:\n" +
14
+ " select <path[:alias]> e.g. select uiapi/query/Case/edges/node/Subject/value\n" +
15
+ " set [<path>] <key>=<value> e.g. set uiapi/query/Case first=10 | set uiapi/query/Case where.Status=New\n" +
16
+ " var $name <path> [default] e.g. var $id uiapi/query/Case/@args/where/Id/eq (type inferred from path)\n" +
17
+ 'Each command is tokenized on spaces and a value MUST NOT contain a space — quoting does not help once a token has started (key=\'a b\' still splits). A filter value that contains a space (e.g. "New York", "In Progress") cannot be expressed via set in v1; use sf_gql_list with a JSON filter, or pass the value through a variable bound with var.\n' +
18
+ "Fails fast: a bad command aborts the whole call. Other CLI verbs (cd, drop, alias, optional, unset) are NOT supported in v1."),
19
+ operation: z
20
+ .enum(["query", "mutation", "aggregate"])
21
+ .optional()
22
+ .describe('Operation root. "query" (default) → uiapi.query; "mutation" → mutation root; "aggregate" → uiapi.aggregate.'),
23
+ typeName: z
24
+ .string()
25
+ .optional()
26
+ .describe("Codegen type prefix / operation name override. Defaults to Raw<Operation>."),
27
+ };
28
+ export function registerSfGqlRawTool(server, opts = {}) {
29
+ server.registerTool("sf_gql_raw", {
30
+ description: "Low-level escape hatch: build an arbitrary UIAPI query, mutation, or aggregate from CLI-style commands (select/set/var) when the typed tools don't model the case (cross-union selections, custom mutations). Returns rendered GraphQL, declared variables, generated TypeScript types, and validation warnings. Fails fast on a bad command.",
31
+ inputSchema,
32
+ }, async (args) => {
33
+ const output = await buildRaw(args, opts.primeDeps);
34
+ return {
35
+ content: [{ type: "text", text: JSON.stringify(output) }],
36
+ };
37
+ });
38
+ }
39
+ //# sourceMappingURL=sf-gql-raw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sf-gql-raw.js","sourceRoot":"","sources":["../../../src/mcp/tools/sf-gql-raw.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAOrD,MAAM,WAAW,GAAG;IACnB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oEAAoE,CAAC;IAC9F,QAAQ,EAAE,CAAC;SACT,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACR,gFAAgF;QAC/E,sFAAsF;QACtF,6GAA6G;QAC7G,0GAA0G;QAC1G,2VAA2V;QAC3V,8HAA8H,CAC/H;IACF,SAAS,EAAE,CAAC;SACV,IAAI,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;SACxC,QAAQ,EAAE;SACV,QAAQ,CACR,6GAA6G,CAC7G;IACF,QAAQ,EAAE,CAAC;SACT,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,4EAA4E,CAAC;CACxF,CAAC;AAEF,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,OAA4B,EAAE;IACrF,MAAM,CAAC,YAAY,CAClB,YAAY,EACZ;QACC,WAAW,EACV,+UAA+U;QAChV,WAAW;KACX,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACd,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACpD,OAAO;YACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;SACzD,CAAC;IACH,CAAC,CACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { type PrimeDeps } from "../../lib/prime-schema.js";
8
+ export interface SfGqlUpdateToolOptions {
9
+ primeDeps?: PrimeDeps;
10
+ }
11
+ export declare function registerSfGqlUpdateTool(server: McpServer, opts?: SfGqlUpdateToolOptions): void;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { z } from "zod";
7
+ import { buildUpdate } from "../../intent/build-update.js";
8
+ const inputSchema = {
9
+ org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
10
+ object: z.string().describe('SObject API name, e.g. "Account", "Order".'),
11
+ returnFields: z
12
+ .array(z.string())
13
+ .optional()
14
+ .describe('Scalar field API names to read back on the updated Record. Defaults to ["Id"]. Dot-paths like Owner.Name are not supported in mutation results; passing them produces a warning and the field is skipped.'),
15
+ inputVariable: z
16
+ .string()
17
+ .optional()
18
+ .describe('Variable name (without "$") for the update input. Defaults to "input". Declared as $<name>: <Object>UpdateInput!.'),
19
+ operationName: z
20
+ .string()
21
+ .optional()
22
+ .describe("Override the GraphQL operation name. Defaults to Update<Object>."),
23
+ };
24
+ export function registerSfGqlUpdateTool(server, opts = {}) {
25
+ server.registerTool("sf_gql_update", {
26
+ description: "Build a UIAPI update mutation against a Salesforce org. Declares a typed input variable ($input: <Object>UpdateInput!) and selects return fields on the updated record. Returns rendered GraphQL, declared variables, generated TypeScript types, and validation warnings.",
27
+ inputSchema,
28
+ }, async (args) => {
29
+ const output = await buildUpdate(args, opts.primeDeps);
30
+ return {
31
+ content: [{ type: "text", text: JSON.stringify(output) }],
32
+ };
33
+ });
34
+ }
35
+ //# sourceMappingURL=sf-gql-update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sf-gql-update.js","sourceRoot":"","sources":["../../../src/mcp/tools/sf-gql-update.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAO3D,MAAM,WAAW,GAAG;IACnB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oEAAoE,CAAC;IAC9F,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;IACzE,YAAY,EAAE,CAAC;SACb,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CACR,2MAA2M,CAC3M;IACF,aAAa,EAAE,CAAC;SACd,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACR,mHAAmH,CACnH;IACF,aAAa,EAAE,CAAC;SACd,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,kEAAkE,CAAC;CAC9E,CAAC;AAEF,MAAM,UAAU,uBAAuB,CACtC,MAAiB,EACjB,OAA+B,EAAE;IAEjC,MAAM,CAAC,YAAY,CAClB,eAAe,EACf;QACC,WAAW,EACV,4QAA4Q;QAC7Q,WAAW;KACX,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACd,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACvD,OAAO;YACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;SACzD,CAAC;IACH,CAAC,CACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { z } from "zod";
7
+ /**
8
+ * Zod string validator that enforces the GraphQL Name production. Returned
9
+ * schema is `.describe()`-tagged with the supplied description so it appears
10
+ * in the MCP tool's advertised JSON Schema.
11
+ */
12
+ export declare const graphqlName: (description: string) => z.ZodString;
13
+ export declare const orgAlias: (description: string) => z.ZodString;
14
+ /**
15
+ * `<Object>_OrderBy` shape. UIAPI accepts a singleton object; tools may
16
+ * additionally accept arrays as a compat shim depending on their advertised
17
+ * schema policy (see `sf-gql-list.ts` vs `sf-gql-detail.ts`).
18
+ */
19
+ export declare const orderByObject: z.ZodRecord<z.ZodString, z.ZodUnknown>;
20
+ /**
21
+ * Build a `childRelationships[]` element schema. The `orderBy` schema is a
22
+ * parameter because tools differ on whether they advertise the array shape
23
+ * (`sf_gql_list`: union; `sf_gql_detail`: singleton with array-collapse
24
+ * preprocess). Everything else — `relationshipName`, `fields`, `first`,
25
+ * `filter` — is identical across callers.
26
+ *
27
+ * `relationshipName` flows into a rendered field-path position in the
28
+ * resulting query, so it carries the same GraphQL Name constraint as
29
+ * top-level `object` — same failure mode if it's invalid.
30
+ */
31
+ export declare const childRelationshipSchema: (orderBy: z.ZodTypeAny) => z.ZodObject<{
32
+ relationshipName: z.ZodString;
33
+ fields: z.ZodArray<z.ZodString, "many">;
34
+ first: z.ZodOptional<z.ZodNumber>;
35
+ filter: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
36
+ orderBy: z.ZodOptional<z.ZodTypeAny>;
37
+ }, "strip", z.ZodTypeAny, {
38
+ fields: string[];
39
+ relationshipName: string;
40
+ filter?: Record<string, unknown> | undefined;
41
+ orderBy?: any;
42
+ first?: number | undefined;
43
+ }, {
44
+ fields: string[];
45
+ relationshipName: string;
46
+ filter?: Record<string, unknown> | undefined;
47
+ orderBy?: any;
48
+ first?: number | undefined;
49
+ }>;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { z } from "zod";
7
+ import { GRAPHQL_NAME_RE } from "../../../lib/graphql-name.js";
8
+ /**
9
+ * Zod string validator that enforces the GraphQL Name production. Returned
10
+ * schema is `.describe()`-tagged with the supplied description so it appears
11
+ * in the MCP tool's advertised JSON Schema.
12
+ */
13
+ export const graphqlName = (description) => z.string().regex(GRAPHQL_NAME_RE, "must be a valid GraphQL Name").describe(description);
14
+ /**
15
+ * Org alias / username charset + length, matching `auth.ts`'s
16
+ * `assertValidOrgAlias`. Defense-in-depth at the MCP boundary so the
17
+ * advertised JSON Schema teaches the LLM the constraint upfront, while
18
+ * `assertValidOrgAlias` still gates the shell-out path.
19
+ */
20
+ const ORG_ALIAS_RE = /^[A-Za-z0-9_][A-Za-z0-9_.+@-]{0,252}$/;
21
+ export const orgAlias = (description) => z.string().regex(ORG_ALIAS_RE, "must be a valid org alias or username").describe(description);
22
+ /**
23
+ * `<Object>_OrderBy` shape. UIAPI accepts a singleton object; tools may
24
+ * additionally accept arrays as a compat shim depending on their advertised
25
+ * schema policy (see `sf-gql-list.ts` vs `sf-gql-detail.ts`).
26
+ */
27
+ export const orderByObject = z.record(z.unknown());
28
+ /**
29
+ * Build a `childRelationships[]` element schema. The `orderBy` schema is a
30
+ * parameter because tools differ on whether they advertise the array shape
31
+ * (`sf_gql_list`: union; `sf_gql_detail`: singleton with array-collapse
32
+ * preprocess). Everything else — `relationshipName`, `fields`, `first`,
33
+ * `filter` — is identical across callers.
34
+ *
35
+ * `relationshipName` flows into a rendered field-path position in the
36
+ * resulting query, so it carries the same GraphQL Name constraint as
37
+ * top-level `object` — same failure mode if it's invalid.
38
+ */
39
+ export const childRelationshipSchema = (orderBy) => z.object({
40
+ relationshipName: graphqlName('Child relationship API name, e.g. "Contacts", "Opportunities". Must be a valid GraphQL Name.'),
41
+ fields: z.array(z.string()),
42
+ first: z.number().int().positive().optional(),
43
+ filter: z.record(z.unknown()).optional(),
44
+ orderBy: orderBy.optional(),
45
+ });
46
+ //# sourceMappingURL=zod-schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-schemas.js","sourceRoot":"","sources":["../../../../src/mcp/tools/shared/zod-schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,WAAmB,EAAe,EAAE,CAC/D,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,8BAA8B,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAEzF;;;;;GAKG;AACH,MAAM,YAAY,GAAG,uCAAuC,CAAC;AAC7D,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,WAAmB,EAAe,EAAE,CAC5D,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,uCAAuC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAE/F;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,OAAqB,EAAE,EAAE,CAChE,CAAC,CAAC,MAAM,CAAC;IACR,gBAAgB,EAAE,WAAW,CAC5B,8FAA8F,CAC9F;IACD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IACxC,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@salesforce/graphiti",
3
+ "version": "10.10.2",
4
+ "description": "Progressive GraphQL query builder CLI for Salesforce orgs",
5
+ "license": "SEE LICENSE IN LICENSE.txt",
6
+ "type": "module",
7
+ "bin": {
8
+ "graphiti": "./dist/cli.js",
9
+ "graphiti-mcp": "./dist/mcp/stdio.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "dev": "tsx src/cli.ts",
14
+ "start": "node dist/cli.js",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest",
17
+ "test:coverage": "vitest run --coverage"
18
+ },
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1.26.0",
21
+ "@salesforce/core": "^8.23.4",
22
+ "commander": "^13.1.0",
23
+ "graphql": "^16.10.0",
24
+ "zod": "^3.25.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^22.13.0",
28
+ "@vitest/coverage-v8": "^4.0.6",
29
+ "tsx": "^4.19.0",
30
+ "typescript": "^5.7.0",
31
+ "vitest": "^4.0.6"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ }
36
+ }
package/ralph-loop.sh ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Ralph loop: repeatedly hand a TASK.md file to Claude and let it iterate.
5
+ #
6
+ # All task-specific instructions (phases, rules, priorities, tools, etc.)
7
+ # belong in the task file itself — this script stays generic and just drives
8
+ # the loop.
9
+ #
10
+ # Usage:
11
+ # ./ralph-loop.sh [TASK_FILE] [ITERATIONS]
12
+ #
13
+ # Environment variables:
14
+ # TASK_FILE Path to the task markdown file (default: TASK.md)
15
+ # ITERATIONS Number of iterations to run (default: 100)
16
+ # REFLECTION_FILE Path to the reflection file the agent reads/writes
17
+ # between runs (default: REFLECTION.md)
18
+ # MODEL Claude model to use (default: opus)
19
+ # WORK_DIR Directory to run the loop in (default: script dir)
20
+ # PRE_CMD Optional command to run once before the loop starts
21
+ # ITER_CMD Optional command to run after each iteration
22
+ # LOG_DIR Where to write per-iteration logs
23
+ # (default: $WORK_DIR/ralph-logs)
24
+ # STOP_FILE If the agent creates this file, the loop exits early
25
+ # (default: DONE)
26
+
27
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
28
+
29
+ TASK_FILE="${1:-${TASK_FILE:-TASK.md}}"
30
+ TOTAL="${2:-${ITERATIONS:-100}}"
31
+ REFLECTION_FILE="${REFLECTION_FILE:-REFLECTION.md}"
32
+ MODEL="${MODEL:-opus}"
33
+ WORK_DIR="${WORK_DIR:-$SCRIPT_DIR}"
34
+ LOG_DIR="${LOG_DIR:-$WORK_DIR/ralph-logs}"
35
+ STOP_FILE="${STOP_FILE:-DONE}"
36
+
37
+ cd "$WORK_DIR"
38
+
39
+ if [ ! -f "$TASK_FILE" ]; then
40
+ echo "ERROR: task file not found: $TASK_FILE" >&2
41
+ echo "Create a $TASK_FILE describing what you want the agent to do," >&2
42
+ echo "or pass a path: ./ralph-loop.sh path/to/task.md" >&2
43
+ exit 1
44
+ fi
45
+
46
+ mkdir -p "$LOG_DIR"
47
+
48
+ echo "Ralph loop configuration:"
49
+ echo " Task file: $TASK_FILE"
50
+ echo " Reflection file: $REFLECTION_FILE"
51
+ echo " Iterations: $TOTAL"
52
+ echo " Model: $MODEL"
53
+ echo " Work dir: $WORK_DIR"
54
+ echo " Log dir: $LOG_DIR"
55
+ echo ""
56
+
57
+ if [ -n "${PRE_CMD:-}" ]; then
58
+ echo "Running PRE_CMD: $PRE_CMD"
59
+ bash -c "$PRE_CMD"
60
+ fi
61
+
62
+ for i in $(seq 1 "$TOTAL"); do
63
+ TIMESTAMP=$(date +%Y%m%d-%H%M%S)
64
+ LOG_FILE="$LOG_DIR/run-${i}-${TIMESTAMP}.log"
65
+
66
+ echo ""
67
+ echo "========================================="
68
+ echo " RALPH LOOP — iteration $i / $TOTAL"
69
+ echo " $(date)"
70
+ echo " Log: $LOG_FILE"
71
+ echo "========================================="
72
+ echo ""
73
+
74
+ PROMPT="Read the file $TASK_FILE in this repository and execute it end-to-end. \
75
+ The task file is the source of truth — follow its phases, rules, and priorities exactly. \
76
+ This is iteration $i of $TOTAL. \
77
+ If $REFLECTION_FILE already exists from a previous iteration, read it first and factor in \
78
+ prior learnings — do not repeat the same mistakes, and build on what was already done. \
79
+ Update $REFLECTION_FILE at the end of this iteration with what you learned and what should \
80
+ change next time. \
81
+ Do not ask the user for input at any point — make reasonable decisions and keep going. \
82
+ When all success criteria in the task file are met, create a file named '$STOP_FILE' \
83
+ (just 'touch $STOP_FILE') to signal the loop should stop. Only create it when you are \
84
+ confident the task is fully complete. \
85
+ When you finish, print a concise summary of what you accomplished in this iteration."
86
+
87
+ claude \
88
+ --print \
89
+ --dangerously-skip-permissions \
90
+ --model "$MODEL" \
91
+ --verbose \
92
+ "$PROMPT" \
93
+ 2>&1 | tee "$LOG_FILE"
94
+
95
+ EXIT_CODE=${PIPESTATUS[0]}
96
+
97
+ echo ""
98
+ echo "Iteration $i finished with exit code $EXIT_CODE at $(date)"
99
+ echo "---"
100
+
101
+ if [ "$EXIT_CODE" -ne 0 ]; then
102
+ echo "WARNING: Iteration $i exited non-zero ($EXIT_CODE). Continuing..."
103
+ fi
104
+
105
+ if [ -n "${ITER_CMD:-}" ]; then
106
+ echo "Running ITER_CMD: $ITER_CMD"
107
+ bash -c "$ITER_CMD" || true
108
+ fi
109
+
110
+ if [ -f "$WORK_DIR/$STOP_FILE" ]; then
111
+ echo ""
112
+ echo "Stop file found ($STOP_FILE) — task complete, exiting early after iteration $i."
113
+ break
114
+ fi
115
+ done
116
+
117
+ echo ""
118
+ echo "========================================="
119
+ echo " RALPH LOOP COMPLETE — $TOTAL iterations"
120
+ echo "========================================="
@@ -0,0 +1,190 @@
1
+ #!/bin/bash
2
+ # Smoke test: orderBy support for sf_gql_aggregate
3
+ #
4
+ # Tests a105, a114, a115, a119 from the aggregates assignment.
5
+ # Exits 0 only if all 4 pass. Writes results to stdout and
6
+ # SMOKE-RESULTS-orderby.txt for the ralph loop reflection.
7
+ #
8
+ # Usage: cd packages/graphiti && bash scripts/smoke-orderby.sh
9
+ #
10
+ # Prerequisites:
11
+ # - pnpm build (clean, dist/ up to date)
12
+ # - ebikes org authenticated via sf CLI (only needed for live runs;
13
+ # the tool validates the schema cache, not live data)
14
+
15
+ set -uo pipefail
16
+
17
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
+ WORK_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
19
+ cd "$WORK_DIR"
20
+
21
+ RESULTS_FILE="$WORK_DIR/SMOKE-RESULTS-orderby.txt"
22
+ PASS_COUNT=0
23
+ FAIL_COUNT=0
24
+ DETAILS=""
25
+
26
+ call_tool() {
27
+ local args="$1"
28
+ printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}\n{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"sf_gql_aggregate","arguments":%s}}\n' "$args" \
29
+ | FORCE_COLOR=0 NO_COLOR=1 node dist/mcp/stdio.js 2>/dev/null \
30
+ | sed -n '2p'
31
+ }
32
+
33
+ extract_query() {
34
+ python3 -c "
35
+ import sys, json
36
+ line = sys.stdin.read().strip()
37
+ data = json.loads(line)
38
+ content = data.get('result',{}).get('content',[{}])[0].get('text','')
39
+ is_error = data.get('result',{}).get('isError', False)
40
+ if is_error:
41
+ print('ERROR: ' + content)
42
+ else:
43
+ parsed = json.loads(content)
44
+ print(parsed.get('query',''))
45
+ "
46
+ }
47
+
48
+ normalize() {
49
+ # Strip whitespace differences for comparison
50
+ python3 -c "
51
+ import sys, re
52
+ q = sys.stdin.read().strip()
53
+ # Collapse all whitespace to single spaces
54
+ q = re.sub(r'\s+', ' ', q)
55
+ print(q)
56
+ "
57
+ }
58
+
59
+ check_orderby() {
60
+ local id="$1"
61
+ local input="$2"
62
+ local expected_query="$3"
63
+ local description="$4"
64
+
65
+ local raw_result
66
+ raw_result=$(call_tool "$input")
67
+
68
+ local query
69
+ query=$(echo "$raw_result" | extract_query)
70
+
71
+ local status="FAIL"
72
+ local note=""
73
+
74
+ if echo "$query" | grep -q "^ERROR:"; then
75
+ note="Tool returned error: $query"
76
+ else
77
+ # Normalize both for comparison
78
+ local actual_norm
79
+ actual_norm=$(echo "$query" | normalize)
80
+ local expected_norm
81
+ expected_norm=$(echo "$expected_query" | normalize)
82
+
83
+ if [ "$actual_norm" = "$expected_norm" ]; then
84
+ status="PASS"
85
+ note="Query matches expected output exactly"
86
+ else
87
+ # Check key structural elements even if not exact match
88
+ local has_orderby=false
89
+ local has_function=false
90
+ local has_order=false
91
+
92
+ if echo "$query" | grep -q "orderBy"; then
93
+ has_orderby=true
94
+ fi
95
+ if echo "$query" | grep -q "function:"; then
96
+ has_function=true
97
+ fi
98
+ if echo "$query" | grep -qi "order:.*\(ASC\|DESC\)"; then
99
+ has_order=true
100
+ fi
101
+
102
+ if [ "$has_orderby" = true ] && [ "$has_function" = true ] && [ "$has_order" = true ]; then
103
+ # Has the right shape but doesn't match exactly
104
+ note="Has orderBy with function and order direction but differs from expected.
105
+ Expected: $expected_norm
106
+ Actual: $actual_norm"
107
+ elif [ "$has_orderby" = false ]; then
108
+ note="Missing orderBy clause entirely. Query: $(echo "$query" | head -5)"
109
+ else
110
+ note="orderBy present but missing function or order direction.
111
+ Expected: $expected_norm
112
+ Actual: $actual_norm"
113
+ fi
114
+ fi
115
+ fi
116
+
117
+ if [ "$status" = "PASS" ]; then
118
+ PASS_COUNT=$((PASS_COUNT + 1))
119
+ else
120
+ FAIL_COUNT=$((FAIL_COUNT + 1))
121
+ fi
122
+
123
+ local entry="### $id — $description
124
+ **Status:** $status
125
+ **Input:** $input
126
+ **Note:** $note
127
+ **Query output:**
128
+ \`\`\`graphql
129
+ $query
130
+ \`\`\`
131
+ **Expected:**
132
+ \`\`\`graphql
133
+ $expected_query
134
+ \`\`\`
135
+ "
136
+ DETAILS="${DETAILS}${entry}
137
+ "
138
+ echo "$status: $id — $note"
139
+ }
140
+
141
+ echo "=== OrderBy Smoke Tests ==="
142
+ echo ""
143
+
144
+ # a105: count Leads, groupBy CreatedDate CALENDAR_YEAR, orderBy Description count DESC
145
+ check_orderby \
146
+ "a105_leads_by_created_year_ordered" \
147
+ '{"org":"ebikes","object":"Lead","aggregations":[{"function":"count"}],"groupBy":[{"field":"CreatedDate","function":"CALENDAR_YEAR"}],"orderBy":{"Description":{"function":"COUNT","order":"DESC"}},"operationName":"LeadsByCreatedYearOrdered"}' \
148
+ 'query LeadsByCreatedYearOrdered($after: String) { uiapi { aggregate { Lead(groupBy: { CreatedDate: { function: CALENDAR_YEAR } }, orderBy: { Description: { function: COUNT, order: DESC } }, after: $after) { edges { node { aggregate { CreatedDate { value } countId: Id { count { value } } } } } pageInfo { hasNextPage endCursor } } } } }' \
149
+ "count Leads groupBy CreatedDate CALENDAR_YEAR, orderBy Description count DESC"
150
+
151
+ # a114: count Opportunities, groupBy AccountId, orderBy Description count DESC
152
+ check_orderby \
153
+ "a114_opps_by_account_ordered_by_count" \
154
+ '{"org":"ebikes","object":"Opportunity","aggregations":[{"function":"count"}],"groupBy":["AccountId"],"orderBy":{"Description":{"function":"COUNT","order":"DESC"}},"operationName":"OppsByAccountOrdered"}' \
155
+ 'query OppsByAccountOrdered($after: String) { uiapi { aggregate { Opportunity(groupBy: { AccountId: { group: true } }, orderBy: { Description: { function: COUNT, order: DESC } }, after: $after) { edges { node { aggregate { AccountId { value } countId: Id { count { value } } } } } pageInfo { hasNextPage endCursor } } } } }' \
156
+ "count Opportunities groupBy AccountId, orderBy Description count DESC"
157
+
158
+ # a115: count Cases, groupBy Priority, orderBy Description count ASC
159
+ check_orderby \
160
+ "a115_cases_by_priority_ordered_by_count" \
161
+ '{"org":"ebikes","object":"Case","aggregations":[{"function":"count"}],"groupBy":["Priority"],"orderBy":{"Description":{"function":"COUNT","order":"ASC"}},"operationName":"CasesByPriorityOrdered"}' \
162
+ 'query CasesByPriorityOrdered($after: String) { uiapi { aggregate { Case(groupBy: { Priority: { group: true } }, orderBy: { Description: { function: COUNT, order: ASC } }, after: $after) { edges { node { aggregate { Priority { value } countId: Id { count { value } } } } } pageInfo { hasNextPage endCursor } } } } }' \
163
+ "count Cases groupBy Priority, orderBy Description count ASC"
164
+
165
+ # a119: count Accounts, groupBy Industry + OwnerId, orderBy Description count DESC
166
+ check_orderby \
167
+ "a119_accounts_by_industry_and_owner_ordered" \
168
+ '{"org":"ebikes","object":"Account","aggregations":[{"function":"count"}],"groupBy":["Industry","OwnerId"],"orderBy":{"Description":{"function":"COUNT","order":"DESC"}},"operationName":"AccountsByIndustryOwnerOrdered"}' \
169
+ 'query AccountsByIndustryOwnerOrdered($after: String) { uiapi { aggregate { Account(groupBy: { Industry: { group: true }, OwnerId: { group: true } }, orderBy: { Description: { function: COUNT, order: DESC } }, after: $after) { edges { node { aggregate { Industry { value } OwnerId { value } countId: Id { count { value } } } } } pageInfo { hasNextPage endCursor } } } } }' \
170
+ "count Accounts groupBy Industry + OwnerId, orderBy Description count DESC"
171
+
172
+ echo ""
173
+ echo "=== Summary: $PASS_COUNT PASS, $FAIL_COUNT FAIL ==="
174
+
175
+ # Write results file for reflection
176
+ cat > "$RESULTS_FILE" <<EOF
177
+ # Smoke Test Results — OrderBy
178
+ # Run: $(date)
179
+ # Pass: $PASS_COUNT / 4
180
+ # Fail: $FAIL_COUNT / 4
181
+
182
+ $DETAILS
183
+ EOF
184
+
185
+ echo "Results written to $RESULTS_FILE"
186
+
187
+ if [ "$FAIL_COUNT" -gt 0 ]; then
188
+ exit 1
189
+ fi
190
+ exit 0
@@ -0,0 +1,46 @@
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 path from "node:path";
8
+ import { type GraphQLSchema, introspectionFromSchema } from "graphql";
9
+ import { atomicWriteJson } from "../../lib/fs-utils.js";
10
+ import {
11
+ schemaCacheKeyForInstanceUrl,
12
+ schemaDir,
13
+ type SchemaMetadata,
14
+ } from "../../lib/introspect.js";
15
+ import { type PrimeDeps } from "../../lib/prime-schema.js";
16
+
17
+ /**
18
+ * Build a `PrimeDeps` whose `downloadSchema` writes the supplied schema to
19
+ * `schemaDir()` — redirected to a tmpdir by the global test setup — so
20
+ * intent-layer tests exercise the priming hook without keychain or network
21
+ * access. Mirrors the real `downloadSchema` contract.
22
+ */
23
+ export function makeNoopPrimeDeps(org: string, orgUrl: string, schema: GraphQLSchema): PrimeDeps {
24
+ return {
25
+ getOrgAuth: async () => ({
26
+ alias: org,
27
+ username: "u",
28
+ instanceUrl: orgUrl,
29
+ accessToken: "t",
30
+ orgId: "00D",
31
+ }),
32
+ downloadSchema: async (auth) => {
33
+ const cacheKey = schemaCacheKeyForInstanceUrl(auth.instanceUrl);
34
+ const filePath = path.join(schemaDir(), `${cacheKey}.json`);
35
+ atomicWriteJson(filePath, { data: introspectionFromSchema(schema) });
36
+ const meta: SchemaMetadata = {
37
+ cacheKey,
38
+ instanceUrl: auth.instanceUrl,
39
+ typeCount: 0,
40
+ downloadedAt: new Date().toISOString(),
41
+ filePath,
42
+ };
43
+ return meta;
44
+ },
45
+ };
46
+ }
@@ -0,0 +1,73 @@
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 { buildSchema } from "graphql";
8
+ import { createSession } from "../../lib/session.js";
9
+ import { primeSchemaCache } from "../../lib/walker.js";
10
+
11
+ /**
12
+ * Test schema covering the union of types referenced across legacy graphiti
13
+ * test files. Intentionally minimal — extend only when new tests need new types.
14
+ */
15
+ export const TEST_SCHEMA = buildSchema(`
16
+ type Query {
17
+ viewer: Viewer!
18
+ accounts(first: Int, where: AccountFilter): AccountConnection!
19
+ search: [SearchHit!]!
20
+ }
21
+
22
+ type Mutation {
23
+ createAccount(input: CreateAccountInput!): Account!
24
+ }
25
+
26
+ type Viewer {
27
+ id: ID!
28
+ name: String!
29
+ }
30
+
31
+ input AccountFilter {
32
+ minRevenue: Int
33
+ name: StringFilter
34
+ }
35
+
36
+ input StringFilter {
37
+ eq: String
38
+ like: String
39
+ }
40
+
41
+ input CreateAccountInput {
42
+ name: String!
43
+ }
44
+
45
+ type AccountConnection {
46
+ edges: [AccountEdge!]!
47
+ }
48
+
49
+ type AccountEdge {
50
+ node: Account!
51
+ }
52
+
53
+ type Account {
54
+ id: ID!
55
+ name: String!
56
+ annualRevenue: Float
57
+ }
58
+
59
+ type Contact {
60
+ id: ID!
61
+ name: String!
62
+ email: String
63
+ }
64
+
65
+ union SearchHit = Account | Contact
66
+ `);
67
+
68
+ export function makeSession(operation: "query" | "mutation" = "query") {
69
+ const alias = `test_${Math.random().toString(16).slice(2, 8)}`;
70
+ const instanceUrl = `https://${alias}.my.salesforce.com`;
71
+ primeSchemaCache(instanceUrl, TEST_SCHEMA);
72
+ return createSession(alias, operation, instanceUrl);
73
+ }