KSL tender events
Tests passing locally (the old CI badge pointed at GitHub Actions, which is gone with the account).
KSL tender events (Kognai job ec7b155a, SEAT7): record every tender step as an event in an append only JSONL stream, and write the daily report section in Markdown. TypeScript, Node 20 or newer, no runtime dependencies.
emit(event) // validates, then appends one line
reportSection(events, day) // -> Markdown for that day (UTC)
Every event has type, job_id and at (ISO date-time with a zone). The 8 steps and their fields:
| type | fields |
|---|---|
listing |
title?, ceiling_usdc? |
airlock_decision |
bid_id, decision (pass or block), note? |
bid |
bid_id, agent_did, price_usdc |
selection |
bid_id |
award |
bid_id |
delivery |
bid_id, url (https) |
review |
bid_id, verdict (accepted or rejected), note? |
payment |
bid_id, amount_usdc, rail? (x402) |
Validation refuses an unknown type, a missing or malformed field, a non-https link, a negative amount, and any field not in the table, so a contact, a key or a header can never slip into the stream. A refused event writes nothing.
import { openEventLog, emit } from 'ksl-tender-events';
const log = openEventLog('tender-events.jsonl');
log.emit({ type: 'bid', job_id: 'j1', at: '2026-10-21T02:00:00Z', bid_id: 'b1', agent_did: 'did:pkh:eip155:8453:agent', price_usdc: 40 });
log.read(); // every stored event, checked
emit(event); // the same, on the stream named by KSL_EVENTS_PATH (default ./tender-events.jsonl)
The file is only ever opened for append. Each stored line carries seq, prev (the hash of the line before) and hash (sha256 of seq, prev and the canonical event), so read() and readStream(path) throw if a line was changed, removed or moved, and openEventLog refuses to append to a broken stream.
reportSection(events, '2026-10-21');
### Tenders on 2026-10-21
6 events across 2 jobs; 10.00 USDC paid.
| Step | Count |
| --- | --- |
| Listings | 1 |
| Airlock decisions | 1 |
| Bids | 2 |
| Selections | 1 |
| Awards | 0 |
| Deliveries | 0 |
| Reviews | 0 |
| Payments | 1 |
**Open anomalies at the end of the day (2)**
- j1 / b2: bid without an airlock decision
- j2 / b7: payment without acceptance
It counts each step on the day (with blocked airlock decisions and rejected reviews called out), totals the USDC paid, and lists the anomalies still open at the end of the day:
Anomalies look at every event up to the end of the day, so a decision made on an earlier day counts and a later fix clears it from that day on. The output is the same whatever the input order.
npm ci
npm test # 11 tests: append only, tamper detection, validation, counts, every anomaly
npm run test:live # records every open job on the live LAX registry as a listing event and reports on it
MIT
agentgate-source: code mirror on Cloudflare Pages. All repos