@salesforce/graphiti 11.26.0 → 11.26.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.
package/AGENT_GUIDE.md CHANGED
@@ -37,6 +37,7 @@ Notes:
37
37
 
38
38
  - Concurrent refreshes (CLI + MCP) **coalesce** into a single introspection.
39
39
  - If the refresh fails (network/5xx) it is retried once. On terminal failure the **old cache is kept** and `sf_gql_connect` returns `refreshed: false` with a staleness `warnings[]` entry instead of erroring — keep working on the still-valid cached schema and retry the refresh shortly.
40
+ - Exception: a **401/403 auth failure** during introspection (expired/unauthorized session) errors with an `Auth:` prefix instead of the soft staleness warning — a dead session makes even the cached schema unusable, so re-authenticate (`sf org login web --alias <org>`) rather than retrying.
40
41
  - ObjectInfo is keyed by org alias: refreshing alias `A` does not invalidate ObjectInfo cached under a different alias `B` for the same org (its 1-hour TTL heals it).
41
42
 
42
43
  ## Session Resolution
package/CHANGELOG.md CHANGED
@@ -3,6 +3,16 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [11.26.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.26.1...v11.26.2) (2026-07-14)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **graphiti:** @W-23335328@ reclassify introspection 401/403 as Auth not Schema ([#739](https://github.com/salesforce-experience-platform-emu/webapps/issues/739)) ([d13ebbf](https://github.com/salesforce-experience-platform-emu/webapps/commit/d13ebbfa928cc60b822c03ebf6daaaf0a2f35aff))
11
+
12
+ ## [11.26.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.26.0...v11.26.1) (2026-07-14)
13
+
14
+ **Note:** Version bump only for package @salesforce/graphiti
15
+
6
16
  ## [11.26.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.25.0...v11.26.0) (2026-07-14)
7
17
 
8
18
  **Note:** Version bump only for package @salesforce/graphiti
@@ -3,14 +3,15 @@
3
3
  * All rights reserved.
4
4
  * For full license text, see the LICENSE.txt file
5
5
  */
6
+ import { AuthError } from "../../lib/errors.js";
6
7
  import { SchemaRefreshError } from "../../lib/prime-schema.js";
7
8
  /**
8
9
  * Best-effort classification of a plain `Error` message into a more specific
9
10
  * code. The intent/auth layers throw plain `Error`s with no type signal, so the
10
11
  * only available hint is the message text. This is a NON-CONTRACT heuristic: a
11
12
  * reworded upstream message can fall through to `INTERNAL`. The verbatim message
12
- * is always preserved either way, and the typed `SchemaRefreshError` signal
13
- * (checked before this) takes precedence. Patterns mirror the MCP-CLI prior art.
13
+ * is always preserved either way, and the typed `AuthError` / `SchemaRefreshError`
14
+ * signals (checked before this) take precedence. Patterns mirror the MCP-CLI prior art.
14
15
  */
15
16
  function classifyErrorMessage(message) {
16
17
  if (/\bauth(?:entication|orization)?\b/i.test(message) ||
@@ -83,8 +84,8 @@ export async function runMirror(jsonArg, schema, build, deps = {}) {
83
84
  emitError("INVALID_ARGS", "Input failed schema validation.", result.error.issues);
84
85
  return;
85
86
  }
86
- // 4. Build and emit. Classify failures: the typed SchemaRefreshError signal
87
- // first, then a best-effort message regex (AUTH_FAILED / SCHEMA_PRIME_FAILED),
87
+ // 4. Build and emit. Classify failures: the typed AuthError / SchemaRefreshError
88
+ // signals first, then a best-effort message regex (AUTH_FAILED / SCHEMA_PRIME_FAILED),
88
89
  // else INTERNAL. The verbatim message is always preserved.
89
90
  try {
90
91
  const output = await build(result.data);
@@ -92,8 +93,17 @@ export async function runMirror(jsonArg, schema, build, deps = {}) {
92
93
  }
93
94
  catch (err) {
94
95
  const message = err instanceof Error ? err.message : String(err);
95
- // Typed signal wins; otherwise fall back to the best-effort message regex.
96
- const code = err instanceof SchemaRefreshError ? "SCHEMA_PRIME_FAILED" : classifyErrorMessage(message);
96
+ // Typed signals win; otherwise fall back to the best-effort message regex.
97
+ // AuthError is checked first because its message (W-23335328) reads "Schema
98
+ // priming failed … expired or unauthorized. Re-authenticate …" — that
99
+ // "priming" makes classifyErrorMessage return SCHEMA_PRIME_FAILED, so an
100
+ // untyped fallthrough would misclassify a 401/403 introspection failure as a
101
+ // schema problem (the exact Schema-vs-Auth confusion this WI fixes).
102
+ const code = err instanceof AuthError
103
+ ? "AUTH_FAILED"
104
+ : err instanceof SchemaRefreshError
105
+ ? "SCHEMA_PRIME_FAILED"
106
+ : classifyErrorMessage(message);
97
107
  // The stack is omitted by default so the envelope never leaks internals;
98
108
  // GRAPHITI_DEBUG=1 opts into attaching it under `details` for debugging.
99
109
  const details = process.env.GRAPHITI_DEBUG === "1" && err instanceof Error && err.stack
@@ -1 +1 @@
1
- {"version":3,"file":"run-mirror.js","sourceRoot":"","sources":["../../../src/commands/mcp-mirror/run-mirror.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-mirror.js","sourceRoot":"","sources":["../../../src/commands/mcp-mirror/run-mirror.ts"],"names":[],"mappings":"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"}
@@ -55,6 +55,28 @@ export type RetryHint = "now" | "backoff" | "no";
55
55
  * is a defensive backstop, not a live path; a throw simply falls back to `"no"`.
56
56
  */
57
57
  export declare function classifyCause(cause: unknown): RetryHint;
58
+ /**
59
+ * Detect whether an introspection/download `cause` is a 401/403-class auth failure
60
+ * that should surface as `Auth:` (re-authenticate) rather than `Schema:` (W-23335328).
61
+ *
62
+ * Mirrors {@link classifyCause}'s defensive shape-reading. A real jsforce /
63
+ * `@salesforce/core` HTTP failure carries a string `errorCode`/`name` — jsforce-node's
64
+ * `HttpApiError` sets `ERROR_HTTP_401`/`ERROR_HTTP_403` for a status-only failure, or
65
+ * the body's code (e.g. `INVALID_SESSION_ID`) for an expired session — but NOT a numeric
66
+ * `statusCode`; so the `ERROR_HTTP_<nnn>` regex on `errorCode`/`name` and the
67
+ * `AUTH_ERROR_CODES` check are the load-bearing paths, and the `statusCode` branch is a
68
+ * defensive fallback for other cause shapes (and the contract tests' status-bearing
69
+ * doubles). Every property read is wrapped so a `cause` with a throwing accessor falls
70
+ * back to `false` (this runs on the priming-failure path; an escaped throw would drop the
71
+ * classification). Returns false for a null/undefined/non-object cause and for any
72
+ * non-auth status — we never over-broaden: a 4xx that is not 401/403 stays `Schema:`.
73
+ *
74
+ * Note (W-23148365 N3): callers must key the Auth reclassification off THIS cause-shape
75
+ * inspection, never off retry-token absence or schema-cache survival — a forced refresh
76
+ * that keeps a usable cache emits no token regardless of the cause, so "no token" is not a
77
+ * reliable signal that a 401/403 occurred.
78
+ */
79
+ export declare function isAuthError(cause: unknown): boolean;
58
80
  /** Credential/auth resolution failure (e.g. unknown org, expired token). → `Auth:` */
59
81
  export declare class AuthError extends Error {
60
82
  constructor(message: string, opts?: {
@@ -74,7 +74,11 @@ export function classifyCause(cause) {
74
74
  }
75
75
  const codeStr = typeof c.errorCode === "string" ? c.errorCode : "";
76
76
  const nameStr = typeof c.name === "string" ? c.name : "";
77
- const httpMatch = /ERROR_HTTP_(\d{3})/.exec(`${codeStr} ${nameStr}`);
77
+ // `(?!\d)` anchors the status to EXACTLY three digits: without it the greedy
78
+ // `\d{3}` would capture the first three digits of a 4-digit tail (e.g. a
79
+ // hypothetical `ERROR_HTTP_5001` → `500`), misreading the class. jsforce only
80
+ // emits canonical 3-digit codes today, so this is defensive hardening.
81
+ const httpMatch = /ERROR_HTTP_(\d{3})(?!\d)/.exec(`${codeStr} ${nameStr}`);
78
82
  if (httpMatch) {
79
83
  const httpStatus = Number(httpMatch[1]);
80
84
  if (TRANSIENT_STATUS.has(httpStatus))
@@ -98,6 +102,71 @@ export function classifyCause(cause) {
98
102
  }
99
103
  return "no";
100
104
  }
105
+ // HTTP statuses that mean the introspection POST failed to AUTHENTICATE/authorize
106
+ // (W-23335328): the org session is missing, expired, or lacks access. The fix is
107
+ // to re-authenticate, so these route to `Auth:` — not `Schema:` (re-prime) — at the
108
+ // MCP boundary. A subset of PERMANENT_STATUS, so they stay non-retryable too.
109
+ const AUTH_STATUS = new Set([401, 403]);
110
+ // Salesforce error codes that ARE a 401 in disguise. jsforce-node collapses a 4xx
111
+ // whose body parses as a Salesforce error array into an HttpApiError whose
112
+ // `errorCode`/`name` is the body code (e.g. INVALID_SESSION_ID for an expired
113
+ // session) and sets NO numeric statusCode — so the canonical expired-session case
114
+ // never presents a 401 status here and must be matched by code. Deliberately kept
115
+ // to the session-invalid code we can tie authoritatively to "re-authenticate fixes
116
+ // it". A structured 403 body code such as INSUFFICIENT_ACCESS / API_DISABLED_FOR_ORG
117
+ // is intentionally NOT here: those are org permission/config problems that a fresh
118
+ // login does NOT resolve, so routing them to `Auth:` ("re-authenticate") would
119
+ // misguide the agent — they correctly stay `Schema:`. Extend only with codes where
120
+ // re-auth is the right remedy (e.g. INVALID_AUTH_HEADER, MISSING_OAUTH_TOKEN) as
121
+ // they are observed on this path.
122
+ const AUTH_ERROR_CODES = new Set(["INVALID_SESSION_ID"]);
123
+ /**
124
+ * Detect whether an introspection/download `cause` is a 401/403-class auth failure
125
+ * that should surface as `Auth:` (re-authenticate) rather than `Schema:` (W-23335328).
126
+ *
127
+ * Mirrors {@link classifyCause}'s defensive shape-reading. A real jsforce /
128
+ * `@salesforce/core` HTTP failure carries a string `errorCode`/`name` — jsforce-node's
129
+ * `HttpApiError` sets `ERROR_HTTP_401`/`ERROR_HTTP_403` for a status-only failure, or
130
+ * the body's code (e.g. `INVALID_SESSION_ID`) for an expired session — but NOT a numeric
131
+ * `statusCode`; so the `ERROR_HTTP_<nnn>` regex on `errorCode`/`name` and the
132
+ * `AUTH_ERROR_CODES` check are the load-bearing paths, and the `statusCode` branch is a
133
+ * defensive fallback for other cause shapes (and the contract tests' status-bearing
134
+ * doubles). Every property read is wrapped so a `cause` with a throwing accessor falls
135
+ * back to `false` (this runs on the priming-failure path; an escaped throw would drop the
136
+ * classification). Returns false for a null/undefined/non-object cause and for any
137
+ * non-auth status — we never over-broaden: a 4xx that is not 401/403 stays `Schema:`.
138
+ *
139
+ * Note (W-23148365 N3): callers must key the Auth reclassification off THIS cause-shape
140
+ * inspection, never off retry-token absence or schema-cache survival — a forced refresh
141
+ * that keeps a usable cache emits no token regardless of the cause, so "no token" is not a
142
+ * reliable signal that a 401/403 occurred.
143
+ */
144
+ export function isAuthError(cause) {
145
+ if (typeof cause !== "object" || cause === null)
146
+ return false;
147
+ const c = cause;
148
+ try {
149
+ const status = typeof c.statusCode === "number" ? c.statusCode : undefined;
150
+ if (status !== undefined && AUTH_STATUS.has(status))
151
+ return true;
152
+ const codeStr = typeof c.errorCode === "string" ? c.errorCode : "";
153
+ const nameStr = typeof c.name === "string" ? c.name : "";
154
+ // `(?!\d)` anchors to EXACTLY three digits so a 4-digit tail can't be misread
155
+ // as a 401/403 (e.g. `ERROR_HTTP_4011` would otherwise capture `401` and
156
+ // wrongly route to Auth). Mirrors the same guard in classifyCause.
157
+ const httpMatch = /ERROR_HTTP_(\d{3})(?!\d)/.exec(`${codeStr} ${nameStr}`);
158
+ if (httpMatch && AUTH_STATUS.has(Number(httpMatch[1])))
159
+ return true;
160
+ if (AUTH_ERROR_CODES.has(codeStr) || AUTH_ERROR_CODES.has(nameStr))
161
+ return true;
162
+ }
163
+ catch {
164
+ // A throwing getter on the cause → treat as not-an-auth-error (fall through
165
+ // to the existing Schema classification, which is the conservative default).
166
+ return false;
167
+ }
168
+ return false;
169
+ }
101
170
  /** Credential/auth resolution failure (e.g. unknown org, expired token). → `Auth:` */
102
171
  export class AuthError extends Error {
103
172
  constructor(message, opts) {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  import fs from "node:fs";
7
7
  import path from "node:path";
8
8
  import { getOrgAuth as realGetOrgAuth } from "./auth.js";
9
- import { classifyCause, SchemaError } from "./errors.js";
9
+ import { AuthError, classifyCause, isAuthError, SchemaError } from "./errors.js";
10
10
  import { downloadSchema as realDownloadSchema, getSchemaMetadata, normalizeInstanceUrl, schemaCacheKeyForInstanceUrl, schemaDir, } from "./introspect.js";
11
11
  import { clearObjectInfoCache } from "./object-info.js";
12
12
  import { clearSchemaCacheByUrl } from "./walker.js";
@@ -240,6 +240,23 @@ export async function primeSchemaWithLock(orgAlias, deps = REAL_DEPS, opts = {})
240
240
  meta = await deps.downloadSchema(auth);
241
241
  }
242
242
  catch (cause) {
243
+ // A 401/403 from the introspection POST is an AUTH failure, not a
244
+ // schema/cache problem (W-23335328): surface it as AuthError so the MCP
245
+ // boundary emits `Auth:` (re-authenticate) and the agent re-auths instead
246
+ // of futilely re-priming. Applies to BOTH lazy prime and forced refresh,
247
+ // and — per the W-23148365 N3 note — is keyed off the cause SHAPE
248
+ // (isAuthError), never off retry-token absence or cache survival. On a
249
+ // forced refresh this INTENTIONALLY bypasses the SchemaRefreshError
250
+ // soft-surviving-cache path below: a cached schema is worthless once the
251
+ // session is dead (every subsequent live query 401s too), so a hard Auth
252
+ // error — not a silent soft-success warning — is the correct signal, and
253
+ // it is what makes the 401/403 actually route to `Auth:` rather than a
254
+ // non-erroring `refreshed:false` return. AuthError carries no retry token
255
+ // (Auth is uniformly non-retryable), preserving the existing contract.
256
+ if (isAuthError(cause)) {
257
+ const msg = cause instanceof Error ? cause.message : String(cause);
258
+ throw new AuthError(`Schema priming failed for "${orgAlias}" — the org session is expired or unauthorized. Re-authenticate with \`sf org login web --alias ${orgAlias}\`.\n${msg}`, { cause });
259
+ }
243
260
  // Lazy prime (no existing cache): no stale cache to keep, so surface
244
261
  // the underlying failure (FR-13.5/13.6). Wrap untyped causes (e.g. a
245
262
  // raw @salesforce/core/jsforce network error from connection.request)
@@ -1 +1 @@
1
- {"version":3,"file":"prime-schema.js","sourceRoot":"","sources":["../../src/lib/prime-schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prime-schema.js","sourceRoot":"","sources":["../../src/lib/prime-schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,IAAI,cAAc,EAAgB,MAAM,WAAW,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAkB,WAAW,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EACN,cAAc,IAAI,kBAAkB,EACpC,iBAAiB,EACjB,oBAAoB,EACpB,4BAA4B,EAC5B,SAAS,GAET,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEpD,wEAAwE;AACxE,yEAAyE;AACzE,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,uEAAuE;AACvE,uEAAuE;AACvE,wEAAwE;AACxE,+DAA+D;AAC/D,MAAM,aAAa,GAAG,CAAC,GAAG,MAAM,CAAC;AACjC,MAAM,OAAO,GAAG,GAAG,CAAC;AACpB,MAAM,WAAW,GAAG,CAAC,GAAG,MAAM,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,SAAiB,EACjB,IAAsB,EACtB,OAAmC,EAAE;IAErC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAG,GAAG,SAAS,OAAO,CAAC;IACrC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3D,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACpC,OAAO,IAAI,EAAE,CAAC;QACb,IAAI,CAAC;YACJ,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,gCAAgC;YACxD,MAAM;QACP,CAAC;QAAC,OAAO,CAAU,EAAE,CAAC;YACrB,MAAM,GAAG,GAAG,CAA0B,CAAC;YACvC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,CAAC,CAAC;YAEnC,6EAA6E;YAC7E,IAAI,IAAI,EAAE;gBAAE,OAAO,SAAS,CAAC;YAE7B,gEAAgE;YAChE,iEAAiE;YACjE,iEAAiE;YACjE,6DAA6D;YAC7D,8DAA8D;YAC9D,0CAA0C;YAC1C,IAAI,CAAC;gBACJ,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACnC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,GAAG,aAAa,EAAE,CAAC;oBAC/C,sDAAsD;oBACtD,wDAAwD;oBACxD,uDAAuD;oBACvD,mCAAmC;oBACnC,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;oBACzC,SAAS;gBACV,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,gEAAgE;YACjE,CAAC;YAED,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,GAAG,WAAW,EAAE,CAAC;gBACjD,kEAAkE;gBAClE,qEAAqE;gBACrE,qEAAqE;gBACrE,oEAAoE;gBACpE,MAAM,IAAI,WAAW,CACpB,qBAAqB,WAAW,iCAAiC,QAAQ,EAAE,EAC3E,EAAE,KAAK,EAAE,KAAK,EAAE,CAChB,CAAC;YACH,CAAC;YACD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QAClD,CAAC;IACF,CAAC;IAED,4EAA4E;IAC5E,wEAAwE;IACxE,oCAAoC;IACpC,IAAI,IAAI,EAAE,EAAE,CAAC;QACZ,IAAI,CAAC;YACJ,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACR,cAAc;QACf,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACJ,OAAO,MAAM,IAAI,EAAE,CAAC;IACrB,CAAC;YAAS,CAAC;QACV,mEAAmE;QACnE,iEAAiE;QACjE,6DAA6D;QAC7D,IAAI,CAAC;YACJ,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACR,uBAAuB;QACxB,CAAC;IACF,CAAC;AACF,CAAC;AAmBD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC5C,UAAU,CAAU;IACpB,WAAW,CAAS;IACX,KAAK,CAAY;IAC1B,YACC,OAAe,EACf,IAAsF;QAEtF,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC7E,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QAClC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACpC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC;IACjC,CAAC;CACD;AAED,SAAS,SAAS,CAAC,EAAU;IAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;IACpC,IAAI,GAAG,GAAG,CAAC;QAAE,OAAO,UAAU,CAAC;IAC/B,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,GAAG,GAAG,OAAO,CAAC;IACnC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;IAChC,IAAI,EAAE,GAAG,EAAE;QAAE,OAAO,GAAG,EAAE,OAAO,CAAC;IACjC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC;AACtC,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAgB,EAAE,SAAgC;IAC5E,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,OAAO,uBAAuB,QAAQ,kGAAkG,QAAQ,WAAW,CAAC;IAC7J,CAAC;IACD,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/E,OAAO,uBAAuB,QAAQ,8DAA8D,GAAG,sEAAsE,CAAC;AAC/K,CAAC;AAWD,MAAM,SAAS,GAAc;IAC5B,UAAU,EAAE,cAAc;IAC1B,cAAc,EAAE,kBAAkB;CAClC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,QAAgB,EAChB,OAAkB,SAAS,EAC3B,OAAmC,EAAE;IAErC,uEAAuE;IACvE,0EAA0E;IAC1E,oEAAoE;IACpE,8EAA8E;IAC9E,0CAA0C;IAC1C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,oBAAoB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,4BAA4B,CAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,GAAG,QAAQ,OAAO,CAAC,CAAC;IAE5D,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;IAEzC,mEAAmE;IACnE,sEAAsE;IACtE,oEAAoE;IACpE,yEAAyE;IACzE,oEAAoE;IACpE,4EAA4E;IAC5E,2EAA2E;IAC3E,gFAAgF;IAChF,MAAM,cAAc,GAAG,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEpF,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;QAC9C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC;IACjF,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,SAA6B,CAAC;IAClC,MAAM,cAAc,CACnB,QAAQ,EACR,KAAK,IAAI,EAAE;QACV,IAAI,IAAoB,CAAC;QACzB,IAAI,CAAC;YACJ,iEAAiE;YACjE,4DAA4D;YAC5D,oEAAoE;YACpE,WAAW;YACX,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,kEAAkE;YAClE,wEAAwE;YACxE,0EAA0E;YAC1E,yEAAyE;YACzE,kEAAkE;YAClE,uEAAuE;YACvE,oEAAoE;YACpE,yEAAyE;YACzE,yEAAyE;YACzE,yEAAyE;YACzE,uEAAuE;YACvE,0EAA0E;YAC1E,uEAAuE;YACvE,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxB,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACnE,MAAM,IAAI,SAAS,CAClB,8BAA8B,QAAQ,mGAAmG,QAAQ,QAAQ,GAAG,EAAE,EAC9J,EAAE,KAAK,EAAE,CACT,CAAC;YACH,CAAC;YACD,qEAAqE;YACrE,qEAAqE;YACrE,sEAAsE;YACtE,oEAAoE;YACpE,qEAAqE;YACrE,mEAAmE;YACnE,oEAAoE;YACpE,iDAAiD;YACjD,IAAI,CAAC,YAAY,EAAE,CAAC;gBACnB,IAAI,KAAK,YAAY,WAAW;oBAAE,MAAM,KAAK,CAAC;gBAC9C,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACnE,MAAM,IAAI,WAAW,CAAC,8BAA8B,QAAQ,MAAM,GAAG,EAAE,EAAE;oBACxE,KAAK;oBACL,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC;iBAC3B,CAAC,CAAC;YACJ,CAAC;YACD,8DAA8D;YAC9D,iEAAiE;YACjE,aAAa;YACb,MAAM,SAAS,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;YACjD,qEAAqE;YACrE,uEAAuE;YACvE,yEAAyE;YACzE,mEAAmE;YACnE,0EAA0E;YAC1E,yEAAyE;YACzE,6EAA6E;YAC7E,MAAM,IAAI,kBAAkB,CAAC,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE;gBACpE,UAAU,EAAE,SAAS,EAAE,YAAY;gBACnC,WAAW;gBACX,KAAK;gBACL,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC;aAC3B,CAAC,CAAC;QACJ,CAAC;QACD,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAC3B,mEAAmE;QACnE,oEAAoE;QACpE,8DAA8D;QAC9D,EAAE;QACF,qEAAqE;QACrE,wEAAwE;QACxE,sEAAsE;QACtE,sEAAsE;QACtE,qEAAqE;QACrE,wEAAwE;QACxE,oEAAoE;QACpE,oDAAoD;QACpD,IAAI,YAAY,EAAE,CAAC;YAClB,qBAAqB,CAAC,WAAW,CAAC,CAAC;YACnC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;QACD,MAAM,GAAG,IAAI,CAAC;IACf,CAAC,EACD,YAAY;QACX,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,GAAG,cAAc,EAAE;QAC7F,CAAC,CAAC,SAAS,CACZ,CAAC;IAEF,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,sEAAsE;QACtE,sEAAsE;QACtE,yEAAyE;QACzE,uEAAuE;QACvE,mEAAmE;QACnE,IAAI,YAAY,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,GAAG,cAAc,EAAE,CAAC;YAC/F,qBAAqB,CAAC,WAAW,CAAC,CAAC;YACnC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;QACD,OAAO;YACN,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,KAAK;YAChB,QAAQ;YACR,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;YAC9B,WAAW;SACX,CAAC;IACH,CAAC;IACD,OAAO;QACN,MAAM,EAAE,KAAK;QACb,SAAS,EAAE,YAAY;QACvB,QAAQ;QACR,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;QAC9B,WAAW;QACX,SAAS;KACT,CAAC;AACH,CAAC"}
@@ -9,7 +9,7 @@ import { runTool } from "../../schemas/tool-adapter.js";
9
9
  const inputSchema = CONNECT_INPUT.shape;
10
10
  export function registerSfGqlConnectTool(server, opts = {}) {
11
11
  server.registerTool("sf_gql_connect", {
12
- description: "Connect to a Salesforce org and prime its GraphQL schema cache. With forceRefresh, re-download the schema and coherently clear all caches so subsequent tools see freshly-deployed metadata. Concurrent refreshes coalesce into a single introspection. Returns { org, instanceUrl, refreshed, cached, durationMs, warnings? } — not the standard ToolOutput envelope. If a refresh fails but a usable cached schema survives, returns refreshed:false with a staleness warning instead of erroring. Error convention (all sf_gql_* tools): on failure the isError text is prefixed with a category — `UserInput:` (fix the request), `Auth:` (re-authenticate the org), `Schema:` (introspection/cache problem), or `Internal:` (unexpected) — so you can decide whether to fix inputs, re-auth, or retry. A `Schema:` error additionally ends with a retryability token: `[retry=now]` (retry immediately — e.g. a priming-lock timeout, where no live org round-trip occurred) or `[retry=backoff]` (the org was unreachable and an automatic retry already failed — wait with increasing backoff, e.g. 2^n seconds capped around 8s, and retry serially; do not fan out concurrent retries against an org that is already failing, and note some conditions such as an org API rate limit may take longer than a few seconds to clear); ABSENCE of any `[retry=...]` token means the failure is permanent (404, missing/malformed `__schema`, GraphQL errors in the body, or no cached schema) — do not retry, fix the request or re-prime via sf_gql_connect. This token convention applies to every sf_gql_* tool, not just this one. Example error text: `Schema: introspection request failed [retry=backoff]`; extract the disposition with the end-anchored regex ` /\\s\\[retry=(now|backoff)\\]$/` (no match ⇒ permanent).",
12
+ description: "Connect to a Salesforce org and prime its GraphQL schema cache. With forceRefresh, re-download the schema and coherently clear all caches so subsequent tools see freshly-deployed metadata. Concurrent refreshes coalesce into a single introspection. Returns { org, instanceUrl, refreshed, cached, durationMs, warnings? } — not the standard ToolOutput envelope. If a refresh fails transiently (network/5xx) but a usable cached schema survives, returns refreshed:false with a staleness warning instead of erroring; a 401/403 auth failure during refresh instead errors with `Auth:` (a dead session makes the cached schema unusable — re-authenticate). Error convention (all sf_gql_* tools): on failure the isError text is prefixed with a category — `UserInput:` (fix the request), `Auth:` (re-authenticate the org — e.g. an expired/unauthorized session on connect or introspection), `Schema:` (introspection/cache problem), or `Internal:` (unexpected) — so you can decide whether to fix inputs, re-auth, or retry. A `Schema:` error additionally ends with a retryability token: `[retry=now]` (retry immediately — e.g. a priming-lock timeout, where no live org round-trip occurred) or `[retry=backoff]` (the org was unreachable and an automatic retry already failed — wait with increasing backoff, e.g. 2^n seconds capped around 8s, and retry serially; do not fan out concurrent retries against an org that is already failing, and note some conditions such as an org API rate limit may take longer than a few seconds to clear); ABSENCE of any `[retry=...]` token means the failure is permanent (404, missing/malformed `__schema`, GraphQL errors in the body, or no cached schema) — do not retry, fix the request or re-prime via sf_gql_connect. This token convention applies to every sf_gql_* tool, not just this one. Example error text: `Schema: introspection request failed [retry=backoff]`; extract the disposition with the end-anchored regex ` /\\s\\[retry=(now|backoff)\\]$/` (no match ⇒ permanent).",
13
13
  inputSchema,
14
14
  }, async (args) => runTool(() => buildConnect(args, opts)));
15
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sf-gql-connect.js","sourceRoot":"","sources":["../../../src/mcp/tools/sf-gql-connect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAIxD,MAAM,WAAW,GAAG,aAAa,CAAC,KAAK,CAAC;AAExC,MAAM,UAAU,wBAAwB,CACvC,MAAiB,EACjB,OAAgC,EAAE;IAElC,MAAM,CAAC,YAAY,CAClB,gBAAgB,EAChB;QACC,WAAW,EACV,wuDAAwuD;QACzuD,WAAW;KACX,EACD,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CACvD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"sf-gql-connect.js","sourceRoot":"","sources":["../../../src/mcp/tools/sf-gql-connect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAIxD,MAAM,WAAW,GAAG,aAAa,CAAC,KAAK,CAAC;AAExC,MAAM,UAAU,wBAAwB,CACvC,MAAiB,EACjB,OAAgC,EAAE;IAElC,MAAM,CAAC,YAAY,CAClB,gBAAgB,EAChB;QACC,WAAW,EACV,48DAA48D;QAC78D,WAAW;KACX,EACD,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CACvD,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/graphiti",
3
- "version": "11.26.0",
3
+ "version": "11.26.2",
4
4
  "description": "Progressive GraphQL query builder CLI for Salesforce orgs",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "type": "module",
@@ -7,6 +7,7 @@
7
7
  import { beforeEach, describe, expect, it } from "vitest";
8
8
  import { z } from "zod";
9
9
  import { captureStdout } from "../../../__tests__/helpers/stdout.js";
10
+ import { AuthError } from "../../../lib/errors.js";
10
11
  import { SchemaRefreshError } from "../../../lib/prime-schema.js";
11
12
  import { runMirror } from "../run-mirror.js";
12
13
 
@@ -114,6 +115,45 @@ describe("runMirror", () => {
114
115
  expect(process.exitCode).toBe(1);
115
116
  });
116
117
 
118
+ it("maps a typed AuthError to AUTH_FAILED (W-23335328)", async () => {
119
+ // A 401/403 introspection failure reclassified to AuthError must map to
120
+ // AUTH_FAILED via the typed check. Its message contains "Schema priming
121
+ // failed …", so classifyErrorMessage's message regex would return
122
+ // SCHEMA_PRIME_FAILED — an untyped fallthrough would mislabel a 401 as a
123
+ // schema problem, which is exactly the confusion W-23335328 fixes.
124
+ const authMessage =
125
+ 'Schema priming failed for "o" — the org session is expired or unauthorized. Re-authenticate with `sf org login web --alias o`.';
126
+ const build = async () => {
127
+ throw new AuthError(authMessage);
128
+ };
129
+
130
+ const out = await captureStdout(() =>
131
+ runMirror('{"org":"o","object":"Account"}', SCHEMA, build),
132
+ );
133
+
134
+ const parsed = JSON.parse(out);
135
+ expect(parsed.error.code).toBe("AUTH_FAILED");
136
+ expect(parsed.error.message).toContain("Re-authenticate");
137
+ expect(process.exitCode).toBe(1);
138
+ });
139
+
140
+ it("the SAME message on a BARE Error would misclassify as SCHEMA_PRIME_FAILED — proves the typed check is load-bearing (W-23335328)", async () => {
141
+ // Regression guard for the trap the typed AuthError branch avoids: the auth
142
+ // message contains "Schema priming", which classifyErrorMessage's regex maps
143
+ // to SCHEMA_PRIME_FAILED. Only the `instanceof AuthError` check rescues it.
144
+ const build = async () => {
145
+ throw new Error(
146
+ 'Schema priming failed for "o" — the org session is expired or unauthorized. Re-authenticate with `sf org login web --alias o`.',
147
+ );
148
+ };
149
+
150
+ const out = await captureStdout(() =>
151
+ runMirror('{"org":"o","object":"Account"}', SCHEMA, build),
152
+ );
153
+
154
+ expect(JSON.parse(out).error.code).toBe("SCHEMA_PRIME_FAILED");
155
+ });
156
+
117
157
  it("maps SchemaRefreshError to SCHEMA_PRIME_FAILED", async () => {
118
158
  const build = async () => {
119
159
  throw new SchemaRefreshError("schema is stale", {
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { type z } from "zod";
8
+ import { AuthError } from "../../lib/errors.js";
8
9
  import { SchemaRefreshError } from "../../lib/prime-schema.js";
9
10
 
10
11
  /**
@@ -28,8 +29,8 @@ export type MirrorErrorCode = "INVALID_ARGS" | "AUTH_FAILED" | "SCHEMA_PRIME_FAI
28
29
  * code. The intent/auth layers throw plain `Error`s with no type signal, so the
29
30
  * only available hint is the message text. This is a NON-CONTRACT heuristic: a
30
31
  * reworded upstream message can fall through to `INTERNAL`. The verbatim message
31
- * is always preserved either way, and the typed `SchemaRefreshError` signal
32
- * (checked before this) takes precedence. Patterns mirror the MCP-CLI prior art.
32
+ * is always preserved either way, and the typed `AuthError` / `SchemaRefreshError`
33
+ * signals (checked before this) take precedence. Patterns mirror the MCP-CLI prior art.
33
34
  */
34
35
  function classifyErrorMessage(message: string): MirrorErrorCode {
35
36
  if (
@@ -127,17 +128,26 @@ export async function runMirror<T>(
127
128
  return;
128
129
  }
129
130
 
130
- // 4. Build and emit. Classify failures: the typed SchemaRefreshError signal
131
- // first, then a best-effort message regex (AUTH_FAILED / SCHEMA_PRIME_FAILED),
131
+ // 4. Build and emit. Classify failures: the typed AuthError / SchemaRefreshError
132
+ // signals first, then a best-effort message regex (AUTH_FAILED / SCHEMA_PRIME_FAILED),
132
133
  // else INTERNAL. The verbatim message is always preserved.
133
134
  try {
134
135
  const output = await build(result.data);
135
136
  console.log(JSON.stringify(output));
136
137
  } catch (err) {
137
138
  const message = err instanceof Error ? err.message : String(err);
138
- // Typed signal wins; otherwise fall back to the best-effort message regex.
139
+ // Typed signals win; otherwise fall back to the best-effort message regex.
140
+ // AuthError is checked first because its message (W-23335328) reads "Schema
141
+ // priming failed … expired or unauthorized. Re-authenticate …" — that
142
+ // "priming" makes classifyErrorMessage return SCHEMA_PRIME_FAILED, so an
143
+ // untyped fallthrough would misclassify a 401/403 introspection failure as a
144
+ // schema problem (the exact Schema-vs-Auth confusion this WI fixes).
139
145
  const code: MirrorErrorCode =
140
- err instanceof SchemaRefreshError ? "SCHEMA_PRIME_FAILED" : classifyErrorMessage(message);
146
+ err instanceof AuthError
147
+ ? "AUTH_FAILED"
148
+ : err instanceof SchemaRefreshError
149
+ ? "SCHEMA_PRIME_FAILED"
150
+ : classifyErrorMessage(message);
141
151
  // The stack is omitted by default so the envelope never leaks internals;
142
152
  // GRAPHITI_DEBUG=1 opts into attaching it under `details` for debugging.
143
153
  const details =
@@ -501,6 +501,115 @@ describe("lib/prime-schema", () => {
501
501
  }
502
502
  });
503
503
 
504
+ // ── 401/403 introspection auth reclassification (W-23335328) ───────────────
505
+ //
506
+ // A 401/403 from the introspection POST is an AUTH failure (expired/unauthorized
507
+ // session), not a schema/cache problem. It must surface as AuthError (→ `Auth:`),
508
+ // on BOTH the lazy-prime and forced-refresh paths, keyed off the cause shape.
509
+ // A real jsforce HttpApiError carries a string errorCode/name (ERROR_HTTP_401 or
510
+ // the body code INVALID_SESSION_ID) and NO numeric statusCode.
511
+ function jsforce401(errorCode = "ERROR_HTTP_401"): Error {
512
+ // Mirror jsforce-node's HttpApiError: name === errorCode, no numeric statusCode.
513
+ return Object.assign(new Error("Session expired or invalid"), {
514
+ name: errorCode,
515
+ errorCode,
516
+ });
517
+ }
518
+
519
+ it("primeSchemaWithLock: a lazy prime that 401s throws AuthError, not SchemaError", async () => {
520
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "graphiti-prime-401-"));
521
+ process.env.GRAPHITI_HOME = tmpRoot;
522
+ try {
523
+ const { primeSchemaWithLock } = await import("../prime-schema.js");
524
+ const { AuthError } = await import("../errors.js");
525
+ const { deps } = makeStubDeps(tmpRoot, { throwOn: { 1: jsforce401() } });
526
+
527
+ const err = await primeSchemaWithLock("test-org", deps).catch((e) => e);
528
+ expect(err).toBeInstanceOf(AuthError);
529
+ expect(err.message).toMatch(/re-?authenticate/i);
530
+ } finally {
531
+ delete process.env.GRAPHITI_HOME;
532
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
533
+ }
534
+ });
535
+
536
+ it("primeSchemaWithLock: a forced refresh WITH a surviving cache that 401s throws AuthError (not a soft SchemaRefreshError)", async () => {
537
+ // N3 regression: even though a usable cached schema survives on disk, a dead
538
+ // session must hard-fail as AuthError — NOT degrade to the soft-warning
539
+ // SchemaRefreshError path (which buildConnect would turn into a non-erroring
540
+ // success). The reclassification is keyed off the cause, not cache survival.
541
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "graphiti-refresh-401-"));
542
+ process.env.GRAPHITI_HOME = tmpRoot;
543
+ try {
544
+ const { primeSchemaWithLock, SchemaRefreshError } = await import("../prime-schema.js");
545
+ const { AuthError } = await import("../errors.js");
546
+
547
+ const fp = await writeSchemaFile(tmpRoot);
548
+ const originalContent = fs.readFileSync(fp, "utf-8");
549
+ const past = new Date(Date.now() - 5_000);
550
+ fs.utimesSync(fp, past, past);
551
+
552
+ const { deps } = makeStubDeps(tmpRoot, { throwOn: { 1: jsforce401("INVALID_SESSION_ID") } });
553
+
554
+ const err = await primeSchemaWithLock("test-org", deps, { forceRefresh: true }).catch(
555
+ (e) => e,
556
+ );
557
+ expect(err).toBeInstanceOf(AuthError);
558
+ expect(err).not.toBeInstanceOf(SchemaRefreshError);
559
+ // The surviving cache is left untouched — clears only happen after a
560
+ // successful download, and the throw precedes any clear.
561
+ expect(fs.readFileSync(fp, "utf-8")).toBe(originalContent);
562
+ } finally {
563
+ delete process.env.GRAPHITI_HOME;
564
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
565
+ }
566
+ });
567
+
568
+ it("primeSchemaWithLock: a forced refresh with NO prior cache that 401s throws AuthError", async () => {
569
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "graphiti-refresh-401-nocache-"));
570
+ process.env.GRAPHITI_HOME = tmpRoot;
571
+ try {
572
+ const { primeSchemaWithLock } = await import("../prime-schema.js");
573
+ const { AuthError } = await import("../errors.js");
574
+ const { deps } = makeStubDeps(tmpRoot, { throwOn: { 1: jsforce401("ERROR_HTTP_403") } });
575
+
576
+ const err = await primeSchemaWithLock("test-org", deps, { forceRefresh: true }).catch(
577
+ (e) => e,
578
+ );
579
+ expect(err).toBeInstanceOf(AuthError);
580
+ } finally {
581
+ delete process.env.GRAPHITI_HOME;
582
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
583
+ }
584
+ });
585
+
586
+ it("primeSchemaWithLock: a non-auth 4xx refresh failure STILL yields the soft SchemaRefreshError (no over-broadening)", async () => {
587
+ // Guard the boundary: only 401/403 reclassify. A 404 (or any other cause)
588
+ // with a surviving cache keeps the existing soft-warning SchemaRefreshError path.
589
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "graphiti-refresh-404-"));
590
+ process.env.GRAPHITI_HOME = tmpRoot;
591
+ try {
592
+ const { primeSchemaWithLock, SchemaRefreshError } = await import("../prime-schema.js");
593
+
594
+ const fp = await writeSchemaFile(tmpRoot);
595
+ const past = new Date(Date.now() - 5_000);
596
+ fs.utimesSync(fp, past, past);
597
+
598
+ const { deps } = makeStubDeps(tmpRoot, {
599
+ throwOn: { 1: Object.assign(new Error("not found"), { statusCode: 404 }) },
600
+ });
601
+
602
+ const err = await primeSchemaWithLock("test-org", deps, { forceRefresh: true }).catch(
603
+ (e) => e,
604
+ );
605
+ expect(err).toBeInstanceOf(SchemaRefreshError);
606
+ expect(err.staleSince).toBeTruthy();
607
+ } finally {
608
+ delete process.env.GRAPHITI_HOME;
609
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
610
+ }
611
+ });
612
+
504
613
  it("primeSchemaWithLock: a coalesced refresh clears its OWN stale caches (R4, isolated from the winner)", async () => {
505
614
  const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "graphiti-refresh-r4-"));
506
615
  process.env.GRAPHITI_HOME = tmpRoot;
package/src/lib/errors.ts CHANGED
@@ -105,7 +105,11 @@ export function classifyCause(cause: unknown): RetryHint {
105
105
 
106
106
  const codeStr = typeof c.errorCode === "string" ? c.errorCode : "";
107
107
  const nameStr = typeof c.name === "string" ? c.name : "";
108
- const httpMatch = /ERROR_HTTP_(\d{3})/.exec(`${codeStr} ${nameStr}`);
108
+ // `(?!\d)` anchors the status to EXACTLY three digits: without it the greedy
109
+ // `\d{3}` would capture the first three digits of a 4-digit tail (e.g. a
110
+ // hypothetical `ERROR_HTTP_5001` → `500`), misreading the class. jsforce only
111
+ // emits canonical 3-digit codes today, so this is defensive hardening.
112
+ const httpMatch = /ERROR_HTTP_(\d{3})(?!\d)/.exec(`${codeStr} ${nameStr}`);
109
113
  if (httpMatch) {
110
114
  const httpStatus = Number(httpMatch[1]);
111
115
  if (TRANSIENT_STATUS.has(httpStatus)) return "backoff";
@@ -127,6 +131,72 @@ export function classifyCause(cause: unknown): RetryHint {
127
131
  return "no";
128
132
  }
129
133
 
134
+ // HTTP statuses that mean the introspection POST failed to AUTHENTICATE/authorize
135
+ // (W-23335328): the org session is missing, expired, or lacks access. The fix is
136
+ // to re-authenticate, so these route to `Auth:` — not `Schema:` (re-prime) — at the
137
+ // MCP boundary. A subset of PERMANENT_STATUS, so they stay non-retryable too.
138
+ const AUTH_STATUS = new Set([401, 403]);
139
+ // Salesforce error codes that ARE a 401 in disguise. jsforce-node collapses a 4xx
140
+ // whose body parses as a Salesforce error array into an HttpApiError whose
141
+ // `errorCode`/`name` is the body code (e.g. INVALID_SESSION_ID for an expired
142
+ // session) and sets NO numeric statusCode — so the canonical expired-session case
143
+ // never presents a 401 status here and must be matched by code. Deliberately kept
144
+ // to the session-invalid code we can tie authoritatively to "re-authenticate fixes
145
+ // it". A structured 403 body code such as INSUFFICIENT_ACCESS / API_DISABLED_FOR_ORG
146
+ // is intentionally NOT here: those are org permission/config problems that a fresh
147
+ // login does NOT resolve, so routing them to `Auth:` ("re-authenticate") would
148
+ // misguide the agent — they correctly stay `Schema:`. Extend only with codes where
149
+ // re-auth is the right remedy (e.g. INVALID_AUTH_HEADER, MISSING_OAUTH_TOKEN) as
150
+ // they are observed on this path.
151
+ const AUTH_ERROR_CODES = new Set(["INVALID_SESSION_ID"]);
152
+
153
+ /**
154
+ * Detect whether an introspection/download `cause` is a 401/403-class auth failure
155
+ * that should surface as `Auth:` (re-authenticate) rather than `Schema:` (W-23335328).
156
+ *
157
+ * Mirrors {@link classifyCause}'s defensive shape-reading. A real jsforce /
158
+ * `@salesforce/core` HTTP failure carries a string `errorCode`/`name` — jsforce-node's
159
+ * `HttpApiError` sets `ERROR_HTTP_401`/`ERROR_HTTP_403` for a status-only failure, or
160
+ * the body's code (e.g. `INVALID_SESSION_ID`) for an expired session — but NOT a numeric
161
+ * `statusCode`; so the `ERROR_HTTP_<nnn>` regex on `errorCode`/`name` and the
162
+ * `AUTH_ERROR_CODES` check are the load-bearing paths, and the `statusCode` branch is a
163
+ * defensive fallback for other cause shapes (and the contract tests' status-bearing
164
+ * doubles). Every property read is wrapped so a `cause` with a throwing accessor falls
165
+ * back to `false` (this runs on the priming-failure path; an escaped throw would drop the
166
+ * classification). Returns false for a null/undefined/non-object cause and for any
167
+ * non-auth status — we never over-broaden: a 4xx that is not 401/403 stays `Schema:`.
168
+ *
169
+ * Note (W-23148365 N3): callers must key the Auth reclassification off THIS cause-shape
170
+ * inspection, never off retry-token absence or schema-cache survival — a forced refresh
171
+ * that keeps a usable cache emits no token regardless of the cause, so "no token" is not a
172
+ * reliable signal that a 401/403 occurred.
173
+ */
174
+ export function isAuthError(cause: unknown): boolean {
175
+ if (typeof cause !== "object" || cause === null) return false;
176
+ const c = cause as Record<string, unknown>;
177
+
178
+ try {
179
+ const status = typeof c.statusCode === "number" ? c.statusCode : undefined;
180
+ if (status !== undefined && AUTH_STATUS.has(status)) return true;
181
+
182
+ const codeStr = typeof c.errorCode === "string" ? c.errorCode : "";
183
+ const nameStr = typeof c.name === "string" ? c.name : "";
184
+ // `(?!\d)` anchors to EXACTLY three digits so a 4-digit tail can't be misread
185
+ // as a 401/403 (e.g. `ERROR_HTTP_4011` would otherwise capture `401` and
186
+ // wrongly route to Auth). Mirrors the same guard in classifyCause.
187
+ const httpMatch = /ERROR_HTTP_(\d{3})(?!\d)/.exec(`${codeStr} ${nameStr}`);
188
+ if (httpMatch && AUTH_STATUS.has(Number(httpMatch[1]))) return true;
189
+
190
+ if (AUTH_ERROR_CODES.has(codeStr) || AUTH_ERROR_CODES.has(nameStr)) return true;
191
+ } catch {
192
+ // A throwing getter on the cause → treat as not-an-auth-error (fall through
193
+ // to the existing Schema classification, which is the conservative default).
194
+ return false;
195
+ }
196
+
197
+ return false;
198
+ }
199
+
130
200
  /** Credential/auth resolution failure (e.g. unknown org, expired token). → `Auth:` */
131
201
  export class AuthError extends Error {
132
202
  constructor(message: string, opts?: { cause?: unknown }) {
@@ -7,7 +7,7 @@
7
7
  import fs from "node:fs";
8
8
  import path from "node:path";
9
9
  import { getOrgAuth as realGetOrgAuth, type OrgAuth } from "./auth.js";
10
- import { classifyCause, type RetryHint, SchemaError } from "./errors.js";
10
+ import { AuthError, classifyCause, isAuthError, type RetryHint, SchemaError } from "./errors.js";
11
11
  import {
12
12
  downloadSchema as realDownloadSchema,
13
13
  getSchemaMetadata,
@@ -296,6 +296,26 @@ export async function primeSchemaWithLock(
296
296
  // through.
297
297
  meta = await deps.downloadSchema(auth);
298
298
  } catch (cause) {
299
+ // A 401/403 from the introspection POST is an AUTH failure, not a
300
+ // schema/cache problem (W-23335328): surface it as AuthError so the MCP
301
+ // boundary emits `Auth:` (re-authenticate) and the agent re-auths instead
302
+ // of futilely re-priming. Applies to BOTH lazy prime and forced refresh,
303
+ // and — per the W-23148365 N3 note — is keyed off the cause SHAPE
304
+ // (isAuthError), never off retry-token absence or cache survival. On a
305
+ // forced refresh this INTENTIONALLY bypasses the SchemaRefreshError
306
+ // soft-surviving-cache path below: a cached schema is worthless once the
307
+ // session is dead (every subsequent live query 401s too), so a hard Auth
308
+ // error — not a silent soft-success warning — is the correct signal, and
309
+ // it is what makes the 401/403 actually route to `Auth:` rather than a
310
+ // non-erroring `refreshed:false` return. AuthError carries no retry token
311
+ // (Auth is uniformly non-retryable), preserving the existing contract.
312
+ if (isAuthError(cause)) {
313
+ const msg = cause instanceof Error ? cause.message : String(cause);
314
+ throw new AuthError(
315
+ `Schema priming failed for "${orgAlias}" — the org session is expired or unauthorized. Re-authenticate with \`sf org login web --alias ${orgAlias}\`.\n${msg}`,
316
+ { cause },
317
+ );
318
+ }
299
319
  // Lazy prime (no existing cache): no stale cache to keep, so surface
300
320
  // the underlying failure (FR-13.5/13.6). Wrap untyped causes (e.g. a
301
321
  // raw @salesforce/core/jsforce network error from connection.request)
@@ -294,6 +294,36 @@ describe("mcp/tools error surface — category prefixes (contract)", () => {
294
294
  }
295
295
  });
296
296
 
297
+ it("Auth: a 401/403 introspection failure surfaces with the Auth prefix and NO retry token (W-23335328)", async () => {
298
+ // A real jsforce HttpApiError on a 401 carries a string errorCode/name
299
+ // (ERROR_HTTP_401, or the body code INVALID_SESSION_ID) and no numeric
300
+ // statusCode. primeSchemaWithLock reclassifies it to AuthError → `Auth:`,
301
+ // not `Schema:` — so the agent re-authenticates instead of re-priming.
302
+ const primeDeps: PrimeDeps = {
303
+ ...makeNoopPrimeDeps(ORG, ORG_URL, SCHEMA),
304
+ downloadSchema: async () => {
305
+ throw Object.assign(new Error("Session expired or invalid"), {
306
+ name: "INVALID_SESSION_ID",
307
+ errorCode: "INVALID_SESSION_ID",
308
+ });
309
+ },
310
+ };
311
+ const { client, server } = await connectWith(primeDeps);
312
+ try {
313
+ const result = await client.callTool({
314
+ name: "sf_gql_list",
315
+ arguments: { org: ORG, object: "Account", fields: ["Id"] },
316
+ });
317
+ expect(result.isError).toBe(true);
318
+ expect(errorText(result)).toMatch(/^Auth: /);
319
+ // Auth is uniformly non-retryable — it never carries a [retry=...] token.
320
+ expect(errorText(result)).not.toMatch(/\[retry=/);
321
+ } finally {
322
+ await client.close();
323
+ await server.close();
324
+ }
325
+ });
326
+
297
327
  it("Internal: an unexpected error is sanitized, prefixed, and logged to stderr", async () => {
298
328
  const stderr = vi.spyOn(console, "error").mockImplementation(() => undefined);
299
329
  const home = os.homedir();
@@ -21,7 +21,7 @@ export function registerSfGqlConnectTool(
21
21
  "sf_gql_connect",
22
22
  {
23
23
  description:
24
- "Connect to a Salesforce org and prime its GraphQL schema cache. With forceRefresh, re-download the schema and coherently clear all caches so subsequent tools see freshly-deployed metadata. Concurrent refreshes coalesce into a single introspection. Returns { org, instanceUrl, refreshed, cached, durationMs, warnings? } — not the standard ToolOutput envelope. If a refresh fails but a usable cached schema survives, returns refreshed:false with a staleness warning instead of erroring. Error convention (all sf_gql_* tools): on failure the isError text is prefixed with a category — `UserInput:` (fix the request), `Auth:` (re-authenticate the org), `Schema:` (introspection/cache problem), or `Internal:` (unexpected) — so you can decide whether to fix inputs, re-auth, or retry. A `Schema:` error additionally ends with a retryability token: `[retry=now]` (retry immediately — e.g. a priming-lock timeout, where no live org round-trip occurred) or `[retry=backoff]` (the org was unreachable and an automatic retry already failed — wait with increasing backoff, e.g. 2^n seconds capped around 8s, and retry serially; do not fan out concurrent retries against an org that is already failing, and note some conditions such as an org API rate limit may take longer than a few seconds to clear); ABSENCE of any `[retry=...]` token means the failure is permanent (404, missing/malformed `__schema`, GraphQL errors in the body, or no cached schema) — do not retry, fix the request or re-prime via sf_gql_connect. This token convention applies to every sf_gql_* tool, not just this one. Example error text: `Schema: introspection request failed [retry=backoff]`; extract the disposition with the end-anchored regex ` /\\s\\[retry=(now|backoff)\\]$/` (no match ⇒ permanent).",
24
+ "Connect to a Salesforce org and prime its GraphQL schema cache. With forceRefresh, re-download the schema and coherently clear all caches so subsequent tools see freshly-deployed metadata. Concurrent refreshes coalesce into a single introspection. Returns { org, instanceUrl, refreshed, cached, durationMs, warnings? } — not the standard ToolOutput envelope. If a refresh fails transiently (network/5xx) but a usable cached schema survives, returns refreshed:false with a staleness warning instead of erroring; a 401/403 auth failure during refresh instead errors with `Auth:` (a dead session makes the cached schema unusable — re-authenticate). Error convention (all sf_gql_* tools): on failure the isError text is prefixed with a category — `UserInput:` (fix the request), `Auth:` (re-authenticate the org — e.g. an expired/unauthorized session on connect or introspection), `Schema:` (introspection/cache problem), or `Internal:` (unexpected) — so you can decide whether to fix inputs, re-auth, or retry. A `Schema:` error additionally ends with a retryability token: `[retry=now]` (retry immediately — e.g. a priming-lock timeout, where no live org round-trip occurred) or `[retry=backoff]` (the org was unreachable and an automatic retry already failed — wait with increasing backoff, e.g. 2^n seconds capped around 8s, and retry serially; do not fan out concurrent retries against an org that is already failing, and note some conditions such as an org API rate limit may take longer than a few seconds to clear); ABSENCE of any `[retry=...]` token means the failure is permanent (404, missing/malformed `__schema`, GraphQL errors in the body, or no cached schema) — do not retry, fix the request or re-prime via sf_gql_connect. This token convention applies to every sf_gql_* tool, not just this one. Example error text: `Schema: introspection request failed [retry=backoff]`; extract the disposition with the end-anchored regex ` /\\s\\[retry=(now|backoff)\\]$/` (no match ⇒ permanent).",
25
25
  inputSchema,
26
26
  },
27
27
  async (args) => runTool(() => buildConnect(args, opts)),
@@ -6,7 +6,13 @@
6
6
 
7
7
  import os from "node:os";
8
8
  import { afterEach, describe, expect, it, vi } from "vitest";
9
- import { AuthError, classifyCause, SchemaError, UserInputError } from "../../lib/errors.js";
9
+ import {
10
+ AuthError,
11
+ classifyCause,
12
+ isAuthError,
13
+ SchemaError,
14
+ UserInputError,
15
+ } from "../../lib/errors.js";
10
16
  import { SchemaRefreshError } from "../../lib/prime-schema.js";
11
17
  import { MutationContextError } from "../../lib/walker.js";
12
18
  import { classifyError, neutralizeControlChars, PATH_MARKERS, runTool } from "../tool-adapter.js";
@@ -506,6 +512,14 @@ describe("schemas/tool-adapter — Schema retryability hint (W-23148365)", () =>
506
512
  expect(classifyCause({ errorCode: "REQUEST_LIMIT_EXCEEDED" })).toBe("backoff");
507
513
  });
508
514
 
515
+ it("anchors ERROR_HTTP_<nnn> to exactly 3 digits — a 4-digit tail is not a status", () => {
516
+ // The `(?!\d)` guard stops the greedy `\d{3}` from reading the leading 3
517
+ // digits of a longer code (`ERROR_HTTP_5001` must NOT parse as 500/backoff).
518
+ // No such code exists in jsforce today; this pins the hardening.
519
+ expect(classifyCause({ errorCode: "ERROR_HTTP_5001" })).toBe("no");
520
+ expect(classifyCause({ name: "ERROR_HTTP_4291" })).toBe("no");
521
+ });
522
+
509
523
  it("defaults to no for an absent/unrecognized cause (never invents retryability)", () => {
510
524
  expect(classifyCause(undefined)).toBe("no");
511
525
  expect(classifyCause(null)).toBe("no");
@@ -530,6 +544,64 @@ describe("schemas/tool-adapter — Schema retryability hint (W-23148365)", () =>
530
544
  });
531
545
  });
532
546
 
547
+ describe("isAuthError — 401/403 auth-failure detection (W-23335328)", () => {
548
+ // A 401/403 from the introspection POST must reclassify to Auth, not Schema.
549
+ // isAuthError detects it from the cause SHAPE (never from token absence /
550
+ // cache survival — the W-23148365 N3 note), mirroring classifyCause's reads.
551
+ it("detects a numeric 401/403 statusCode (contract-test / non-jsforce shape)", () => {
552
+ expect(isAuthError({ statusCode: 401 })).toBe(true);
553
+ expect(isAuthError({ statusCode: 403 })).toBe(true);
554
+ });
555
+
556
+ it("detects the load-bearing jsforce ERROR_HTTP_401/403 on errorCode/name", () => {
557
+ // jsforce-node's HttpApiError sets string errorCode/name, NOT a numeric
558
+ // statusCode — this regex path is the real production trigger.
559
+ expect(isAuthError({ errorCode: "ERROR_HTTP_401" })).toBe(true);
560
+ expect(isAuthError({ name: "ERROR_HTTP_403" })).toBe(true);
561
+ });
562
+
563
+ it("detects INVALID_SESSION_ID (the expired-session 401 jsforce reports by body code)", () => {
564
+ // A 401 whose body parses as a Salesforce error array collapses to an
565
+ // HttpApiError whose errorCode/name is the body code, not ERROR_HTTP_401.
566
+ expect(isAuthError({ errorCode: "INVALID_SESSION_ID" })).toBe(true);
567
+ expect(isAuthError({ name: "INVALID_SESSION_ID" })).toBe(true);
568
+ });
569
+
570
+ it("does NOT over-broaden: other 4xx / 5xx / network causes stay non-auth (Schema)", () => {
571
+ expect(isAuthError({ statusCode: 400 })).toBe(false);
572
+ expect(isAuthError({ statusCode: 404 })).toBe(false);
573
+ expect(isAuthError({ statusCode: 429 })).toBe(false);
574
+ expect(isAuthError({ statusCode: 500 })).toBe(false);
575
+ expect(isAuthError({ errorCode: "ERROR_HTTP_404" })).toBe(false);
576
+ expect(isAuthError({ errorCode: "ERROR_HTTP_500" })).toBe(false);
577
+ expect(isAuthError({ code: "ECONNRESET" })).toBe(false);
578
+ expect(isAuthError({ errorCode: "REQUEST_LIMIT_EXCEEDED" })).toBe(false);
579
+ // A 4-digit tail must NOT be truncated to a 401/403 by the `\d{3}` capture:
580
+ // the `(?!\d)` anchor keeps `ERROR_HTTP_4011`/`4030` out of Auth.
581
+ expect(isAuthError({ errorCode: "ERROR_HTTP_4011" })).toBe(false);
582
+ expect(isAuthError({ name: "ERROR_HTTP_4030" })).toBe(false);
583
+ });
584
+
585
+ it("returns false for absent / non-object / unrecognized causes (never invents auth)", () => {
586
+ expect(isAuthError(undefined)).toBe(false);
587
+ expect(isAuthError(null)).toBe(false);
588
+ expect(isAuthError("a bare string")).toBe(false);
589
+ expect(isAuthError({ unrelated: true })).toBe(false);
590
+ });
591
+
592
+ it("returns false for a cause whose accessor throws (no escape)", () => {
593
+ const booby = {};
594
+ Object.defineProperty(booby, "statusCode", {
595
+ get() {
596
+ throw new Error("boom");
597
+ },
598
+ enumerable: true,
599
+ });
600
+ expect(() => isAuthError(booby)).not.toThrow();
601
+ expect(isAuthError(booby)).toBe(false);
602
+ });
603
+ });
604
+
533
605
  describe("classifyError().retry", () => {
534
606
  it("reads the stamped retry off a typed SchemaError", () => {
535
607
  expect(classifyError(new SchemaError("lock timeout", { retry: "now" })).retry).toBe("now");