@molecule/api-ai-tools 1.9.10 → 1.9.12

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/README.md CHANGED
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-10-18T06:52:09.924Z
6
+ Generated: 2026-10-18T07:36:48.788Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -404,6 +404,28 @@ interface TraceRequest {
404
404
  * fixture), in which case only recording order is available.
405
405
  */
406
406
  startedAt?: number
407
+ /**
408
+ * Whether the request carried an `Authorization` header at all. Its VALUE is never recorded.
409
+ *
410
+ * A 401 cannot be read without this: the API's own body said
411
+ * `{"errors":{"token":["is missing"]}}` while the note told the run to read the API handler,
412
+ * when the header's absence proved the CLIENT never attached the token
413
+ * (xr-molecule-flash-m148 +29:18). A header present moves the answer to the server: the API
414
+ * refused a token it was sent.
415
+ */
416
+ sentAuthorization?: boolean
417
+ /**
418
+ * The scheme WORD of an `Authorization` header (`Bearer`, `Token`), or undefined when there is
419
+ * no header or its first word does not parse as a scheme (a value pasted without one is a
420
+ * credential, and is never copied here).
421
+ */
422
+ authorization?: string
423
+ /**
424
+ * Whether the request carried a `Cookie` header — never the cookies themselves. An app that
425
+ * signs in with a session cookie has no `Authorization` header by design, so this tells a
426
+ * credential-less request from one whose credential is a cookie.
427
+ */
428
+ sentCookie?: boolean
407
429
  }
408
430
  ```
409
431
 
@@ -455,7 +477,7 @@ interface TraceAction {
455
477
 
456
478
  #### `TraceArtifact`
457
479
 
458
- What one trace says: the test it belongs to, the requests it made, and its console errors.
480
+ What one trace says: the test it belongs to, the requests it made, and its console lines.
459
481
 
460
482
  ```typescript
461
483
  interface TraceArtifact {
@@ -477,7 +499,11 @@ interface TraceArtifact {
477
499
  * Absent when the trace recorded neither, and on traces read before this field existed.
478
500
  */
479
501
  lead?: TraceRequest
480
- /** The console errors and page errors the page raised. */
502
+ /**
503
+ * The console lines and page errors the page raised: its errors first, then a line that names a
504
+ * session decision or comes from a molecule package, then any other warning, then the browser's
505
+ * own resource error ({@link traceArtifactsScript}).
506
+ */
481
507
  console: string[]
482
508
  /**
483
509
  * The test's LAST user action before the failing assertion and the requests it caused — what the
@@ -1819,7 +1845,7 @@ function buildTools(backend: ExecutionBackend, config?: ToolBuildConfig): AITool
1819
1845
 
1820
1846
  The Node program the sandbox runs to read a failed run's traces: `node <script> <dir>` prints one
1821
1847
  `TRACE <json>` line per `trace.zip` under `<dir>` (newest first, {@link MAX_TRACE_ARTIFACTS} of
1822
- them), each with the test it belongs to, the requests its page made and its console errors.
1848
+ them), each with the test it belongs to, the requests its page made and its console lines.
1823
1849
 
1824
1850
  It is a standalone program, not an import, for the reason `@molecule/api-code-sandbox` runs it:
1825
1851
  Playwright's own zip reader is not reachable from a project's `node_modules`, and a dependency
@@ -3221,7 +3247,8 @@ const MAX_TRACED_REQUESTS: 6
3221
3247
 
3222
3248
  #### `MAX_TRACED_CONSOLE`
3223
3249
 
3224
- How many console errors one test's trace contributes.
3250
+ How many console lines one test's trace contributes: errors ahead of a session or molecule-package
3251
+ line, both ahead of a plain warning and of the browser's own resource error.
3225
3252
 
3226
3253
  ```typescript
3227
3254
  const MAX_TRACED_CONSOLE: 4
@@ -67,6 +67,28 @@ export interface TraceRequest {
67
67
  * fixture), in which case only recording order is available.
68
68
  */
69
69
  startedAt?: number;
70
+ /**
71
+ * Whether the request carried an `Authorization` header at all. Its VALUE is never recorded.
72
+ *
73
+ * A 401 cannot be read without this: the API's own body said
74
+ * `{"errors":{"token":["is missing"]}}` while the note told the run to read the API handler,
75
+ * when the header's absence proved the CLIENT never attached the token
76
+ * (xr-molecule-flash-m148 +29:18). A header present moves the answer to the server: the API
77
+ * refused a token it was sent.
78
+ */
79
+ sentAuthorization?: boolean;
80
+ /**
81
+ * The scheme WORD of an `Authorization` header (`Bearer`, `Token`), or undefined when there is
82
+ * no header or its first word does not parse as a scheme (a value pasted without one is a
83
+ * credential, and is never copied here).
84
+ */
85
+ authorization?: string;
86
+ /**
87
+ * Whether the request carried a `Cookie` header — never the cookies themselves. An app that
88
+ * signs in with a session cookie has no `Authorization` header by design, so this tells a
89
+ * credential-less request from one whose credential is a cookie.
90
+ */
91
+ sentCookie?: boolean;
70
92
  }
71
93
  /**
72
94
  * The test's LAST user action before the failing assertion, and the requests it caused.
@@ -104,7 +126,7 @@ export interface TraceAction {
104
126
  /** When it finished, or null when the run ended before it did. */
105
127
  end: number | null;
106
128
  }
