@blamejs/core 0.14.6 → 0.14.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +3 -2
  3. package/lib/a2a-tasks.js +6 -6
  4. package/lib/agent-event-bus.js +4 -4
  5. package/lib/agent-idempotency.js +6 -6
  6. package/lib/agent-orchestrator.js +9 -9
  7. package/lib/agent-posture-chain.js +10 -10
  8. package/lib/agent-saga.js +6 -7
  9. package/lib/agent-snapshot.js +8 -8
  10. package/lib/agent-stream.js +3 -3
  11. package/lib/agent-tenant.js +4 -4
  12. package/lib/agent-trace.js +5 -5
  13. package/lib/ai-disclosure.js +3 -3
  14. package/lib/ai-input.js +1 -1
  15. package/lib/app.js +2 -2
  16. package/lib/archive-read.js +1 -1
  17. package/lib/archive-tar-read.js +1 -1
  18. package/lib/archive-wrap.js +5 -5
  19. package/lib/audit-tools.js +65 -5
  20. package/lib/audit.js +2 -2
  21. package/lib/auth/acr-vocabulary.js +1 -1
  22. package/lib/auth/ciba.js +4 -4
  23. package/lib/auth/dpop.js +1 -1
  24. package/lib/auth/fal.js +1 -1
  25. package/lib/auth/fido-mds3.js +2 -3
  26. package/lib/auth/jwt-external.js +2 -2
  27. package/lib/auth/oauth.js +10 -10
  28. package/lib/auth/oid4vci.js +8 -8
  29. package/lib/auth/oid4vp.js +1 -1
  30. package/lib/auth/openid-federation.js +6 -6
  31. package/lib/auth/passkey.js +6 -6
  32. package/lib/auth/saml.js +1 -1
  33. package/lib/auth/sd-jwt-vc.js +3 -6
  34. package/lib/backup/index.js +18 -18
  35. package/lib/breach-deadline.js +3 -3
  36. package/lib/cache.js +4 -4
  37. package/lib/calendar.js +7 -7
  38. package/lib/circuit-breaker.js +1 -1
  39. package/lib/cms-codec.js +2 -2
  40. package/lib/compliance.js +14 -14
  41. package/lib/content-credentials.js +3 -3
  42. package/lib/crypto-field.js +58 -21
  43. package/lib/crypto.js +5 -6
  44. package/lib/db-query.js +131 -9
  45. package/lib/db.js +106 -22
  46. package/lib/ddl-change-control.js +2 -2
  47. package/lib/did.js +2 -2
  48. package/lib/dsr.js +4 -4
  49. package/lib/external-db.js +65 -17
  50. package/lib/framework-schema.js +4 -4
  51. package/lib/guard-cidr.js +1 -1
  52. package/lib/guard-image.js +1 -1
  53. package/lib/guard-list-id.js +2 -2
  54. package/lib/guard-list-unsubscribe.js +2 -3
  55. package/lib/guard-time.js +1 -1
  56. package/lib/guard-xml.js +1 -1
  57. package/lib/http-client-cache.js +1 -1
  58. package/lib/iab-tcf.js +4 -4
  59. package/lib/incident-report.js +150 -0
  60. package/lib/json-schema.js +1 -1
  61. package/lib/jtd.js +1 -1
  62. package/lib/mail-auth.js +1 -1
  63. package/lib/mail-bimi.js +1 -1
  64. package/lib/mail-crypto-smime.js +2 -2
  65. package/lib/mail-deploy.js +3 -3
  66. package/lib/mail-server-managesieve.js +2 -2
  67. package/lib/mail-server-mx.js +1 -1
  68. package/lib/mail-server-pop3.js +2 -2
  69. package/lib/mail-server-rate-limit.js +1 -1
  70. package/lib/mail-server-submission.js +1 -1
  71. package/lib/mail-store.js +1 -1
  72. package/lib/mcp.js +7 -7
  73. package/lib/mdoc.js +1 -1
  74. package/lib/metrics.js +10 -10
  75. package/lib/middleware/compose-pipeline.js +1 -1
  76. package/lib/middleware/csrf-protect.js +1 -1
  77. package/lib/middleware/dpop.js +5 -5
  78. package/lib/middleware/idempotency-key.js +21 -22
  79. package/lib/middleware/protected-resource-metadata.js +2 -2
  80. package/lib/network-dns-resolver.js +2 -2
  81. package/lib/network-dns.js +1 -2
  82. package/lib/network-dnssec.js +2 -2
  83. package/lib/network-smtp-policy.js +1 -1
  84. package/lib/network-tls.js +1 -2
  85. package/lib/network-tsig.js +3 -3
  86. package/lib/outbox.js +1 -1
  87. package/lib/pqc-agent.js +1 -1
  88. package/lib/retention.js +1 -1
  89. package/lib/retry.js +1 -1
  90. package/lib/rfc3339.js +2 -2
  91. package/lib/safe-archive.js +2 -2
  92. package/lib/safe-decompress.js +1 -1
  93. package/lib/safe-ical.js +2 -2
  94. package/lib/safe-mime.js +1 -1
  95. package/lib/self-update-standalone-verifier.js +1 -1
  96. package/lib/self-update.js +2 -2
  97. package/lib/standard-webhooks.js +3 -3
  98. package/lib/static.js +1 -1
  99. package/lib/stream-throttle.js +2 -2
  100. package/lib/structured-fields.js +1 -1
  101. package/lib/subject.js +2 -2
  102. package/lib/vault/index.js +64 -1
  103. package/lib/vault/rotate.js +19 -0
  104. package/lib/vault/seal-pem-file.js +1 -1
  105. package/lib/vendor-data.js +1 -1
  106. package/lib/web-push-vapid.js +1 -1
  107. package/lib/webhook.js +1 -1
  108. package/lib/websocket.js +1 -1
  109. package/package.json +1 -1
  110. package/sbom.cdx.json +6 -6
package/CHANGELOG.md CHANGED
@@ -8,6 +8,10 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.14.x
10
10
 
