@valve-tech/tx-tracker 0.6.0 → 0.8.0

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 (57) hide show
  1. package/AGENTS.md +237 -0
  2. package/CHANGELOG.md +140 -0
  3. package/README.md +13 -6
  4. package/dist/events.d.ts +309 -0
  5. package/dist/events.d.ts.map +1 -0
  6. package/dist/events.js +132 -0
  7. package/dist/events.js.map +1 -0
  8. package/dist/group-events.d.ts +82 -0
  9. package/dist/group-events.d.ts.map +1 -0
  10. package/dist/group-events.js +47 -0
  11. package/dist/group-events.js.map +1 -0
  12. package/dist/group.d.ts +31 -0
  13. package/dist/group.d.ts.map +1 -0
  14. package/dist/group.js +196 -0
  15. package/dist/group.js.map +1 -0
  16. package/dist/index.d.ts +57 -10
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +52 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/observations.d.ts +169 -0
  21. package/dist/observations.d.ts.map +1 -0
  22. package/dist/observations.js +287 -0
  23. package/dist/observations.js.map +1 -0
  24. package/dist/reorg.d.ts +108 -0
  25. package/dist/reorg.d.ts.map +1 -0
  26. package/dist/reorg.js +125 -0
  27. package/dist/reorg.js.map +1 -0
  28. package/dist/replace-transaction.d.ts +46 -0
  29. package/dist/replace-transaction.d.ts.map +1 -0
  30. package/dist/replace-transaction.js +47 -0
  31. package/dist/replace-transaction.js.map +1 -0
  32. package/dist/selectors.d.ts +78 -0
  33. package/dist/selectors.d.ts.map +1 -0
  34. package/dist/selectors.js +119 -0
  35. package/dist/selectors.js.map +1 -0
  36. package/dist/store.d.ts +166 -0
  37. package/dist/store.d.ts.map +1 -0
  38. package/dist/store.js +110 -0
  39. package/dist/store.js.map +1 -0
  40. package/dist/tracker.d.ts +211 -0
  41. package/dist/tracker.d.ts.map +1 -0
  42. package/dist/tracker.js +1004 -0
  43. package/dist/tracker.js.map +1 -0
  44. package/dist/wait-for-pending.d.ts +41 -0
  45. package/dist/wait-for-pending.d.ts.map +1 -0
  46. package/dist/wait-for-pending.js +71 -0
  47. package/dist/wait-for-pending.js.map +1 -0
  48. package/dist/wait-for-transaction.d.ts +55 -0
  49. package/dist/wait-for-transaction.d.ts.map +1 -0
  50. package/dist/wait-for-transaction.js +72 -0
  51. package/dist/wait-for-transaction.js.map +1 -0
  52. package/dist/watch-transaction.d.ts +57 -0
  53. package/dist/watch-transaction.d.ts.map +1 -0
  54. package/dist/watch-transaction.js +76 -0
  55. package/dist/watch-transaction.js.map +1 -0
  56. package/package.json +6 -1
  57. package/skills/tx-tracker-integration/SKILL.md +198 -0