107
- /** What one trace says: the test it belongs to, the requests it made, and its console errors. */
129
+ /** What one trace says: the test it belongs to, the requests it made, and its console lines. */
108
130
  export interface TraceArtifact {
109
131
  /** The failure's spec file, relative to the run's directory, when the trace names one. */
110
132
  spec: string | null;
@@ -124,7 +146,11 @@ export interface TraceArtifact {
124
146
  * Absent when the trace recorded neither, and on traces read before this field existed.
125
147
  */
126
148
  lead?: TraceRequest;
127
- /** The console errors and page errors the page raised. */
149
+ /**
150
+ * The console lines and page errors the page raised: its errors first, then a line that names a
151
+ * session decision or comes from a molecule package, then any other warning, then the browser's
152
+ * own resource error ({@link traceArtifactsScript}).
153
+ */
128
154
  console: string[];
129
155
  /**
130
156
  * The test's LAST user action before the failing assertion and the requests it caused — what the
@@ -145,7 +171,10 @@ export interface TraceArtifact {
145
171
  export declare const MAX_TRACE_ARTIFACTS = 12;
146
172
  /** How many requests one test's trace contributes. */
147
173
  export declare const MAX_TRACED_REQUESTS = 6;
148
- /** How many console errors one test's trace contributes. */
174
+ /**
175
+ * How many console lines one test's trace contributes: errors ahead of a session or molecule-package
176
+ * line, both ahead of a plain warning and of the browser's own resource error.
177
+ */
149
178
  export declare const MAX_TRACED_CONSOLE = 4;
150
179
  /** How many of the page's own actions one test's trace contributes, the LAST ones. */
151
180
  export declare const MAX_TRACED_ACTIONS = 40;
@@ -170,7 +199,7 @@ export declare const TRACE_NOTE_LEAD_GOT_CHARS = 200;
170
199
  /**
171
200
  * The Node program the sandbox runs to read a failed run's traces: `node <script> <dir>` prints one
172
201
  * `TRACE <json>` line per `trace.zip` under `<dir>` (newest first, {@link MAX_TRACE_ARTIFACTS} of
173
- * them), each with the test it belongs to, the requests its page made and its console errors.
202
+ * them), each with the test it belongs to, the requests its page made and its console lines.
174
203
  *
175
204
  * It is a standalone program, not an import, for the reason `@molecule/api-code-sandbox` runs it:
176
205
  * Playwright's own zip reader is not reachable from a project's `node_modules`, and a dependency
@@ -282,7 +311,7 @@ export declare function stalledDocumentSubresource(request: TraceRequest): boole
282
311
  * row falls back to {@link TraceArtifact.lead}: the page's last answered fetch/xhr.
283
312
  *
284
313
  * WHICH rows it spends is evidence, not artifact order (xr-molecule-flash-m112 +53:46): a row
285
- * needs a recorded response, a real failure or a console error ({@link traceRowEvidence}), rows are
314
+ * needs a recorded response, a real failure or a console line ({@link traceRowEvidence}), rows are
286
315
  * deduplicated by the test's location, and they are ordered real failure → repeated from an
287
316
  * earlier walk → the rest ({@link traceRowTier}). Without that the note spent all five rows on four
288
317
  * traces of one `afterEach` line whose every request was `-1`, and left the favorite cause — the
@@ -306,6 +335,15 @@ export declare function stalledDocumentSubresource(request: TraceRequest): boole
306
335
  * request that never reached the API. The prefix is READ (the caller passes what the app's client
307
336
  * config names; the traces add what their own requests show) and never assumed to be `/api`.
308
337
  *
338
+ * A 401/403 on the app's own origin is answered from the request's own credential record instead
339
+ * ({@link authRefusalSentence}): no `Authorization` header means the CLIENT never attached the
340
+ * token (the shared HTTP client's `setAuthToken` is what the auth bond mirrors it into), while a
341
+ * header the API then refused moves the answer to the server. The handler sentence is kept for
342
+ * the rest — 404/422/500 — and dropped when every failure was a credential refusal, because a
343
+ * trace that proves the header was never sent cannot be explained by the handler.
344
+ * xr-molecule-flash-m148 (+29:18) hit this exactly: six local walks read
345
+ * `POST …/favorite → 401 {"errors":{"token":["is missing"]}}` as a handler bug.
346
+ *
309
347
  * @param items - The traces {@link parseTraceArtifacts} read.
310
348
  * @param target - Where the run went — the deployed host, as the note names it.
311
349
  * @param earlierFailures - The failing `file:line`s earlier walks of this suite left, so a repeat
@@ -1 +1 @@
1
- {"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;;OAQG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAA;IACX,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB;;;;OAIG;IACH,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAA;IACX,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACnB;AAED,gGAAgG;AAChG,MAAM,WAAW,aAAa;IAC5B,0FAA0F;IAC1F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAA;IACZ,6FAA6F;IAC7F,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB;;;;OAIG;IACH,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B;;;;OAIG;IACH,OAAO,EAAE,WAAW,EAAE,CAAA;CACvB;AAED,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAEpC;;;GAGG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAEnC,sFAAsF;AACtF,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC,yFAAyF;AACzF,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAErC,iDAAiD;AACjD,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,gEAAgE;AAChE,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC,wFAAwF;AACxF,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC,kFAAkF;AAClF,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAEvC;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAK5C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,oBAAoB,QAAO,MA8fvC,CAAA;AAoDD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAkCnE;AA2KD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAKnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAG5D;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAGhE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAMzE;AA4VD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,MAAM,EAAE,MAAM,EACd,eAAe,GAAE,SAAS,MAAM,EAAO,EACvC,WAAW,GAAE,SAAS,MAAM,EAAO,GAClC,MAAM,GAAG,IAAI,CAkPf;AAaD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAG9D;AAED,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,8BAA8B,CAAA;AAwG/D,oGAAoG;AACpG,MAAM,WAAW,qBAAqB;IACpC,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,4EAA4E;IAC5E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAC5C,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA2BjC"}
@@ -33,7 +33,10 @@ import { isPlaywrightRunCommand, redactSecrets } from './utilities.js';
33
33
  export const MAX_TRACE_ARTIFACTS = 12;
34
34
  /** How many requests one test's trace contributes. */
35
35
  export const MAX_TRACED_REQUESTS = 6;
36
- /** How many console errors one test's trace contributes. */
36
+ /**
37
+ * How many console lines one test's trace contributes: errors ahead of a session or molecule-package
38
+ * line, both ahead of a plain warning and of the browser's own resource error.
39
+ */
37
40
  export const MAX_TRACED_CONSOLE = 4;
38
41
  /** How many of the page's own actions one test's trace contributes, the LAST ones. */
39
42
  export const MAX_TRACED_ACTIONS = 40;
@@ -60,7 +63,7 @@ const TRACE_NOTE_FAILURE_CHARS = 120;
60
63
  /**
61
64
  * The Node program the sandbox runs to read a failed run's traces: `node <script> <dir>` prints one
62
65
  * `TRACE <json>` line per `trace.zip` under `<dir>` (newest first, {@link MAX_TRACE_ARTIFACTS} of
63
- * them), each with the test it belongs to, the requests its page made and its console errors.
66
+ * them), each with the test it belongs to, the requests its page made and its console lines.
64
67
  *
65
68
  * It is a standalone program, not an import, for the reason `@molecule/api-code-sandbox` runs it:
66
69
  * Playwright's own zip reader is not reachable from a project's `node_modules`, and a dependency
@@ -95,6 +98,20 @@ const MAX_CONSOLE = ${MAX_TRACED_CONSOLE}
95
98
  const MAX_ACTIONS = ${MAX_TRACED_ACTIONS}
96
99
  const MAX_BODY = ${MAX_TRACED_BODY_CHARS}
97
100
  const MAX_URL = 200
101
+ // A console line that is not an error but still has to reach the note (m148): one a molecule
102
+ // package wrote — it names the package, the \`[molecule/…]\` prefix, or the \`[scope]\` prefix
103
+ // molecule's own logger writes for a named module ([auth], [http], [i18n]) — or one that names a
104
+ // session decision in the page's own words. \`@molecule/app-auth\` logged "Token refresh failed,
105
+ // logging out" at WARNING level, and this reader kept error entries only, so every trace note
106
+ // carried the browser's generic "Failed to load resource: 401" while the platform's own package was
107
+ // saying, in words, that it had signed the user out.
108
+ const MOLECULE_CONSOLE = /@molecule\\/|\\[molecule[:\\/][^\\]]*\\]|^\\s*\\[[a-z][a-z0-9-]*\\]/
109
+ const SESSION_CONSOLE = /\\b(?:auth(?:entication|enticated|orization|orized)?|session|token|sign(?:ed|ing)?[\\s-]?out|log(?:ged|ging)?[\\s-]?out|refresh(?:ed|ing)?\\s+failed|no\\s+provider)\\b/i
110
+
111
+ // Whether a console line belongs in the note even though it is not at error level.
112
+ function consoleSignal(text) {
113
+ return MOLECULE_CONSOLE.test(text) || SESSION_CONSOLE.test(text)
114
+ }
98
115
 
99
116
  function walk(dir, depth, out) {
100
117
  if (depth < 0) return
@@ -226,12 +243,34 @@ for (const one of found.slice(0, MAX_TRACES)) {
226
243
  const response = snapshot.response || {}
227
244
  const type = String(snapshot._resourceType || '')
228
245
  if (!origin) origin = type === 'document' ? originOf(String(request.url)) : ''
246
+ // What the request carried for CREDENTIALS — never the secret itself. A 401 on the app's own
247
+ // API cannot be read without this: m148's favorite POST answered 401 with no Authorization
248
+ // header at all, so the client never attached the token, and the note sent the run to the
249
+ // API handler instead (xr-molecule-flash-m148 +29:18). Presence is recorded as a boolean and
250
+ // only the scheme WORD (\`Bearer\`, \`Token\`) is kept; the value is a live credential and never
251
+ // leaves the trace. A first word that does not parse as a scheme (\`Authorization: <raw jwt>\`)
252
+ // is recorded as presence only, so a pasted token can never ride along as a "scheme".
253
+ const headerList = Array.isArray(request.headers) ? request.headers : []
254
+ const authHeader = headerList.find(function (h) {
255
+ return h && typeof h.name === 'string' && /^authorization$/i.test(h.name)
256
+ })
257
+ const authValue =
258
+ authHeader && typeof authHeader.value === 'string' ? String(authHeader.value).trim() : ''
259
+ const authSpace = authValue.search(/\\s/)
260
+ const authWord = authSpace > 0 ? authValue.slice(0, authSpace) : ''
261
+ const authScheme = /^[A-Za-z][A-Za-z0-9._~+/-]{0,23}$/.test(authWord) ? authWord : undefined
262
+ const sentCookie = headerList.some(function (h) {
263
+ return h && typeof h.name === 'string' && /^cookie$/i.test(h.name)
264
+ })
229
265
  requests.push({
230
266
  method: String(request.method).toUpperCase(),
231
267
  path: String(request.url).slice(0, MAX_URL),
232
268
  status: Number(response.status) || 0,
233
269
  failure: typeof response._failureText === 'string' ? response._failureText : undefined,
234
270
  type: type,
271
+ sentAuthorization: authHeader !== undefined,
272
+ authorization: authScheme,
273
+ sentCookie: sentCookie,
235
274
  // The HAR entry carries the monotonic clock the trace's action entries use, so a request
236
275
  // can be attributed to the action that caused it (see \`lastAction\` below).
237
276
  startedAt: typeof snapshot._monotonicTime === 'number' ? snapshot._monotonicTime : undefined,
@@ -413,6 +452,9 @@ for (const one of found.slice(0, MAX_TRACES)) {
413
452
  ...(r.sent ? { sent: r.sent } : {}),
414
453
  ...(r.got ? { got: r.got } : {}),
415
454
  type: r.type || undefined,
455
+ ...(r.sentAuthorization ? { sentAuthorization: true } : {}),
456
+ ...(r.authorization ? { authorization: r.authorization } : {}),
457
+ ...(r.sentCookie ? { sentCookie: true } : {}),
416
458
  }
417
459
  }
418
460
  // The main frame's URL, timed: what the page showed when the action ran, and where it ended up.
@@ -508,13 +550,18 @@ for (const one of found.slice(0, MAX_TRACES)) {
508
550
  chosen.push(wire(r))
509
551
  }
510
552
  const consoleErrors = []
553
+ const consoleSignals = []
554
+ const consoleWarnings = []
511
555
  let spec = null
512
556
  let line = 0
513
557
  for (const entry of trace) {
514
558
  if (!entry) continue
515
- if (entry.type === 'console' && entry.messageType === 'error' && typeof entry.text === 'string')
516
- consoleErrors.push(entry.text)
517
- else if (entry.type === 'event' && entry.method === 'pageError') {
559
+ if (entry.type === 'console' && typeof entry.text === 'string') {
560
+ if (entry.messageType === 'error') consoleErrors.push(entry.text)
561
+ else if (consoleSignal(entry.text)) consoleSignals.push(entry.text)
562
+ else if (entry.messageType === 'warning' || entry.messageType === 'warn')
563
+ consoleWarnings.push(entry.text)
564
+ } else if (entry.type === 'event' && entry.method === 'pageError') {
518
565
  const message = messageOf(entry.params)
519
566
  if (message) consoleErrors.push(message)
520
567
  } else if (entry.type === 'before' && Array.isArray(entry.stack) && entry.stack.length) {
@@ -526,9 +573,13 @@ for (const one of found.slice(0, MAX_TRACES)) {
526
573
  }
527
574
  }
528
575
  // Chromium reports every failed request as a console error too; where the page logged something
529
- // of its own about it, that is the line worth reading.
576
+ // of its own about it, that is the line worth reading. Errors lead, then the lines that name a
577
+ // session decision or come from a molecule package (m148), then any other warning — the browser's
578
+ // resource error is last, because it is evidence of a failed request, not of a cause, and the
579
+ // platform's own package saying "logging out" must not be ranked below it.
530
580
  const specific = consoleErrors.filter(function (text) { return !/^Failed to load resource/.test(text) })
531
- const keptConsole = specific.length > 0 ? specific : consoleErrors
581
+ const keptConsole = specific.concat(consoleSignals, consoleWarnings)
582
+ if (specific.length === 0) keptConsole.push.apply(keptConsole, consoleErrors)
532
583
  if (chosen.length === 0 && keptConsole.length === 0 && actions.length === 0) continue
533
584
  process.stdout.write('TRACE ' + JSON.stringify({
534
585
  dir: path.relative(ROOT, path.dirname(one.file)),
@@ -564,6 +615,12 @@ function parseTraceRequest(value) {
564
615
  ...(typeof r.got === 'string' && r.got ? { got: r.got } : {}),
565
616
  ...(typeof r.type === 'string' && r.type ? { type: r.type } : {}),
566
617
  ...(Number.isFinite(Number(r.startedAt)) ? { startedAt: Number(r.startedAt) } : {}),
618
+ // The scheme word only: a raw header value is a live credential and is never parsed in.
619
+ ...(r.sentAuthorization === true ? { sentAuthorization: true } : {}),
620
+ ...(typeof r.authorization === 'string' && r.authorization
621
+ ? { authorization: r.authorization }
622
+ : {}),
623
+ ...(r.sentCookie === true ? { sentCookie: true } : {}),
567
624
  };
568
625
  }
569
626
  /**
@@ -910,7 +967,7 @@ function repeatsEarlierFailure(item, earlier) {
910
967
  }
911
968
  /**
912
969
  * Whether a trace holds evidence the note can explain: a request that got a RECORDED response, a
913
- * real failure (4xx/5xx or a network error), or a console error. A request with no response is
970
+ * real failure (4xx/5xx or a network error), or a console line. A request with no response is
914
971
  * not evidence (m112 +53:46): four of the note's five rows were traces of one `afterEach` line
915
972
  * whose every request was `-1`, so the favorite cause — repeated in all three walks — stayed
916
973
  * hidden in "… and 7 more traced failures".
@@ -921,7 +978,7 @@ function repeatsEarlierFailure(item, earlier) {
921
978
  * `page.goto` timed out ({@link stalledDocumentSubresource}).
922
979
  *
923
980
  * @param item - One traced failure.
924
- * @returns True when the trace holds a request or console error worth a row.
981
+ * @returns True when the trace holds a request or console line worth a row.
925
982
  */
926
983
  function traceRowEvidence(item) {
927
984
  return (item.console.length > 0 ||
@@ -1005,6 +1062,94 @@ function sameOriginPath(request) {
1005
1062
  return null;
1006
1063
  return raw.replace(/\/+$/, '');
1007
1064
  }
1065
+ /**
1066
+ * The sentence a 401/403 on the app's own API gets: which half of the wire refused the request.
1067
+ *
1068
+ * A fixed "read the handler this route reaches" line is the wrong half for a credential refusal.
1069
+ * The request's own `Authorization` header — recorded by the trace reader as its scheme WORD, never
1070
+ * its value — decides: absent means the CLIENT never attached the token (a session store that the
1071
+ * HTTP client does not read, a bare `fetch`, an auth bond the app replaced), while present means
1072
+ * the SERVER refused a token it was sent (scheme or issuer). A 403 is a permission answer, not a
1073
+ * credential one, and says so.
1074
+ *
1075
+ * xr-molecule-flash-m148 (+29:18): `POST /api/articles/…/favorite → 401 {"errors":{"token":["is
1076
+ * missing"]}}` was answered with the handler sentence for six local walks, while the trace proved
1077
+ * the request carried no `Authorization` header at all and the app's hand-rolled session store
1078
+ * never handed its token to the shared client.
1079
+ *
1080
+ * The trace cannot name the file that made the call: a real HAR entry carries the request, response,
1081
+ * `_resourceType`, `_monotonicTime` and a frame ref, but no initiator stack. So the sentence names
1082
+ * the shared client and the auth bond instead of guessing a file.
1083
+ *
1084
+ * @param request - The refused request, a 401 or 403 on the app's own origin.
1085
+ * @returns The sentence the note appends.
1086
+ */
1087
+ function authRefusalSentence(request) {
1088
+ const label = requestLabel(request);
1089
+ // The header's scheme, when it parsed; presence alone is still the server's half of the wire.
1090
+ const carried = request.authorization !== undefined
1091
+ ? `with \`Authorization: ${request.authorization} …\``
1092
+ : 'with an `Authorization` header';
1093
+ if (request.status === 403) {
1094
+ return request.sentAuthorization === true
1095
+ ? `The browser sent \`${label}\` ${carried} and the API answered 403 — the token was ` +
1096
+ 'accepted but this user may not do it: read the route’s role/permission check in your ' +
1097
+ 'own source, and the account the session belongs to.'
1098
+ : `The browser sent \`${label}\` and the API answered 403 — the request carried no ` +
1099
+ '`Authorization` header, so either the client did not attach the token (check that the ' +
1100
+ 'request goes through the app’s HTTP client that the auth bond configures, and that the ' +
1101
+ 'token is stored where the client reads it) or the route refuses this user: read the ' +
1102
+ 'route’s role/permission check in your own source.';
1103
+ }
1104
+ if (request.sentAuthorization === true) {
1105
+ return (`The browser sent \`${label}\` ${carried} and the API refused it: the token was sent and ` +
1106
+ 'rejected — the scheme (the client’s `authScheme`, e.g. `Token` vs `Bearer`) or the ' +
1107
+ 'token’s issuer differs from what the server verifies; read the verify middleware this ' +
1108
+ 'route reaches.');
1109
+ }
1110
+ return (`The browser sent \`${label}\` with NO \`Authorization\` header — the client did not attach ` +
1111
+ 'the token: check that the request goes through the app’s HTTP client that the auth bond ' +
1112
+ 'configures (not a bare `fetch`), and that the token is stored where the client reads it. ' +
1113
+ 'The shared client carries it through `setAuthToken` — the auth bond ' +
1114
+ '(`createDefaultAuthClientWithHttpSync` in `@molecule/app-bonds-default-react`) mirrors the ' +
1115
+ 'auth client’s token there, and a session store of your own has to call it too. ' +
1116
+ (request.sentCookie
1117
+ ? 'The request did carry a `Cookie` header, so if this app signs in with a session cookie ' +
1118
+ 'that is the credential the server did not accept. '
1119
+ : '') +
1120
+ 'The API handler is not the problem.');
1121
+ }
1122
+ /**
1123
+ * The note's credential-refusal advice, one sentence per distinct shape — at most two.
1124
+ *
1125
+ * The shapes are `(status, Authorization scheme, cookie present)`: m148's ten walks repeated one
1126
+ * shape (`401`, no header) on every failing test, and repeating its sentence per row would bury
1127
+ * the fact rather than state it.
1128
+ *
1129
+ * @param items - The traces the note is about.
1130
+ * @returns The sentences to append (each led by a space), or '' when no same-origin 401/403 was
1131
+ * traced.
1132
+ */
1133
+ function authRefusalAdvice(items) {
1134
+ const sentences = [];
1135
+ const seen = new Set();
1136
+ for (const item of items) {
1137
+ for (const request of item.requests) {
1138
+ if (request.status !== 401 && request.status !== 403)
1139
+ continue;
1140
+ if (sameOriginPath(request) === null)
1141
+ continue;
1142
+ const key = `${request.status} ${request.sentAuthorization ? (request.authorization ?? 'header') : 'none'} ${request.sentCookie ? 'cookie' : ''}`;
1143
+ if (seen.has(key))
1144
+ continue;
1145
+ seen.add(key);
1146
+ sentences.push(authRefusalSentence(request));
1147
+ if (sentences.length === 2)
1148
+ return ` ${sentences.join(' ')}`;
1149
+ }
1150
+ }
1151
+ return sentences.length === 0 ? '' : ` ${sentences.join(' ')}`;
1152
+ }
1008
1153
  /**
1009
1154
  * The API path prefixes the traced requests themselves show.
1010
1155
  *
@@ -1112,7 +1257,7 @@ function missingApiPrefixSentence(request) {
1112
1257
  * row falls back to {@link TraceArtifact.lead}: the page's last answered fetch/xhr.
1113
1258
  *
1114
1259
  * WHICH rows it spends is evidence, not artifact order (xr-molecule-flash-m112 +53:46): a row
1115
- * needs a recorded response, a real failure or a console error ({@link traceRowEvidence}), rows are
1260
+ * needs a recorded response, a real failure or a console line ({@link traceRowEvidence}), rows are
1116
1261
  * deduplicated by the test's location, and they are ordered real failure → repeated from an
1117
1262
  * earlier walk → the rest ({@link traceRowTier}). Without that the note spent all five rows on four
1118
1263
  * traces of one `afterEach` line whose every request was `-1`, and left the favorite cause — the
@@ -1136,6 +1281,15 @@ function missingApiPrefixSentence(request) {
1136
1281
  * request that never reached the API. The prefix is READ (the caller passes what the app's client
1137
1282
  * config names; the traces add what their own requests show) and never assumed to be `/api`.
1138
1283
  *
1284
+ * A 401/403 on the app's own origin is answered from the request's own credential record instead
1285
+ * ({@link authRefusalSentence}): no `Authorization` header means the CLIENT never attached the
1286
+ * token (the shared HTTP client's `setAuthToken` is what the auth bond mirrors it into), while a
1287
+ * header the API then refused moves the answer to the server. The handler sentence is kept for
1288
+ * the rest — 404/422/500 — and dropped when every failure was a credential refusal, because a
1289
+ * trace that proves the header was never sent cannot be explained by the handler.
1290
+ * xr-molecule-flash-m148 (+29:18) hit this exactly: six local walks read
1291
+ * `POST …/favorite → 401 {"errors":{"token":["is missing"]}}` as a handler bug.
1292
+ *
1139
1293
  * @param items - The traces {@link parseTraceArtifacts} read.
1140
1294
  * @param target - Where the run went — the deployed host, as the note names it.
1141
1295
  * @param earlierFailures - The failing `file:line`s earlier walks of this suite left, so a repeat
@@ -1186,6 +1340,11 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1186
1340
  const prefixAdvice = missingPrefix.length > 0
1187
1341
  ? ` ${missingPrefix.slice(0, 2).map(missingApiPrefixSentence).join(' ')}`
1188
1342
  : '';
1343
+ // A credential refusal (401/403 on the app's own API) is not the handler's answer: the trace's
1344
+ // own `Authorization` record says whether the CLIENT attached the token at all (m148 +29:18).
1345
+ const authRefusals = failedRequests.filter((request) => sameOriginPath(request) !== null && (request.status === 401 || request.status === 403));
1346
+ const everyFailureAuth = failedRequests.length > 0 && authRefusals.length === failedRequests.length;
1347
+ const authAdvice = authRefusalAdvice(traced);
1189
1348
  // The document subresources that never finished anywhere in this read — the assets a timed-out
1190
1349
  // navigation was waiting on, when there is no failed request to explain the timeout.
1191
1350
  const stalledAssets = [
@@ -1326,9 +1485,14 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1326
1485
  (anyFailed
1327
1486
  ? everyFailureLacksPrefix
1328
1487
  ? ''
1329
- : 'A click that changed nothing is usually one of these requests failing, not the selector: ' +
1330
- 'read the handler this route reaches in your own source, and stop `catch`-ing the request of a ' +
1331
- 'user action silently — log it.'
1488
+ : everyFailureAuth
1489
+ ? // Every failure was a credential refusal: the handler is not the problem, and the
1490
+ // auth advice already names what is (m148 was sent to the handler six walks running).
1491
+ authAdvice
1492
+ : 'A click that changed nothing is usually one of these requests failing, not the selector: ' +
1493
+ 'read the handler this route reaches in your own source, and stop `catch`-ing the request of a ' +
1494
+ 'user action silently — log it.' +
1495
+ authAdvice
1332
1496
  : stalledAssets.length > 0
1333
1497
  ? 'No listed request failed — none got a 4xx/5xx or a network error — but the document did ' +
1334
1498
  'not finish loading because its own subresource never answered. Do not read this as a ' +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-ai-tools",
3
- "version": "1.9.10",
3
+ "version": "1.9.12",
4
4
  "description": "Shared AI agent tools with backend abstraction for sandbox and local execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",