11
+ - v0.14.8 (2026-05-30) — **Source-comment and codebase-check hygiene, plus a new require-block alignment check; no API or behaviour changes.** Internal lint and comment cleanup with no operator-facing surface change. Several codebase-check comments and one stub helper name that described behaviour the check no longer has are corrected; an unused lint-suppression class and a set of stale duplicate-cluster qualifiers (functions that were since renamed or extracted) are pruned or re-pointed. Fifty-nine `// allow:` markers that named the byte-size or time-literal check on values those checks no longer flag are removed, and twenty self-negating rationales on markers the time check genuinely fires on are rewritten to say why the value coincidentally matches. A new codebase check holds top-of-file require blocks to consistent `=` column alignment, with the files that currently carry drift listed as a migration allowlist. No exported API, error code, wire format, or runtime behaviour changes. **Changed:** *Lint-suppression and codebase-check comment cleanup* — Corrected codebase-check comments that overstated their check's scope (a duplicate-code threshold described as three files when the advisory threshold is two, a narrowed byte-literal check carrying its pre-narrowing description, and a deferred-scan helper named as though it enforced a guarantee it does not yet provide). Removed an unused lint-suppression class and its one dead in-code marker, and pruned or re-pointed stale duplicate-cluster qualifiers that named functions since renamed or extracted into shared helpers. Removed fifty-nine `// allow:` markers that suppressed nothing, and rewrote twenty self-negating marker rationales (which read "not seconds" while sitting on a value the time check fires on) to explain the coincidental match. Source-comment and test hygiene only. **Detectors:** *Require-block `=` alignment check* — A new codebase check flags a top-of-file require block that mixes its `=` column alignment — a fittable line whose `=` drifts off the column the rest of the block shares. Compact single-space blocks are exempt (only blocks that declare alignment intent are checked), as are destructures and long names physically too wide to reach the column, and blank- or comment-separated tiers align independently. The files that currently carry such drift are an explicit migration allowlist, reflowed over time; new code is held to the rule.
12
+
13
+ - v0.14.7 (2026-05-30) — **Storage and audit-trail hardening: queries are gated to declared columns, raw SQL refuses embedded literals, the database key is bound to its location, sealed-column lookup hashes gain a keyed mode, audit-chain purges can require dual control, and breach deadlines ship a running clock.** This release tightens the data and audit layers against a set of failure modes that were previously reachable. Database queries are now checked against the columns a table declared in its schema: a reference to an undeclared column fails closed by default instead of silently matching nothing, and the `whereRaw` escape hatch refuses an embedded string literal so values bind through placeholders. The database encryption key is sealed with its purpose, data directory, and key path as additional authenticated data, so a key file cannot be relocated to another deployment and unsealed there; an older key without that binding upgrades itself on first load. Sealed-column equality-lookup hashes can now be computed as a keyed MAC (HMAC-SHAKE256) off a per-deployment key, making the lookup hash unforgeable without that key, while the salted-SHA3 default is unchanged. Purging the tamper-evident audit chain can be placed under a two-authorizer dual-control grant so one operator cannot erase it alone, and database credential-rejection audits now record which relation the rejected credential tried to reach. Finally, breach-notification deadlines get a running clock that raises approaching and passed alerts as each regime's window elapses. One behavior change to note: the column gate defaults to reject — if a service issues queries against columns it did not declare in its schema, set `db.init({ columnGate: "warn" })` (audited, allowed) or `"off"` while the schema is reconciled. **Added:** *Column-membership gate on every query* — `b.db.from(table)` now checks each referenced column against the table's declared schema. The mode is set with `db.init({ columnGate: "reject" | "warn" | "off" })` (default `reject`), and `query.allowedColumns([...])` narrows a single query to an explicit allowlist that is always enforced. `b.db.getDeclaredColumns(table)` returns a table's declared column names (or `null` for an unknown table). This is defense in depth against typo'd or caller-influenced column names reaching the SQL layer (CWE-89). · *Keyed mode for sealed-column lookup hashes* — Equality-lookup ("derived") hashes for sealed columns can be computed as `hmac-shake256` — a keyed MAC over a per-deployment key — instead of the default `salted-sha3`. Set it per table with `cryptoField.registerTable(name, { derivedHashMode: "hmac-shake256" })` or per column with `{ from, mode: "hmac-shake256" }`. The keyed hash is unforgeable and un-correlatable without the deployment's MAC key, which raises the bar against offline lookup-table attacks on low-entropy sealed values (CWE-916). `b.vault.getDerivedHashMacKey()` exposes the 32-byte per-deployment key; it is created on first use and re-sealed across key rotation automatically. · *Dual-control gate on audit-chain purge* — `b.auditTools.purge` accepts `dualControlGrant`. When `audit_log` is placed under dual control, a verified archive and `confirm: true` are no longer sufficient: the purge additionally requires a consumed m-of-n grant whose action is bound to the purge, so a grant minted for another operation cannot be replayed and one operator cannot erase the tamper-evident chain alone (NIST SP 800-53 AU-9, separation of duties). · *Running clock for breach-notification deadlines* — `b.incident.report.createDeadlineClock({ notify, approachThresholds })` tracks open incidents and raises `deadline_approaching` and `deadline_passed` alerts as each regime's window elapses (GDPR 72h, DORA, NIS2, and the rest of the registry). Alerts are deduplicated per incident and stage, suppressed once a submission stage is acknowledged, and the clock can run on an interval or be ticked manually. **Changed:** *Queries against undeclared columns now fail closed by default* — The column gate defaults to `reject`: a query that references a column the table did not declare throws rather than silently matching nothing. A service that intentionally queries undeclared columns can set `db.init({ columnGate: "warn" })` to audit and allow, or `"off"` to disable the gate, while its schema is reconciled. Framework-declared columns (including `_id` and derived-hash columns) are always members. **Security:** *Database encryption key bound to its location* — `db.key.enc` is sealed with additional authenticated data over its purpose, resolved data directory, and resolved key path. A sealed key copied to a different deployment or path no longer unseals there — the AEAD authentication fails — which prevents silent key relocation. A legacy key sealed without this binding is detected and re-sealed in the bound format on first load, with no operator action required. · *`whereRaw` refuses embedded string literals* — `whereRaw(sql, params)` and `WhereBuilder.raw(sql, params)` reject a raw fragment containing a string literal (`'...'`); values must bind through the `params` array. A static, operator-controlled literal can opt in with `{ allowLiterals: true }`. This closes a path where a value concatenated into a raw fragment would reintroduce SQL injection (CWE-89). · *Credential-rejection audits record the attempted relation* — A `db.auth.failed` audit row (SQLSTATE 28000 / 28P01 / 42501) now carries `attemptedTable`, the relation the rejected credential tried to reach, extracted defensively from the statement. Triage can scope the blast radius of a credential-abuse event without correlating back to the raw SQL log (CWE-778). **Detectors:** *Audit-purge dual-control gate* — A new check fails the build if a call to `purgeAuditChain` appears in a file that does not also route through the dual-control gate, so a future caller cannot physically delete chain rows without two-authorizer enforcement. · *Raw-SQL literal/interpolation guard* — A new check fails the build on a `whereRaw` / `.raw` call whose SQL argument is built by template interpolation or string concatenation, keeping the bound-params discipline enforceable in framework code. · *Hand-rolled lookup-hash guard* — A new check fails the build if a sealed-column lookup hash is derived from the per-deployment salt outside the canonical helper, so call sites cannot bypass the keyed-mode and per-column mode policy. · *Auth-audit attempted-relation guard* — A new check fails the build if a `db.auth.failed` audit is emitted in a file that does not name `attemptedTable`, so the forensic field cannot be dropped from a future emitter.
14
+
11
15
  - v0.14.6 (2026-05-30) — **Access-refusal middleware can return RFC 9457 problem+json or a custom response, and several documented-but-uncallable APIs are now reachable.** Every access-refusal middleware — the auth gates (bearer, DPoP, mTLS, AAL, bound-key), CSRF, CORS, rate-limit, bot-guard, age-gate, the host and network allowlists, and the method and content-type gates — now accepts two uniform options: `problemDetails: true` returns an RFC 9457 `application/problem+json` body, and `onDeny(req, res, info)` hands the response to the caller. With neither set the refusal is byte-for-byte what it was, so this is a drop-in change that lets a service standardize one error envelope across its API instead of working around each middleware's hardcoded body. Alongside that: `b.middleware.requireBoundKey` is now exported (it was documented and tested but never wired into the middleware surface), `b.middleware.bearerAuth` accepts `requiredScopes` (previously rejected at construction, which made its scope-enforcement path unreachable), API-key refusals send the RFC 6750 challenge code that matches the failure, two documented call paths that named a missing namespace segment are corrected, and the release flow now flags stale GitHub Actions and vendored bundles — with a ready-to-paste pin — before a dependency PR is needed. **Added:** *Uniform `onDeny` and `problemDetails` options on every access-refusal middleware* — Each request-lifecycle middleware that refuses a request now takes `problemDetails: true` to emit an RFC 9457 `application/problem+json` body (composing `b.problemDetails`) and `onDeny(req, res, info)` to take over the response entirely; `info` carries the status, a machine reason, and the middleware-specific fields. The deny-path response headers (`Allow`, `WWW-Authenticate`, `Retry-After`, `Accept`) survive every mode. When neither option is set the response is unchanged. Covers `requireAuth`, `requireAal`, `requireMethods`, `requireContentType`, `requireMtls`, `requireBoundKey`, `bearerAuth`, `dpop`, `csrfProtect`, `fetchMetadata`, `botGuard`, `ageGate`, `hostAllowlist`, `networkAllowlist`, `cors`, `rateLimit`, and `dailyByteQuota` (whose existing `onExceeded` keeps working as an alias of `onDeny`). **Fixed:** *`b.middleware.requireBoundKey` is now callable* — The Bearer-API-key middleware was documented (with examples and tests) but never exported on `b.middleware`, so `b.middleware.requireBoundKey(...)` threw `undefined is not a function`. It is now wired into the middleware surface. · *`b.middleware.bearerAuth` accepts `requiredScopes`* — The RFC 6750 scope-enforcement path read `opts.requiredScopes`, but the option was rejected at construction with `unknown option`, making the 403 `insufficient_scope` behavior unreachable. `requiredScopes` is now an accepted option. · *RFC 6750 challenge codes on API-key refusals* — `b.middleware.requireBoundKey` now sends the `WWW-Authenticate` error code that matches the failure: `insufficient_scope` on a 403 missing-scope, `invalid_token` on an unknown or revoked token, and no error code on a 401 that presented no credentials (RFC 6750 §3). It previously sent `invalid_request` for every refusal. · *Corrected two documented call paths* — The compliance and network references named a path that dropped a namespace segment: the conformity-assessment scaffold is at `b.cra.report.conformityAssessment` (not `b.cra.conformityAssessment`), and the per-socket tuning helper is at `b.network.socket.applyToSocket` (not `b.network.applyToSocket`). The documented signatures now match the callable paths. · *GitHub Actions pins refreshed* — `github/codeql-action` 4.35.5 to 4.36.0, and `docker/login-action`, `docker/setup-buildx-action`, and `docker/setup-qemu-action` to their latest releases. **Detectors:** *`@primitive` reachability gate* — A new check resolves every documented `b.X.Y` primitive against the actual public surface and fails the build when a documented path is not callable (factory-instance shorthands excluded). This is the gate that would have caught the `requireBoundKey` and call-path issues above. · *Deny-path composition gate* — A new check requires every access-refusal middleware to route its refusal through the shared deny-response writer, so a future middleware cannot reintroduce a hardcoded body that locks callers out of `onDeny` / `problemDetails`. · *Actions and vendor currency in the release flow* — The release flow now fails the cut when a SHA-pinned GitHub Action or a vendored bundle is behind its latest upstream release. The actions report prints a ready-to-paste `owner/repo@<sha> # vX.Y.Z` pin and every file and line that uses it, so the bump is copy-paste rather than an after-the-fact dependency PR. Transient registry or API errors stay advisory so a flaky network response does not block an unrelated release.