package/AGENTS.md ADDED
@@ -0,0 +1,237 @@
1
+ # AGENTS.md
2
+
3
+ Terse reference for AI agents (Claude Code, Cursor, Aider, etc.) integrating
4
+ `@valve-tech/tx-tracker`. The full README is for humans; this file is for
5
+ agents that need to ground their work in the package's actual surface
6
+ quickly.
7
+
8
+ ## What this package does
9
+
10
+ Per-tx state machine for EVM chains. Consumes a `ChainSource`'s block +
11
+ mempool stream and emits **neutral observations** (`seen-in-mempool`,
12
+ `seen-in-block`, `replaced-by`, `vanished-from-block`,
13
+ `unseen-for-N-blocks`, `signal-degraded`, `signal-recovered`,
14
+ `stopped`). Three consumption shapes (callback / async iterator /
15
+ snapshot) over one push-based core. Per-method capability disclosure
16
+ keeps the no-silent-downgrade rule.
17
+
18
+ Sibling to `@valve-tech/gas-oracle` — both consume the same
19
+ `ChainSource`, neither depends on the other. One upstream RPC poll
20
+ cycle can feed both.
21
+
22
+ `viem ^2.0.0` is the only external peer. `@valve-tech/chain-source`
23
+ is a runtime dependency.
24
+
25
+ ## Public API
26
+
27
+ All exports live under `src/index.ts` (single subpath; no sub-exports).
28
+
29
+ ```ts
30
+ import {
31
+ createTxTracker, // primary constructor
32
+ createInMemoryStore, // default TxTrackerStore impl
33
+ computeRetentionExpiry, // pure helper
34
+ defaultRetentionBlocks, // 64
35
+ defaultReorgDepthBlocks, // 12
36
+ defaultMaxBulkSubscriptions, // 16
37
+ // pure detectors / matchers
38
+ appendBlock,
39
+ detectDivergences,
40
+ compileSelector,
41
+ matchAll,
42
+ // event builders (mostly internal — exported for store implementers)
43
+ buildStarted, buildSeenInMempool, buildLeftMempool,
44
+ buildSeenInBlock, buildVanishedFromBlock, buildReplacedBy,
45
+ buildUnseenForNBlocks, buildSignalDegraded, buildSignalRecovered,
46
+ buildStopped, buildInitialStatus,
47
+ // types
48
+ type TxTracker,
49
+ type CreateTxTrackerOptions,
50
+ type TrackOptions,
51
+ type BulkTrackOptions,
52
+ type TxMatchEvent,
53
+ type TxSubscription,
54
+ type LostSignalPolicy,
55
+ type TxEvent,
56
+ type TxEventStarted, type TxEventSeenInMempool, type TxEventLeftMempool,
57
+ type TxEventSeenInBlock, type TxEventVanishedFromBlock,
58
+ type TxEventReplacedBy, type TxEventUnseenForNBlocks,
59
+ type TxEventSignalDegraded, type TxEventSignalRecovered,
60
+ type TxEventStopped,
61
+ type TxStatus,
62
+ type Address, type Hash, type At, type Envelope,
63
+ // store types
64
+ type TxTrackerStore,
65
+ type TrackedTxRecord,
66
+ type PersistedSubscription,
67
+ type HashSelector,
68
+ type BulkSelector,
69
+ type InMemoryStoreOptions,
70
+ // reorg
71
+ type BlockSample,
72
+ type BlockDivergence,
73
+ // selectors
74
+ type CompiledSelector,
75
+ type BulkMatchPayload,
76
+ } from '@valve-tech/tx-tracker'
77
+ ```
78
+
79
+ ## Five types you must know
80
+
81
+ | Type | What it is |
82
+ |---|---|
83
+ | `CreateTxTrackerOptions` | Constructor config. Required: `source`, `chainId`. Tuneables: `store`, `lostSignalPolicy`, `reorgDepthBlocks`, `unseenThresholdBlocks`, `maxBulkSubscriptions`, `onError`, `lifecycle`. |
84
+ | `TxEvent` | Discriminated union of 10 variants (see below). Every variant carries `{ hash, chainId, source, at: { blockNumber, timestamp } }`. |
85
+ | `TxStatus` | Cached snapshot returned by `getTxStatus(hash)`. Carries the **last observation** (`lastSeenInBlock`, `lastSeenInMempool`, `replacedBy`, `vanishedAt`) plus housekeeping (`unseenStreak`, `firstObservedAtBlock`, `lastObservedAtBlock`, `capabilities`). |
86
+ | `TxTrackerStore` | Persistence surface. `put` / `get` / `delete` / `listDurable` / `appendEvent` / `readEventLog?`. Default: `createInMemoryStore`. |
87
+ | `BulkSelector` | `{ kind: 'from' \| 'to' \| 'predicate', address?, match? }`. From / to lowercase the address once at compile time. Predicate runs O(N) per tx per tick. |
88
+
89
+ ## The discriminated `TxEvent`
90
+
91
+ Every event carries the same envelope; the `kind` field discriminates
92
+ the variant-specific payload.
93
+
94
+ ```ts
95
+ type TxEvent =
96
+ | { kind: 'started'; capabilities: Capabilities }
97
+ | { kind: 'seen-in-mempool'; bucket: 'pending' | 'queued'; tx: RawTx }
98
+ | { kind: 'left-mempool' }
99
+ | { kind: 'seen-in-block'; blockHash; blockNumber; transactionIndex; confirmations }
100
+ | { kind: 'vanished-from-block'; previousBlockHash; canonicalBlockHash; blockNumber }
101
+ | { kind: 'replaced-by'; replacementHash; replacementBlockNumber: bigint | null }
102
+ | { kind: 'unseen-for-N-blocks'; blocks: number }
103
+ | { kind: 'signal-degraded'; capabilityLost: keyof Capabilities; fallbackSource }
104
+ | { kind: 'signal-recovered'; capabilityRestored: keyof Capabilities }
105
+ | { kind: 'stopped'; reason: 'unsubscribed' | 'retention-expired' | 'tracker-stopped' }
106
+ ```
107
+
108
+ The envelope on every variant:
109
+
110
+ ```ts
111
+ {
112
+ hash: Hash
113
+ chainId: number
114
+ source: 'subscription' | 'block-poll' | 'mempool-snapshot' | 'receipt-poll'
115
+ at: { blockNumber: bigint; timestamp: bigint }
116
+ }
117
+ ```
118
+
119
+ ## Three consumption shapes (consistent across all three)
120
+
121
+ ```ts
122
+ // 1. Snapshot — sub-millisecond, returns null if not tracked
123
+ const status = tracker.getTxStatus(hash)
124
+
125
+ // 2. Callback — returns an unsubscribe handle
126
+ const unsub = tracker.subscribe(hash, (event) => { /* ... */ })
127
+
128
+ // 3. Async iterator — recommended for new code
129
+ for await (const event of tracker.track(hash)) {
130
+ if (event.kind === 'seen-in-block' && event.confirmations >= 6) break
131
+ }
132
+ ```
133
+
134
+ All three back onto the same internal `Subscriptions<TxEvent>` per
135
+ hash, so they see consistent state.
136
+
137
+ ## Bulk subscriptions (indexer-style)
138
+
139
+ ```ts
140
+ const sub = tracker.trackFromAddress(treasuryAddress, { durable: true })
141
+ // raw match stream:
142
+ for await (const m of sub.events()) { /* m: TxMatchEvent */ }
143
+ // per-hash event stream (auto-tracked by default):
144
+ sub.subscribe((event) => { /* TxEvent */ })
145
+ sub.stop() // does NOT stop already-auto-tracked per-hash subs
146
+ ```
147
+
148
+ `trackFromAddress` / `trackToAddress` / `trackPredicate`. Capped at
149
+ `maxBulkSubscriptions: 16` by default.
150
+
151
+ ## Composing with gas-oracle
152
+
153
+ One `ChainSource` shared across both — one upstream RPC poll cycle:
154
+
155
+ ```ts
156
+ import { createChainSource } from '@valve-tech/chain-source'
157
+ import { createGasOracle } from '@valve-tech/gas-oracle'
158
+ import { createTxTracker } from '@valve-tech/tx-tracker'
159
+
160
+ const source = createChainSource({ client })
161
+ const oracle = createGasOracle({ source, chainId: 1 })
162
+ const tracker = createTxTracker({ source, chainId: 1 })
163
+
164
+ source.start(); oracle.start(); tracker.start()
165
+ ```
166
+
167
+ Each surface owns its own lifecycle — `oracle.stop()` does not stop
168
+ the source or the tracker.
169
+
170
+ ## Configuration patterns
171
+
172
+ | Setting | Default | Tune up for | Tune down for |
173
+ |---|---|---|---|
174
+ | `reorgDepthBlocks` | 12 | Weak-finality chains (PoW, small validator sets) | High-finality chains; only care about shallow reorgs |
175
+ | `unseenThresholdBlocks` | 30 | Slow chains (Ethereum: ~6 min) | Fast L2s |
176
+ | `lostSignalPolicy` | `'emit-uncertain'` | (default — loud is correct) | `'silent'` for wallets that don't want capability-churn UI flicker |
177
+ | `createInMemoryStore({ retentionBlocks })` | 64 | Indexers replaying long windows | Wallet UIs |
178
+ | `createInMemoryStore({ eventLogCapacity })` | 256 | Heavy catch-up on restart | Memory-constrained mobile / edge |
179
+
180
+ `reorgDepthBlocks` and retention are in **block-units, not seconds** —
181
+ reorg safety is a depth invariant. Spec §10.1.
182
+
183
+ ## Wire format
184
+
185
+ All numeric fields are `bigint` (block numbers, fees, timestamps).
186
+ `JSON.stringify(event)` will throw without hex-encoding at the wire
187
+ boundary. Durable store implementers MUST hex-encode (`'0x' + n.toString(16)`)
188
+ on write and decode on read. The default in-memory store keeps `bigint`
189
+ end-to-end.
190
+
191
+ ## Capability disclosure
192
+
193
+ `tracker.capabilities()` forwards the source's snapshot:
194
+
195
+ ```ts
196
+ {
197
+ newHeads: 'subscription' | 'poll-only' | 'unavailable'
198
+ newPendingTransactions: 'subscription' | 'poll-only' | 'unavailable'
199
+ txpoolContent: 'available' | 'gated'
200
+ receiptByHash: 'available' | 'unavailable'
201
+ reprobeOnReconnect: boolean
202
+ }
203
+ ```
204
+
205
+ When capabilities change mid-tracking, the tracker emits
206
+ `signal-degraded` / `signal-recovered` per affected key. Consumers that
207
+ need hard inclusion guarantees filter to `event.source === 'subscription'`.
208
+
209
+ ## Examples
210
+
211
+ - `examples/07-tx-tracker.ts` — minimal tracker, no oracle (async iterator)
212
+ - `examples/08-tx-tracker-with-oracle.ts` — shared `ChainSource` between gas-oracle + tracker
213
+ - `examples/09-bulk-from-address.ts` — indexer-style bulk subscription
214
+
215
+ (Examples live under `node_modules/@valve-tech/gas-oracle/examples/` —
216
+ the toolkit's examples directory is hosted by gas-oracle.) Run with
217
+ `yarn tsx examples/07-tx-tracker.ts`.
218
+
219
+ ## Skills (for AI agents)
220
+
221
+ `skills/` directory ships in the npm tarball. If you're an AI agent
222
+ working in a project that has installed this package, look in
223
+ `node_modules/@valve-tech/tx-tracker/skills/tx-tracker-integration/SKILL.md`
224
+ for trigger conditions and integration recipes that go deeper than this
225
+ file.
226
+
227
+ ## Verifying provenance
228
+
229
+ v0.6.0+ ships with SLSA provenance attestation:
230
+
231
+ ```bash
232
+ npm view @valve-tech/tx-tracker@latest --json | jq .dist.attestations
233
+ npm audit signatures
234
+ ```
235
+
236
+ The attestation links the published tarball to the GitHub Actions
237
+ workflow run that built it.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,146 @@ this file.
6
6
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7
7
  and this project adheres to [Semantic Versioning](https://semver.org/).
8
8
 
9
+ ## [0.8.0] — 2026-05-06
10
+
11
+ ### Added
12
+ - `lostSignalPolicy: { strategy: 'receipt-poll-fallback', pollEveryBlocks: N }` runtime. Closes the type-vs-runtime gap from v0.3.x — when a tracked subscription is in a degraded state, the tracker fetches `getReceipt` every N block ticks and emits `seen-in-block` with `source: 'receipt-poll'` on hit. Capability gate: requires `receiptByHash === 'available'`.
13
+ - `withReceipts: true` opt-in receipt enrichment on `TrackOptions`. When set, the tracker pre-fetches the receipt before the per-record block decision and attaches it to `seen-in-block` events via the new `TxEventSeenInBlock.receipt` field. One emit per inclusion — receipt is on the first event, not a follow-up.
14
+ - `tracker.group(hashes, options?)` — cross-tx correlation (spec §18.1). Emits `group-progress` / `group-complete` / `group-failed` / `group-stopped` derived from per-member event streams. Replacement does NOT auto-promote.
15
+ - `watchTransaction({ client, hash, ... })` — one-shot callback convenience export.
16
+ - `waitForTransaction({ client, hash, ... })` — Promise variant of `watchTransaction`. Resolves with discriminated-union outcome (`mined` / `dropped` / `replaced` / `failed`).
17
+ - `waitForPending({ client, hash, timeoutBlocks })` — Promise that resolves on first `seen-in-mempool`; rejects with typed `WaitForPendingTimeoutError` if the hash isn't observed within `timeoutBlocks`.
18
+ - `replaceTransaction({ original, walletClient, newGas })` — same-nonce replacement primitive. Caller-provides-newGas keeps tx-tracker independent of `@valve-tech/gas-oracle`.
19
+
20
+ ### Changed
21
+ - `onBlock` is now async to support pre-fetching receipts before the per-record decision. Stale-block guard added against the resulting interleave window so a delayed pre-fetch can't clobber state advanced by a concurrent block tick.
22
+ - `decideBlockObservation` accepts an optional `prefetchedReceipts: ReadonlyMap<Hash, TransactionReceipt>` parameter (backward-compatible — existing callsites omit it).
23
+
24
+ ## [0.7.0] — 2026-05-06
25
+
26
+ > **The implementation lands.** This is the first release of
27
+ > `@valve-tech/tx-tracker` with a real public surface. Prior versions
28
+ > (v0.0.1 → v0.6.0) were stubs reserving the npm name. The full
29
+ > design contract is at `docs/tx-tracker-spec.md` in the
30
+ > `valve-tech/evm-toolkit` repo.
31
+
32
+ ### Added
33
+
34
+ - **Per-tx state machine** (`createTxTracker`) consuming a
35
+ `ChainSource` for upstream block + mempool signals. Three
36
+ consumption shapes over one push-based core: `getTxStatus(hash)`
37
+ for the cached snapshot, `subscribe(hash, cb)` for callback-style,
38
+ and `track(hash)` for the async-iterator shape — all three back
39
+ onto the same internal stream so they see consistent state.
40
+ - **`TxEvent` discriminated union** (spec §6) with neutral
41
+ observation kinds: `started`, `seen-in-mempool`, `left-mempool`,
42
+ `seen-in-block`, `vanished-from-block`, `replaced-by`,
43
+ `unseen-for-N-blocks`, `signal-degraded`, `signal-recovered`,
44
+ `stopped`. Every event carries an envelope (`hash`, `chainId`,
45
+ `source`, `at: { blockNumber, timestamp }`) so consumers can
46
+ apply policy (`'confirmed'`, `'stuck'`, etc.) in their own UX
47
+ voice without the tracker prejudging.
48
+ - **`TxTrackerStore` interface + `createInMemoryStore` default**
49
+ (spec §9, §10). Block-unit retention (`retentionBlocks: 64` by
50
+ default — reorg safety is a depth invariant, not a wall-clock
51
+ invariant), bounded per-hash audit log (`eventLogCapacity: 256`
52
+ by default) for catch-up replay.
53
+ - **Reorg detector** (`detectDivergences`, spec §12) — pure function
54
+ over `BlockSample[]` that flags same-height different-hash
55
+ divergences within `reorgDepthBlocks` (default 12). Ring is
56
+ conservative about heights with no canonical entry — a partial
57
+ canonical sequence does not nuke unrelated ring entries.
58
+ - **Bulk subscriptions** (spec §11): `trackFromAddress`,
59
+ `trackToAddress`, `trackPredicate`. Auto-tracks matched hashes
60
+ by default (`autoTrackMatched: true`) so the per-hash event
61
+ stream is available too. Capped at `maxBulkSubscriptions: 16`.
62
+ - **Capability disclosure** — `tracker.capabilities()` forwards the
63
+ source's snapshot. `signal-degraded` / `signal-recovered` events
64
+ fire on every tracked hash when source-level capability
65
+ transitions cross authority boundaries.
66
+ - **Replacement detection** — caches `(from, nonce)` on first
67
+ observation and emits `replaced-by` when a different hash with
68
+ the same identity appears (mempool: `replacementBlockNumber: null`;
69
+ block: filled-in block number).
70
+ - **`subscribeAll(cb)`** — global stream of every event the tracker
71
+ emits, useful for indexers piping to a single sink.
72
+ - **`AGENTS.md`** + **`skills/tx-tracker-integration/SKILL.md`** for AI
73
+ agents working in downstream projects that import the package. Both
74
+ ship in the npm tarball; the SKILL.md trigger phrases catch
75
+ "track this transaction," "watch tx hash," "stuck transaction," and
76
+ composition questions with `@valve-tech/gas-oracle`.
77
+
78
+ ### Changed
79
+
80
+ - **Coverage hardening pre-1.0.** Eliminated dead defensive branches
81
+ in `reorg.ts` (sort-comparator equal-key arms unreachable after
82
+ the dedup `filter`; `?? 0n` defaults unreachable after the empty-
83
+ array early return). Tightened `capabilityRank`'s input type from
84
+ `string` to a `CapabilityValue` union literal so the switch is
85
+ exhaustive without a default arm.
86
+ - **Test suite up from 75 → 95 tests** (+20). New coverage:
87
+ `trackToAddress`, per-subscription `lostSignalPolicy` overrides,
88
+ durable-subscription persistence (with stub stores), predicate-
89
+ selector + `durable: true` warning, store.appendEvent / store.put
90
+ failure routing through `onError`, bad-block-number handling,
91
+ async iterator queue-vs-waiter ordering and early-break cleanup,
92
+ bulk async iterator drain via `sub.stop()`, multi-sub-on-same-hash
93
+ cleanup semantics, idempotent `stop` / `unsub` / `sub.stop`,
94
+ reorg handler skipping records without `lastSeenInBlock`,
95
+ `findReplacement` raw-nonce fallback when `BigInt()` throws,
96
+ `lifecycle: 'lazy'` accepts-the-option contract.
97
+ - Coverage went **89.23% / 78.59% / 92.13% / 91.84%** stmts / branches
98
+ / funcs / lines → **96.13% / 88.93% / 98.87% / 97.61%**, then to
99
+ **97.22% / 92.7% / 98.9% / 98.12%** after the per-record decision
100
+ logic was extracted into pure functions (see "Refactor" below).
101
+
102
+ ### Refactor
103
+
104
+ - **Per-record decision logic extracted from `tracker.ts` into a new
105
+ pure module `observations.ts`.** The previous shape — two giant
106
+ closures inside `onBlock` / `onMempool` mutating shared state and
107
+ emitting events as a side effect — was a pile of conditionals that
108
+ could only be tested by spinning up the full state machine through
109
+ a stub source. Now `decideBlockObservation` and
110
+ `decideMempoolObservation` are pure functions: literal inputs in,
111
+ `{ events, statusPatch, identityPatch, inMempoolPatch }` out. The
112
+ orchestrator in `tracker.ts` shrank to "compute envelope, loop
113
+ records, call decision fn, merge patch, emit events." Same shape
114
+ as the rest of the toolkit (`reducePollInputs` pure / poll loop
115
+ stateful in gas-oracle; math pure / source stateful in chain-source).
116
+ - **`observations.ts` lands at 100% statements / 100% branches**
117
+ (67/67 stmts, 55/55 branches) covered by 33 fixture-driven unit
118
+ tests in `observations.test.ts`. Each per-record decision arm
119
+ has a dedicated test with literal inputs — no async, no stubs,
120
+ no shared state.
121
+ - `tracker.ts` shrank from 374 statements → 344 (the extracted
122
+ code is gone) and is now mostly orchestration; its branch
123
+ coverage rose from 86.69% → 88.75%.
124
+ - `findReplacement` (closure-based) replaced with pure
125
+ `findReplacementInMempool(snapshot, identity, originalHash)`.
126
+ `cacheIdentityFromTx` (mutation-based) replaced with pure
127
+ `cacheIdentity(current, tx)` returning a patch.
128
+ - **No behavior change.** All 95 pre-refactor integration tests
129
+ continue to pass unchanged; the refactor is internal-only and
130
+ the public API surface is identical.
131
+ - Tracker test suite: 95 → 133 (+38: 33 from `observations.test.ts`
132
+ plus 5 new tracker integration tests covering reorg height-mismatch
133
+ skip and async-iterator multi-waiter drain paths).
134
+
135
+ ### Notes
136
+
137
+ - Implements spec §5–§12 minus the `'receipt-poll-fallback'`
138
+ lostSignalPolicy strategy (the type is accepted; the runtime
139
+ falls back to `'emit-uncertain'` and a follow-up PR adds the
140
+ per-block receipt fetch path).
141
+ - Predicate bulk selectors are silently non-durable per spec §13.2
142
+ (closures don't survive a process boundary). The tracker logs a
143
+ warning via `onError` when a `predicate` selector is registered
144
+ with `durable: true` and persists everything else about the
145
+ selector.
146
+ - `gas-oracle` and `tx-tracker` remain siblings — neither imports
147
+ the other; both consume `@valve-tech/chain-source` directly.
148
+
9
149
  ## [0.6.0] — 2026-05-05
10
150
 
11
151
  ### Notes
package/README.md CHANGED
@@ -1,10 +1,5 @@
1
1
  # @valve-tech/tx-tracker
2
2
 
3
- > **Status: stub (v0.0.1).** This package is a name reservation. The
4
- > implementation lands in v0.1.0. See
5
- > [`docs/tx-tracker-spec.md`](https://github.com/valve-tech/evm-toolkit/blob/main/docs/tx-tracker-spec.md)
6
- > for the full design contract.
7
-
8
3
  Per-tx state machine for EVM chains. Emits **neutral observations** —
9
4
  `seen-in-mempool`, `seen-in-block`, `replaced-by`, `vanished-from-block`,
10
5
  `unseen-for-N-blocks`, `signal-degraded`, `signal-recovered`, `stopped` —
@@ -12,8 +7,11 @@ so wallet UIs, indexers, and relays can write their own interpretations
12
7
  on top. The package itself never says "confirmed" or "stuck"; it gives
13
8
  you the data to decide.
14
9
 
10
+ See
11
+ [`docs/tx-tracker-spec.md`](https://github.com/valve-tech/evm-toolkit/blob/main/docs/tx-tracker-spec.md)
12
+ for the full design contract.
13
+
15
14
  ```ts
16
- // v0.1.0+ shape (not yet implemented):
17
15
  import { createChainSource } from '@valve-tech/chain-source'
18
16
  import { createTxTracker } from '@valve-tech/tx-tracker'
19
17
 
@@ -51,6 +49,15 @@ adapters (callback / async iterator / snapshot).
51
49
  yarn add @valve-tech/tx-tracker @valve-tech/chain-source viem
52
50
  ```
53
51
 
52
+ ## For AI agents
53
+
54
+ This package ships an [`AGENTS.md`](AGENTS.md) reference and a
55
+ [`skills/`](skills/) directory for Claude Code / Cursor skill files
56
+ shipped in the npm tarball. After install, both are reachable at:
57
+
58
+ - `node_modules/@valve-tech/tx-tracker/AGENTS.md`
59
+ - `node_modules/@valve-tech/tx-tracker/skills/tx-tracker-integration/SKILL.md`
60
+
54
61
  ## License
55
62
 
56
63
  MIT