ghostlogic-agent-watchdog
v0.1.0-alpha.0
Published
GhostLogic agent watchdog (Node impl) — forensic forwarder for Node-native AI coding agents. Wire-identity agent_id="node-agent-watchdog" per spec; sister to logicd; uses @ghostlogic/ghostseal for all canonical crypto.
Readme
@ghostlogic/agent-watchdog
GhostLogic node-agent-watchdog — forensic forwarder for Node-native AI coding agent contexts. Sister implementation to logicd per the canonical spec at /home/contact/ghostlogic-spec/node-agent.md.
Status: v0.1 alpha — under construction. No customer-facing adapters ship in v0.1. Real adapters (Cursor / Continue / Cline / others) are Phase 2 product calls per
node-agent.md§3. v0.1 is shipping the substrate: canonical envelope, event vocabulary, adapter contract, deterministic mock fixture, enrollment, HTTP ingest with retry/backoff, dead-letter queue, F-WD-010 pause sentinel, heartbeat emitter, CLI shell, and CI gates — every module independently tested.
Authority
All canonical serialization / hashing / sealing / signing goes through @ghostlogic/ghostseal — never re-derived in-tree. Byte-equality with logicd's emission (via the shared conformance corpus) is the load-bearing guarantee.
Primary spec: /home/contact/ghostlogic-spec/node-agent.md. Binding cross-cuts: CONTRACTS.md (Tier 0/2/6), WIRE_PROTOCOL.md (envelope), AUTH_PROTOCOL.md (auth), STATE_MACHINES.md, FAILURE_MODES.md, ghostseal-v1-spec.md. State of truth for the build: PROGRESS.md. Per-iteration spec ledger: SPEC_AUDIT.md. Executable audit gate: tools/spec-audit.ts.
Intentional divergence from logicd
These are not bugs; they are spec-mandated. Each is logged in SPEC_AUDIT.md and asserted by tools/spec-audit.ts.
| Aspect | logicd | node-agent-watchdog | Spec cite |
| --- | --- | --- | --- |
| Event vocabulary | tool_event, heartbeat | tool_call, prompt_mutation, heartbeat | node-agent.md §1; CONTRACTS Tier 2 §83 → §35 |
| event_schema_version literal | "1.1" | "node.1.0" | node-agent.md §2.2 ("distinct from logicd's 1.1, e.g. node.1.0") |
| Compression default | off | gzip on | CONTRACTS Tier 2 §82 (item 82); node-agent.md §2.2 |
| Envelope impl_id | omitted (server infers from agent_id) | always emitted: node-agent-watchdog-<version> | WIRE_PROTOCOL.md §2.1; CONTRACTS §46/§95 |
| Auth | Bearer key | Bearer (v0.1) → OIDC/JWT (Phase 2) | node-agent.md §2.3; AUTH_PROTOCOL §5 |
| Enrollment User-Agent | logicd-enroll/0.4.0 | node-agent-watchdog-enroll/<version> | WIRE_PROTOCOL.md §1.2 |
| Enrollment flow | gle_ token → gl_agent_* key | same shape; node-agent vocabulary in requested_event_types; OIDC reserved for Phase 2 | CONTRACTS Tier 6 §48 / §84 |
| Event base field set | 23 fields including file_path, line_number, byte_offset, sha256, etc. | minimal; node-agent sources are IDE-extension IPC, not tailed JSONL files | node-agent.md §1 |
| ns-epoch fields | Python int | bigint (Number rejected — > 2^53) | ghostseal-v1-spec.md §4 |
| Adapter contract | Adapter ABC + tail_file(path, offset, line_no, tick_seconds) | Adapter interface + defineAdapter() + tailSource(ctx, enqueueEvent) → AdapterHandle | node-agent.md §1 + §3 |
| Heartbeat source sentinel | "logicd" | "node-agent-watchdog" | WIRE_PROTOCOL.md §4.3 |
| Bearer-rotation pause | <state_dir>/paused.json | same — <state_dir>/paused.json (logicd-identical) | INVARIANT_REGISTRY F-WD-010 |
| Auth-fatal exit code | EXIT_AUTH_FATAL = 3 | same — EXIT_AUTH_FATAL = 3 | logicd __main__.py:30 |
| DLQ storage | <state_dir>/dead_letter/<ns>_<bid12>.json.gz | same scheme; replayed entries move to replayed/ (Blackbeard rule) | FAILURE_MODES §4.1 |
| F-WD-014 splitter | implemented in watcher.py | deferred to v0.2 (v0.1 has caller-controlled batch sizing) | FAILURE_MODES §4.1 |
Layout
src/
├── index.ts # AGENT_ID, VERSION, IMPL_ID, EVENT_SCHEMA_VERSION constants
├── envelope.ts # canonical wire envelope (built via ghostseal.computeBatchId)
├── events.ts # NodeAgentEvent union + makeToolCall / makePromptMutation / makeHeartbeat
├── client.ts # shipEnvelope: gzip-on Bearer POST + retry/backoff + AuthFatalError
├── enroll.ts # buildEnrollmentRequest + parseEnrollmentResponse + enroll(fetcher)
├── dlq.ts # park / listParked / readParked / archiveReplayed / replayAll
├── pause.ts # F-WD-010 sentinel: writePause / clearPause / assertNotPaused
├── heartbeat.ts # startHeartbeat — startup + periodic R-HB emissions
├── cli.ts # enroll / run / status subcommands (exit codes 0/1/2/3)
└── adapters/
├── adapter.ts # Adapter interface + defineAdapter() helper
└── mock.ts # deterministic test-only fixture (createMockFixture)
tools/
└── spec-audit.ts # executable audit gate; grows with every iteration
.github/workflows/
└── ci.yml # matrix Node 20+22; runs the same four gates as commitsDevelopment gates (per iteration AND in CI)
tsc --noEmit
eslint .
vitest run
npx tsx tools/spec-audit.tsA commit cannot land if any gate fails. Drift surfaces immediately, not at review.
Quick start (developer mode)
@ghostlogic/ghostseal is not yet on npm. v0.1 consumes it via file:../ghostseal-node. To run locally you need both repos side-by-side:
~/work/
├── ghostseal-node/ # sister, must be built first
└── ghostlogic-node-agent-watchdog/ # this repocd ghostseal-node && npm install && npm run build
cd ../ghostlogic-node-agent-watchdog && npm install
npm test
npx tsx src/cli.ts status --state-dir ./stateSee MIGRATION.md for the v0.1 → v0.2 transition (file: dep → npm).