12
16
 
13
17
  - v0.14.5 (2026-05-30) — **Finished cleaning up the mislabeled byte-literal lint suppressions, with no API or behavior changes.** A follow-up to the byte-literal lint tightening. The remaining suppression comments that named the byte-literal check on values that are not byte sizes — JSON-RPC error codes, HTTP status codes, octet ranges, day-in-milliseconds constants — are removed, keeping their explanatory text and any correctly-named companion suppression. Every byte-literal suppression that remains is now on genuine 1024-scale byte arithmetic. Source-comment hygiene only. **Changed:** *Remaining mislabeled byte-literal suppressions removed* — The byte-literal lint was previously a check on any multiple-of-8 integer, so suppression comments naming it were scattered across non-byte values. The last of those (in a handful of files, in mixed comment formats) are now removed — their explanatory text is retained as plain comments, and any correctly-named companion suppression is kept. The only byte-literal suppressions that remain are on genuine 1024-scale byte arithmetic. No change to any exported API, error code, wire format, or runtime behavior.
package/README.md CHANGED
@@ -61,7 +61,7 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
61
61
  ### Data layer
62
62
 
63
63
  - **SQLite with sealed-by-default columns** — `b.db`, migrations, seeders, atomic-file writes
64
- - **Chainable query builder** — atomic `.increment(col, delta)`, closure-form `.whereGroup` / top-level `.orWhere` OR composition, `.search(fields, term)` LIKE-OR with safe `%`/`_` ESCAPE handling, `.paginate(opts)` returning `{ items, total, page, totalPages }`
64
+ - **Chainable query builder** — atomic `.increment(col, delta)`, closure-form `.whereGroup` / top-level `.orWhere` OR composition, `.search(fields, term)` LIKE-OR with safe `%`/`_` ESCAPE handling, `.paginate(opts)` returning `{ items, total, page, totalPages }`; a column-membership gate (`db.init({ columnGate })`, default reject) fails a query closed when it names a column the table never declared, and `whereRaw` refuses an embedded string literal so values bind through placeholders
65
65
  - **Mongo-style document-store facade** — `b.db.collection(name, opts?)` with `$set` / `$inc` / `$unset` / `$eq` / `$ne` / `$gt` / `$gte` / `$lt` / `$lte` / `$in` / `$like`; schemaless-document opts via `overflow: "<col>"` (folds unknown fields into a JSON-text column; rewrites `WHERE` on virtual fields to `JSON_EXTRACT`), `jsonColumns: [...]` (auto-stringify on write + parse via `b.safeJson` on read), `sealedFields: { email: "emailHash" }` (co-locates a `b.cryptoField` sealed-column / derived-hash declaration so plaintext lookups auto-rewrite to hash-column lookups)
66
66
  - **DB lifecycle** — in-memory encrypted snapshot via `b.db.snapshot()`; standalone encrypted-DB-file lifecycle (`b.db.fileLifecycle({ dataDir, vault })` — decrypt-to-tmpfs, periodic re-encrypt flush, graceful shutdown — same envelope as `b.db`, no schema/audit-chain coupling); `db.init` opt-outs `frameworkTables: false` / `auditSigning: false` and path overrides `encryptedDbPath` / `encryptedDbName` / `dbKeyPath`
67
67
  - **External RDBMS** — bring-your-own Postgres / MySQL with pool tuning + role-aware connect + read-replica routing (`b.externalDb`); declarative role-narrowed views and Postgres row-level-security migrations (`b.db.declareView`, `b.db.declareRowPolicy`)
@@ -98,7 +98,8 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
98
98
  - **At-rest envelope** — envelope-versioned PQC (ML-KEM-1024 + P-384 hybrid, XChaCha20-Poly1305, SHAKE256); vault sealing (`b.crypto`, `b.vault`)
99
99
  - **Power-on self-test** — `b.crypto.selfTest()` runs FIPS 140-3-style integrity checks: NIST FIPS 202 known-answer tests (SHA3-256/512, SHAKE256), AEAD round-trip + tamper-detect, and ML-KEM-1024 / ML-DSA-87 / SLH-DSA-SHAKE-256f pairwise-consistency + negative tests; fails closed (throws) on any mismatch
100
100
  - **Field-level + crypto-shred** — `b.cryptoField.eraseRow`; per-column data residency tagging + per-row keys (`K_row = HKDF(K_table, rowId)`) so erasing the per-row key makes WAL / replica residuals undecryptable (`b.cryptoField.declareColumnResidency`, `b.cryptoField.declarePerRowKey`)
101
- - **AAD-bound sealed columns** — AEAD tag tied to `(table, rowId, column, schemaVersion)`; copy-paste between rows or schema-version replay surfaces as refused decrypt (`b.vault.aad`)
101
+ - **AAD-bound sealed columns** — AEAD tag tied to `(table, rowId, column, schemaVersion)`; copy-paste between rows or schema-version replay surfaces as refused decrypt (`b.vault.aad`). The database encryption key is sealed the same way — bound to its purpose, data directory, and key path — so a relocated key file fails to unseal; an older unbound key upgrades itself on first load
102
+ - **Keyed lookup hashes** — sealed-column equality-lookup hashes default to salted SHA3-512 and can opt into a keyed `hmac-shake256` MAC off a per-deployment key (`cryptoField.registerTable({ derivedHashMode })`, `b.vault.getDerivedHashMacKey`), making the lookup hash unforgeable and un-correlatable across deployments
102
103
  - **Signed webhooks + API encryption** — SLH-DSA-SHAKE-256f default; ML-DSA-65 opt-in; ECIES API encryption (`b.webhook`, `b.crypto`)
103
104
  - **HPKE / HTTP signatures** — RFC 9180 HPKE with ML-KEM-1024 + HKDF-SHA3-512 + ChaCha20-Poly1305 (`b.crypto.hpke`); RFC 9421 HTTP Message Signatures with derived components and ed25519 / ML-DSA-65 (`b.crypto.httpSig`); RFC 9530 Content-Digest / Repr-Digest body-integrity fields (SHA-256 / SHA-512, legacy algorithms refused — `b.contentDigest`) to sign the digest rather than the whole body
104
105
  - **X-Wing hybrid KEM** — `b.crypto.xwing` (draft-connolly-cfrg-xwing-kem, experimental): ML-KEM-768 + X25519 bound by SHA3-256, secure if either component holds — the conservative key-encapsulation shape for migrating off classical ECDH. `keygen` / `encapsulate` / `decapsulate` with a 1216-byte public key, 1120-byte ciphertext, and 32-byte shared secret
package/lib/a2a-tasks.js CHANGED
@@ -62,17 +62,17 @@ var A2aTasksError = defineClass("A2aTasksError", { alwaysPermanent: true });
62
62
  var JSONRPC_VERSION = "2.0";
63
63
 
64
64
  // JSON-RPC 2.0 fixed error codes — A2A inherits these.
65
- var JSONRPC_PARSE_ERROR = -32700; // allow:raw-time-literal — not seconds
66
- var JSONRPC_INVALID_REQUEST = -32600; // allow:raw-time-literal — not seconds
67
- var JSONRPC_METHOD_NOT_FOUND = -32601; // allow:raw-time-literal — not seconds
68
- var JSONRPC_INVALID_PARAMS = -32602; // allow:raw-time-literal — not seconds
69
- var JSONRPC_INTERNAL_ERROR = -32603; // allow:raw-time-literal — not seconds
65
+ var JSONRPC_PARSE_ERROR = -32700; // allow:raw-time-literal — JSON-RPC error code -32700; coincidental multiple-of-60, not a time value, C.TIME N/A
66
+ var JSONRPC_INVALID_REQUEST = -32600;
67
+ var JSONRPC_METHOD_NOT_FOUND = -32601;
68
+ var JSONRPC_INVALID_PARAMS = -32602;
69
+ var JSONRPC_INTERNAL_ERROR = -32603;
70
70
 
71
71
  // A2A-specific error codes per the spec's task-error vocabulary.
72
72
  // A2A_TASK_NOT_FOUND (-32002) + A2A_TASK_NOT_CANCELABLE (-32003) are
73
73
  // raised by operator handlers — they're reserved here for documentation
74
74
  // purposes only.
75
- var A2A_SCOPE_DENIED = -32001; // allow:raw-time-literal — not seconds
75
+ var A2A_SCOPE_DENIED = -32001;
76
76
 
77
77
  var ALLOWED_METHODS = Object.freeze(["tasks/send", "tasks/get", "tasks/cancel"]);
78
78
 
@@ -136,7 +136,7 @@ function _registerTopic(topics, name, topicOpts, auditImpl) {
136
136
  throw new AgentEventBusError("agent-event-bus/bad-schema",
137
137
  "registerTopic: schema required (flat key→type map)");
138
138
  }
139
- // BUG-12 — `kind` is now captured on register so listTopics's kind
139
+ // `kind` is now captured on register so listTopics's kind
140
140
  // filter actually matches. Prior shape never set entry.kind, so the
141
141
  // filter at args.kind was dead. Default value derives from the
142
142
  // dotted topic name's first segment ("mail.scan.x" → "mail"), giving
@@ -164,7 +164,7 @@ function _registerTopic(topics, name, topicOpts, auditImpl) {
164
164
  });
165
165
  }
166
166
 
167
- // SUBSTRATE-22 — operators reloading a module (test runners between
167
+ // Operators reloading a module (test runners between
168
168
  // runs, hot-reload tools, multi-tenant onboarding flows that
169
169
  // register-deregister topics) need a clean unregister path; without
170
170
  // it the second register throws topic-duplicate and the operator is
@@ -183,7 +183,7 @@ function _unregisterTopic(topics, name, auditImpl) {
183
183
  function _listTopics(topics, args, permissions) {
184
184
  // Permission gate: list-topics requires no special scope by default;
185
185
  // operator can wrap with their own permissions instance for stricter.
186
- // BUG-12 — kind filter now matches because register captures kind.
186
+ // Kind filter now matches because register captures kind.
187
187
  var out = [];
188
188
  topics.forEach(function (entry) {
189
189
  if (args.kind && entry.kind !== args.kind) return;
@@ -229,7 +229,7 @@ async function _publish(topics, pubsub, name, payload, pOpts, permissions, audit
229
229
  }
230
230
  // Schema validation.
231
231
  guardEventBusPayload.validate(payload, entry.schema);
232
- // SUBSTRATE-6 — when a topic is tenant-scoped, require the publisher
232
+ // When a topic is tenant-scoped, require the publisher
233
233
  // to declare a tenantId BEFORE the event reaches the durable bus
234
234
  // backend. Prior shape allowed `wrapped._tenantId: null` to land on
235
235
  // the bus, and the receive-side drop only fired AFTER persistence —
@@ -158,7 +158,7 @@ function create(opts) {
158
158
  return {
159
159
  get: function (method, actorId, key) { return _get(store, method, actorId, key, auditImpl, ttlMs, maxResultBytes); },
160
160
  put: function (method, actorId, key, result, putOpts) { return _put(store, method, actorId, key, result, putOpts || {}, ttlMs, maxResultBytes, fingerprintArgs, auditImpl); },
161
- // SUBSTRATE-4 — putIfAbsent gates concurrent retries at the cache
161
+ // putIfAbsent gates concurrent retries at the cache
162
162
  // boundary so only one consumer runs the handler. Operator wraps:
163
163
  // var claim = await idem.putIfAbsent(method, actor, key, args);
164
164
  // if (claim.alreadyClaimed) return claim.result; // another retry won
@@ -173,7 +173,7 @@ function create(opts) {
173
173
  };
174
174
  }
175
175
 
176
- // SUBSTRATE-4 — atomic claim/check/run pattern. Returns one of:
176
+ // Atomic claim/check/run pattern. Returns one of:
177
177
  // { alreadyClaimed: false, fingerprint } — caller runs the handler
178
178
  // { alreadyClaimed: true, pending: true } — another in-flight claim holds the slot
179
179
  // { alreadyClaimed: true, result: <cached> } — prior handler completed; cached result
@@ -299,7 +299,7 @@ async function _get(store, method, actorId, key, auditImpl, ttlMs, maxResultByte
299
299
  throw new AgentIdempotencyError("agent-idempotency/corrupt-result",
300
300
  "get: cached result failed to parse — " + (e && e.message ? e.message : String(e)));
301
301
  }
302
- // SUBSTRATE-12 — atomic replayCount increment. The prior shape
302
+ // Atomic replayCount increment. The prior shape
303
303
  // (read row → mutate → put row) raced two concurrent retries: each
304
304
  // saw replayCount=N, both wrote replayCount=N+1, so the counter
305
305
  // missed bumps and the put-with-fresh-result race-clobbered prior
@@ -439,7 +439,7 @@ function _fingerprintArgs(args) {
439
439
  // unique fingerprint and defeat the args-mismatch defense. Strip
440
440
  // _traceContext (varies per-hop, doesn't change result).
441
441
  //
442
- // SUBSTRATE-11 — DO NOT strip _postureChain. The prior shape ignored
442
+ // DO NOT strip _postureChain. The prior shape ignored
443
443
  // _postureChain.postureSet, so a request made under
444
444
  // postureSet:["hipaa","pci-dss"] cached the same result that a
445
445
  // downgrade attempt under postureSet:["pci-dss"] would replay
@@ -493,7 +493,7 @@ function _inMemoryBackend(maxEntries) {
493
493
  map.set(_k(method, actorId, hash), row);
494
494
  return Promise.resolve();
495
495
  },
496
- // SUBSTRATE-4 — atomic insert. Map.set is synchronous so the
496
+ // Atomic insert. Map.set is synchronous so the
497
497
  // get+set pair below is naturally race-free within the in-memory
498
498
  // backend (V8 single-threaded). Returns true when inserted, false
499
499
  // when the row already exists.
@@ -503,7 +503,7 @@ function _inMemoryBackend(maxEntries) {
503
503
  map.set(k, row);
504
504
  return Promise.resolve(true);
505
505
  },
506
- // SUBSTRATE-12 — atomic replayCount increment. Operators wiring
506
+ // Atomic replayCount increment. Operators wiring
507
507
  // a SQL backend implement this with `UPDATE ... SET
508
508
  // replay_count = replay_count + 1 WHERE keyHash = $1 RETURNING *`
509
509
  // — read-modify-write race-free. In-memory backend is naturally
@@ -119,7 +119,7 @@ function _unsealRegistryRow(row) {
119
119
  var DEFAULT_DRAIN_TIMEOUT_MS = C.TIME.minutes(2);
120
120
  var STREAM_ID_RAND_BYTES = 8; // stream-id random-suffix byte length, not a size cap
121
121
  var DEFAULT_PER_CONSUMER_STOP_MS = C.TIME.seconds(5);
122
- // SUBSTRATE-20 — FNV-1a offset basis salted with the first 32 bits of
122
+ // FNV-1a offset basis salted with the first 32 bits of
123
123
  // SHA3-512(vault master). Attackers who don't have read access to the
124
124
  // vault keypair can't compute the salt, so they can't engineer
125
125
  // tenantIds that all map to one shard. Cached per-process; rotation
@@ -183,7 +183,7 @@ function create(opts) {
183
183
  // operator-supplied metadata (kind / tenantId / posture / ...);
184
184
  // every consuming process holds its own runtime map of name → agent.
185
185
  liveAgents: new Map(),
186
- // SUBSTRATE-8 — drain quiesce wiring. Operator passes
186
+ // Drain quiesce wiring. Operator passes
187
187
  // { outbox, sagaInFlightCount, pubsubFlush } via create() so the
188
188
  // drain phase can quiesce real in-flight work, not just stop
189
189
  // consumers. Optional — operators with no outbox / saga / pubsub
@@ -192,7 +192,7 @@ function create(opts) {
192
192
  sagaInFlightCount: typeof opts.sagaInFlightCount === "function" ? opts.sagaInFlightCount : null,
193
193
  pubsubFlush: typeof opts.pubsubFlush === "function" ? opts.pubsubFlush : null,
194
194
  perConsumerStopMs: typeof opts.perConsumerStopMs === "number" ? opts.perConsumerStopMs : DEFAULT_PER_CONSUMER_STOP_MS,
195
- // SUBSTRATE-9 — onTransition handler invalidates election cache
195
+ // onTransition handler invalidates election cache
196
196
  // on lease-lost / acquired / released. Operator opts out via
197
197
  // { cacheElections: false } to always re-query b.cluster.
198
198
  cacheElections: opts.cacheElections !== false,
@@ -205,7 +205,7 @@ function create(opts) {
205
205
  });
206
206
  }
207
207
 
208
- // SUBSTRATE-9 — subscribe to cluster lease transitions so cached
208
+ // Subscribe to cluster lease transitions so cached
209
209
  // election state can't go stale after a partition. b.cluster
210
210
  // .onTransition fires for every lease-acquired / lease-lost / lease-
211
211
  // released event; we invalidate the affected resource's cached
@@ -254,7 +254,7 @@ function create(opts) {
254
254
  * @status stable
255
255
  * @related b.agent.orchestrator.create
256
256
  *
257
- * SUBSTRATE-3 — attach an in-process live agent reference to a row
257
+ * Attach an in-process live agent reference to a row
258
258
  * that already exists in the persistent registry backend. The
259
259
  * canonical boot-phase contract: the *first* process to start a new
260
260
  * agent calls `register()` (writes the backend row + holds the live
@@ -499,7 +499,7 @@ function _spawnSingleConsumer(ctx, agent, queue, topic, maxConcurrency) {
499
499
  * // → integer in [0, 8)
500
500
  */
501
501
  function _saltedFnvBasis() {
502
- // SUBSTRATE-20 — salt FNV-1a offset basis with the vault master so
502
+ // Salt FNV-1a offset basis with the vault master so
503
503
  // an attacker can't engineer tenantIds that all hash to one shard.
504
504
  // Vault-less path (single-process tests / dev) falls back to the
505
505
  // standard FNV offset basis; production deployments with vault
@@ -561,7 +561,7 @@ async function _elect(ctx, args) {
561
561
  });
562
562
  return elec;
563
563
  }
564
- // SUBSTRATE-9 — cluster mode: ALWAYS query truth from b.cluster.
564
+ // Cluster mode: ALWAYS query truth from b.cluster.
565
565
  // The onTransition handler installed in create() invalidates the
566
566
  // cache on every lease event, so a cache hit here is safe (it
567
567
  // means no lease event has fired since the last query). But the
@@ -600,10 +600,10 @@ async function _drain(ctx, args) {
600
600
  var drained = 0;
601
601
  var startedAt = Date.now();
602
602
  var perConsumerMs = ctx.perConsumerStopMs;
603
- // SUBSTRATE-8 — drain phases:
603
+ // Drain phases:
604
604
  // 1. set ctx.draining so streams emit drain-markers + new task
605
605
  // dispatches refuse (consumers re-check on every envelope).
606
- // 2. stop each consumer with BUG-6 per-consumer timeout race —
606
+ // 2. stop each consumer with a per-consumer timeout race —
607
607
  // one hung consumer can't block the full drain budget.
608
608
  // 3. quiesce in-flight: poll outbox.pendingCount + sagaInFlightCount
609
609
  // until 0 OR remaining-budget-ms elapses.
@@ -65,7 +65,7 @@ var AgentPostureChainError = defineClass("AgentPostureChainError", { alwaysPerma
65
65
 
66
66
  var BUILTIN_REGIMES = Object.freeze(["hipaa", "pci-dss", "gdpr", "soc2"]);
67
67
 
68
- // SUBSTRATE-10 — envelope MAC vocabulary. Cross-process envelope
68
+ // Envelope MAC vocabulary. Cross-process envelope
69
69
  // integrity: an attacker with queue / event-bus write access who
70
70
  // strips postureSet to [] and re-sends a saga / sub-agent envelope
71
71
  // can bypass the downgrade refusal in _validate (which only checks
@@ -73,7 +73,7 @@ var BUILTIN_REGIMES = Object.freeze(["hipaa", "pci-dss", "gdpr", "soc2"]);
73
73
  // envelope bytes, computed at appendHop and verified at validate.
74
74
  var ENVELOPE_MAC_LABEL = "blamejs.agent.postureChain/v1";
75
75
  var ENVELOPE_MAC_KEY_BYTES = 32; // HMAC-SHA3-512 keyed bytes
76
- // SUBSTRATE-21 — hop count cap defends infinite recursion across
76
+ // Hop count cap defends infinite recursion across
77
77
  // agent delegation. 16 is the spec default; operators can lower via
78
78
  // opts.maxHopCount but never raise (audit fan-out without a cap is a
79
79
  // DoS class).
@@ -111,7 +111,7 @@ function _resolveMacKey() {
111
111
 
112
112
  function _envelopeMacBytes(envelope) {
113
113
  // Sign every field that downstream consumers verify off the wire,
114
- // except the `_mac` field itself. SUBSTRATE-21 — also includes
114
+ // except the `_mac` field itself. Also includes
115
115
  // hopCount + chainTrail so a hostile rewriter can't roll back the
116
116
  // trail to evade the cap.
117
117
  var payload = {
@@ -163,8 +163,8 @@ function create(opts) {
163
163
  opts.maxHopCount <= DEFAULT_MAX_HOP_COUNT
164
164
  ? Math.floor(opts.maxHopCount)
165
165
  : DEFAULT_MAX_HOP_COUNT;
166
- // SUBSTRATE-10 escape hatch — only operator-confirmed single-process
167
- // unit tests should opt out of envelope MAC. Production / multi-
166
+ // Escape hatch — only single-process unit tests should opt out of
167
+ // envelope MAC. Production / multi-
168
168
  // process / queue-spanning deployments leave the default on; the
169
169
  // gate audit-emits when bypassed so the posture is visible.
170
170
  var requireMac = opts.requireMac !== false;
@@ -257,7 +257,7 @@ function _appendHop(ctx, envelope, hopName) {
257
257
  "appendHop: hopName must be a non-empty string");
258
258
  }
259
259
  var trail = Array.isArray(envelope.chainTrail) ? envelope.chainTrail.slice() : [];
260
- // SUBSTRATE-21 — cap enforced BEFORE the push so the hop-cap throw
260
+ // Cap enforced BEFORE the push so the hop-cap throw
261
261
  // fires consistently regardless of whether the operator inspects
262
262
  // trail.length first. Cap is a hard refusal (no truncation) because
263
263
  // a silently-dropped hop loses audit provenance for the call.
@@ -279,8 +279,8 @@ function _appendHop(ctx, envelope, hopName) {
279
279
  hopCount: trail.length,
280
280
  });
281
281
  guardPostureChain.validate(newEnvelope);
282
- // SUBSTRATE-10 — sign at every hop. Verify-side enforces requireMac.
283
- // ctx.requireMac=false (operator-confirmed test escape hatch) skips
282
+ // Sign at every hop. Verify-side enforces requireMac.
283
+ // ctx.requireMac=false (test escape hatch) skips
284
284
  // the sign so a vault-less test path still works.
285
285
  if (ctx.requireMac) {
286
286
  try {
@@ -301,7 +301,7 @@ function _appendHop(ctx, envelope, hopName) {
301
301
 
302
302
  function _validate(ctx, envelope, agentPostureSet) {
303
303
  guardPostureChain.validate(envelope);
304
- // SUBSTRATE-10 — MAC verification BEFORE any field-based decision so
304
+ // MAC verification BEFORE any field-based decision so
305
305
  // the wire-rewrite attack (postureSet:[] downgrade with valid SHAPE
306
306
  // but no integrity binding) is refused. ctx.requireMac=false skips
307
307
  // verification and emits an audit so the bypass is visible.
@@ -326,7 +326,7 @@ function _validate(ctx, envelope, agentPostureSet) {
326
326
  chainTrail: envelope.chainTrail,
327
327
  });
328
328
  }
329
- // SUBSTRATE-21 — hop cap also enforced at validate-time. A hostile
329
+ // Hop cap also enforced at validate-time. A hostile
330
330
  // envelope might arrive with hopCount > cap if a prior hop's
331
331
  // requireMac was off; refuse here regardless.
332
332
  if (Array.isArray(envelope.chainTrail) && envelope.chainTrail.length > ctx.maxHopCount) {
package/lib/agent-saga.js CHANGED
@@ -58,8 +58,7 @@
58
58
  *
59
59
  * ## No saga-level retry
60
60
  *
61
- * Per the substrate playbook decision (operator-confirmed
62
- * 2026-05-14): saga's value-add is compensation, not retry. If a
61
+ * Saga's value-add is compensation, not retry. If a
63
62
  * step needs retry-with-backoff, the operator wraps `step.run`
64
63
  * with `b.retry` inside the step body. With v0.9.22 idempotency
65
64
  * available, internal retry inside step.run is side-effect-safe.
@@ -109,7 +108,7 @@ var SAGA_ID_RAND_BYTES = 8;
109
108
  function create(config) {
110
109
  guardSagaConfig.validate(config);
111
110
  var auditImpl = config.audit || audit();
112
- // SUBSTRATE-7 — operator wires a stateStore for crash-safe resume.
111
+ // Operator wires a stateStore for crash-safe resume.
113
112
  // Interface: { saveStep, loadResumePoint, markCompleted, markFailed }.
114
113
  // saveStep({sagaId, stepIndex, stepName, state, status}) commits
115
114
  // after each step.run; loadResumePoint(sagaId) returns the resume
@@ -189,7 +188,7 @@ async function _runFrom(config, auditImpl, stateStore, ctx, state, opts, sagaId,
189
188
  });
190
189
  await step.run(ctx, state);
191
190
  completedSteps.push({ step: step, index: i });
192
- // SUBSTRATE-7 — checkpoint after the step.run returns. saveStep
191
+ // Checkpoint after the step.run returns. saveStep
193
192
  // commits the post-step state so a crash before the NEXT step
194
193
  // resumes from i+1. The audit chain records the checkpoint
195
194
  // independently of the operator's stateStore — operator can
@@ -232,7 +231,7 @@ async function _runFrom(config, auditImpl, stateStore, ctx, state, opts, sagaId,
232
231
  message: (stepErr && stepErr.message) || String(stepErr),
233
232
  });
234
233
  var compensationError = null;
235
- // BUG-5 — capture the compensation step that ACTUALLY failed,
234
+ // Capture the compensation step that ACTUALLY failed,
236
235
  // not "completedSteps[completedSteps.length-1].name" which
237
236
  // names the last-COMPLETED step regardless of which compensation
238
237
  // threw. CWE-209-adjacent (information disclosure via wrong
@@ -278,7 +277,7 @@ async function _runFrom(config, auditImpl, stateStore, ctx, state, opts, sagaId,
278
277
  });
279
278
  } catch (_e) { /* drop-silent — audit already records */ }
280
279
  }
281
- // SUBSTRATE-15 — attach cause:stepErr so the original step
280
+ // Attach cause:stepErr so the original step
282
281
  // error stack survives. ES2022 Error.cause is the standard
283
282
  // mechanism; the framework's defineClass-built AgentSagaError
284
283
  // accepts cause via the third arg.
@@ -290,7 +289,7 @@ async function _runFrom(config, auditImpl, stateStore, ctx, state, opts, sagaId,
290
289
  ((compensationError.message) || String(compensationError));
291
290
  }
292
291
  var sagaErr = new AgentSagaError("agent-saga/failed", detailMsg);
293
- // SUBSTRATE-15 — ES2022 Error.cause attaches the originating
292
+ // ES2022 Error.cause attaches the originating
294
293
  // stepErr so operator stack-trace tooling can walk the chain.
295
294
  // defineClass({alwaysPermanent:true}) doesn't accept cause in
296
295
  // its constructor signature; the property assignment after
@@ -60,7 +60,7 @@ var vault = lazyRequire(function () { return require("./vault");
60
60
 
61
61
  var AgentSnapshotError = defineClass("AgentSnapshotError", { alwaysPermanent: true });
62
62
 
63
- // SUBSTRATE-2 — sealed envelopes start with this prefix on disk; the
63
+ // Sealed envelopes start with this prefix on disk; the
64
64
  // loader sniffs it and routes through unseal before guardSnapshotEnvelope
65
65
  // validation. Compatible with operator backends that store the value
66
66
  // as a string (JSON DBs, k/v stores) or wrap it in `{ value: "..." }`.
@@ -113,19 +113,19 @@ function create(opts) {
113
113
  var snapshotIntervalMs = typeof policy.snapshotIntervalMs === "number" ? policy.snapshotIntervalMs : DEFAULT_SNAPSHOT_INTERVAL_MS;
114
114
  var maxSnapshotBytes = typeof policy.maxSnapshotBytes === "number" ? policy.maxSnapshotBytes : DEFAULT_MAX_SNAPSHOT_BYTES;
115
115
  var auditImpl = opts.audit || audit();
116
- // SUBSTRATE-1 — operator may inject `signer` (interface
116
+ // Operator may inject `signer` (interface
117
117
  // `{ sign(bytes) → Buffer, verify(bytes, sig, pubKey?) → boolean }`)
118
118
  // for testing / alternate key custody. Default = b.auditSign when
119
119
  // initialized at boot; refuses persist() with a clear error if
120
120
  // neither is wired so secure-by-default holds.
121
121
  var signer = opts.signer || null;
122
- // SUBSTRATE-2 — operator may inject `sealer` (interface
122
+ // Operator may inject `sealer` (interface
123
123
  // `{ seal(plaintext, aadParts) → string, unseal(value, aadParts) → string }`)
124
124
  // for alternate KMS integration. Default = b.vault.aad. Refused if
125
125
  // neither is wired AND opts.allowPlaintext is not explicitly true
126
126
  // (operator-justified dev / single-tenant deployments only).
127
127
  var sealer = opts.sealer || null;
128
- // SUBSTRATE-18 — operator-supplied restoreHandlers walk the
128
+ // Operator-supplied restoreHandlers walk the
129
129
  // snapshot inFlight + idempotencyCache + orchestratorState segments
130
130
  // and hydrate the corresponding consumer module. Map shape:
131
131
  // { streams, sagas, outboxJobs, busSubscribers, pendingDeliveries,
@@ -300,7 +300,7 @@ async function _takeSnapshot(ctx, snapshotOpts) {
300
300
 
301
301
  async function _persist(ctx, snap) {
302
302
  guardSnapshotEnvelope.validate(snap);
303
- // SUBSTRATE-1 — sign first so a backend that mutates on put() (very
303
+ // Sign first so a backend that mutates on put() (very
304
304
  // common for k/v stores adding metadata) doesn't poison the signed
305
305
  // bytes downstream readers verify.
306
306
  var signer = _resolveSigner(ctx);
@@ -314,7 +314,7 @@ async function _persist(ctx, snap) {
314
314
  // pubkey at verify time).
315
315
  snap.sigPubKey = (typeof signer.getPublicKey === "function" && signer.getPublicKey()) || null;
316
316
 
317
- // SUBSTRATE-2 — seal the entire envelope under AAD that pins
317
+ // Seal the entire envelope under AAD that pins
318
318
  // snapshotId + schemaVersion + tenantId. AAD mismatch on unseal (a
319
319
  // copy-paste attack from one snapshotId's row into another) fails
320
320
  // the Poly1305 tag check; tampered bytes also fail. The sealed
@@ -405,7 +405,7 @@ async function _unwrapAndVerify(ctx, raw, expectedId) {
405
405
  throw new AgentSnapshotError("agent-snapshot/snapshot-id-mismatch",
406
406
  "load: wrapper snapshotId='" + expectedId + "' does not match envelope='" + snap.snapshotId + "'");
407
407
  }
408
- // SUBSTRATE-1 — verify the signature before returning the envelope
408
+ // Verify the signature before returning the envelope
409
409
  // to the caller. Restore-side trust derives from this gate. The
410
410
  // allowPlaintext escape hatch (operator-acknowledged dev mode)
411
411
  // also waives signature verification because there's no key custody
@@ -496,7 +496,7 @@ async function _restore(ctx, snap, restoreOpts) {
496
496
  affectedStreams: (snap.inFlight && snap.inFlight.streams || []).length,
497
497
  });
498
498
  }
499
- // SUBSTRATE-18 — invoke operator-supplied restoreHandlers across
499
+ // Invoke operator-supplied restoreHandlers across
500
500
  // every segment the snapshot envelope carries. Handlers are
501
501
  // declared at create() time; the snapshot primitive owns ordering
502
502
  // (orchestratorState first so live agents register before consumers
@@ -138,7 +138,7 @@ function _makeIterator(ctx) {
138
138
  var done = false;
139
139
  var closed = false;
140
140
  var drained = false;
141
- // SUBSTRATE-14 — track the cursor of the LAST row actually yielded
141
+ // Track the cursor of the LAST row actually yielded
142
142
  // to the consumer. The prior shape called cursor.lastSeenCursor()
143
143
  // at drain-marker emit, which returned the position of the last
144
144
  // FETCHED batch — clients resuming from that cursor SKIPPED every
@@ -167,7 +167,7 @@ function _makeIterator(ctx) {
167
167
  try {
168
168
  if (buffer.length > 0) {
169
169
  var row = buffer.shift();
170
- // SUBSTRATE-14 — record the cursor for this yielded row so a
170
+ // Record the cursor for this yielded row so a
171
171
  // drain that fires BETWEEN buffered yields emits a marker
172
172
  // whose lastSeenCursor matches what the client actually
173
173
  // received. The cursor extraction shape mirrors the
@@ -248,7 +248,7 @@ function _safeAudit(auditImpl, action, actor, metadata) {
248
248
  agentAudit.safeAudit(auditImpl, action, actor, metadata);
249
249
  }
250
250
 
251
- // SUBSTRATE-14 — resolve the resume cursor for a row about to be
251
+ // Resolve the resume cursor for a row about to be
252
252
  // yielded. Operators may attach the cursor per-row (`row._cursor` /
253
253
  // `row.cursor`) OR rely on the cursor's own per-row tracker
254
254
  // (`cursor.cursorForRow(row)`) — both shapes supported.
@@ -222,7 +222,7 @@ async function _unregister(ctx, tenantId, args) {
222
222
  }
223
223
  // Archive default — retain the key + metadata for retention-mandated
224
224
  // restoration. Operator's compliance regime drives archivePolicy.
225
- // SUBSTRATE-19 — persist as a `status: "archived"` row in the same
225
+ // Persist as a `status: "archived"` row in the same
226
226
  // backend rather than only the process-local Map. GDPR Art. 17 +
227
227
  // HIPAA §164.530(j) require the archived state to survive process
228
228
  // restart (auditor pulls a deleted tenant's archival record years
@@ -373,7 +373,7 @@ function _check(ctx, actor, agentTenantId) {
373
373
 
374
374
  // ---- Per-tenant derived key -----------------------------------------------
375
375
  //
376
- // SUBSTRATE-5 — `namespaceHash(label, tenantId)` is a PUBLIC function
376
+ // `namespaceHash(label, tenantId)` is a PUBLIC function
377
377
  // over PUBLIC inputs; an attacker who learns `tenantId` (an account id
378
378
  // surfaced in URLs / API responses) reconstructs every per-tenant key
379
379
  // without any secret material. The defense the docstring promises —
@@ -555,7 +555,7 @@ var TENANT_FIELD_PREFIX = "tnt-v1:";
555
555
  function _tenantFieldKey(tenantId, table) {
556
556
  // 32-byte symmetric key for XChaCha20-Poly1305. _deriveTenantKeyBytes
557
557
  // returns the raw key bound to the vault master + tenantId + purpose
558
- // — see SUBSTRATE-5 commentary above _derivedKey for the threat
558
+ // — see the commentary above _derivedKey for the threat
559
559
  // model that drove this away from public-input-only derivation.
560
560
  return _deriveTenantKeyBytes(tenantId, "cryptoField:" + table);
561
561
  }
@@ -646,7 +646,7 @@ function _unsealRowForTenant(ctx, tenantId, table, row) {
646
646
  if (out[f] !== undefined && out[f] !== null) {
647
647
  try { out[f] = _unsealField(tenantId, table, f, out[f]); }
648
648
  catch (e) {
649
- // BUG-4 — null-on-decrypt-failure was silent; the docstring
649
+ // Null-on-decrypt-failure was silent; the docstring
650
650
  // promised "audit chain surfaces the failure" but no emit ever
651
651
  // ran. Cross-tenant ciphertext replay / tampered row / wrong-
652
652
  // prefix all hit this path; operator audit pipelines need the
@@ -70,7 +70,7 @@ var audit = lazyRequire(function () { return require("./audit"); })
70
70
 
71
71
  var AgentTraceError = defineClass("AgentTraceError", { alwaysPermanent: true });
72
72
 
73
- // SUBSTRATE-24 — once-per-process audit emit on the first tracer
73
+ // Once-per-process audit emit on the first tracer
74
74
  // failure each install fires. Operators get the signal even when
75
75
  // individual span calls are best-effort suppressed.
76
76
  var _failureAuditEmittedFor = Object.create(null);
@@ -122,7 +122,7 @@ function create(opts) {
122
122
  injectIntoEnvelope: function (envelope, span) { return _injectIntoEnvelope(opts.tracing, envelope, span); },
123
123
  extractFromEnvelope: function (envelope) { return _extractFromEnvelope(envelope); },
124
124
  recordResult: function (span, result, error) { return _recordResult(span, result, error, auditImpl); },
125
- // SUBSTRATE-17 — `shouldSample` now takes a traceId so the same
125
+ // `shouldSample` now takes a traceId so the same
126
126
  // trace gets the same decision across hops. Operator-supplied
127
127
  // traceId comes from the W3C `traceparent` header at request-
128
128
  // entry; absent that, falls back to Math.random (start of trace).
@@ -150,7 +150,7 @@ function _startSpan(tracing, name, sopts, auditImpl) {
150
150
  return tracing.startSpan(name, sopts);
151
151
  }
152
152
  } catch (e) {
153
- // SUBSTRATE-24 — tracer failures should not crash the agent's
153
+ // Tracer failures should not crash the agent's
154
154
  // method call; surface the first failure to the audit chain
155
155
  // (rate-limited) so operators get the signal.
156
156
  _emitFirstFailureAudit(auditImpl, "startSpan", e && e.message);
@@ -206,7 +206,7 @@ function _extractFromEnvelope(envelope) {
206
206
 
207
207
  function _recordResult(span, result, error, auditImpl) {
208
208
  if (!span || typeof span !== "object") return;
209
- // SUBSTRATE-24 — surface first occurrence of each span-op failure
209
+ // Surface first occurrence of each span-op failure
210
210
  // via audit so the operator gets the signal. Subsequent failures
211
211
  // stay silent (best-effort) per the operational spec.
212
212
  if (error) {
@@ -228,7 +228,7 @@ function _recordResult(span, result, error, auditImpl) {
228
228
  }
229
229
  }
230
230
 
231
- // SUBSTRATE-17 — deterministic sampling per W3C Trace Context §3.2.3.1.
231
+ // Deterministic sampling per W3C Trace Context §3.2.3.1.
232
232
  // `Math.random` makes child-vs-parent sampling decisions non-coherent:
233
233
  // a parent span sampled OUT can still have child spans sampled IN,
234
234
  // producing orphaned spans operators can't correlate. Hashing the
@@ -133,7 +133,7 @@ function chatbot(session, opts) {
133
133
  var text = typeof opts.text === "string" && opts.text.length > 0
134
134
  ? opts.text
135
135
  : DEFAULT_CHATBOT_TEXT;
136
- // Codex P1A on v0.12.12 PR #163 — "on-request" placement gates on
136
+ // "on-request" placement gates on
137
137
  // the operator's explicit `opts.requested: true` signal. Without
138
138
  // it, "on-request" collapsed into "always" semantics and emitted
139
139
  // every call. The operator wires this from an explicit user
@@ -155,7 +155,7 @@ function chatbot(session, opts) {
155
155
  regulation: "Regulation (EU) 2024/1689",
156
156
  };
157
157
  if (shouldEmit) {
158
- // Codex P1B on v0.12.12 PR #163 — mark the session so subsequent
158
+ // Mark the session so subsequent
159
159
  // calls with the same session under "first-message" placement
160
160
  // see `aiDisclosureEmitted: true` and return shouldEmit=false.
161
161
  // Without this mutation operators had to remember to flip the
@@ -385,7 +385,7 @@ function applyAll(scenario) {
385
385
  "applyAll: scenario.kinds must be a non-empty array of " +
386
386
  "\"chatbot\" / \"deepfake\" / \"emotion\"");
387
387
  }
388
- // Codex P1 on v0.12.25 PR #176 — validate every kind +
388
+ // Validate every kind +
389
389
  // per-kind required field UP FRONT, before any emission.
390
390
  // Previously a later-kind failure (e.g. deepfake missing
391
391
  // contentType, unknown trailing kind) ran AFTER earlier kinds
package/lib/ai-input.js CHANGED
@@ -26,7 +26,7 @@ var audit = require("./audit");
26
26
  var { AiInputError } = require("./framework-error");
27
27
 
28
28
  var SAMPLE_TRUNC = 80; // sample truncation length, not bytes
29
- var CONFIDENCE_BASE = 60; // allow:raw-time-literal — not seconds
29
+ var CONFIDENCE_BASE = 60; // allow:raw-time-literal — confidence-score base 60; coincidental multiple-of-60, not a duration, C.TIME N/A
30
30
 
31
31
  var PATTERNS = [
32
32
  { id: "ignore-prior-instructions", severity: 3, re:
package/lib/app.js CHANGED
@@ -113,7 +113,7 @@ function _resolveMiddlewareOpt(value, allowDefault, name) {
113
113
  if (value === false) {
114
114
  // Operator explicitly disabled this middleware. When it's one of the
115
115
  // security-on-by-default layers (allowDefault), leave an audit trace
116
- // so the weakened posture is visible — Core Rule §3 security defaults
116
+ // so the weakened posture is visible — security defaults
117
117
  // shouldn't be silently opt-out-able. Drop-silent observability sink.
118
118
  if (allowDefault && name) {
119
119
  try {
@@ -227,7 +227,7 @@ async function createApp(opts) {
227
227
  var rateLimitOpts = _resolveMiddlewareOpt(mwConfig.rateLimit, false, "rateLimit");
228
228
  if (rateLimitOpts) router.use(middleware.rateLimit(rateLimitOpts));
229
229
 
230
- // Security middleware wired ON by default (Core Rule §3). Each reads its
230
+ // Security middleware wired ON by default. Each reads its
231
231
  // config from opts.middleware.<name>: pass `false` to opt out (audited
232
232
  // via _resolveMiddlewareOpt), or an object to customize — operator cookie
233
233
  // / field names flow straight through, nothing static is baked in.