Skip to content

Releases: kaushika05/liaison

v1.1.0 - Audit remediation and a live protocol surface

Choose a tag to compare

@kaushika05 kaushika05 released this 15 Aug 06:37

Liaison is a self-hosted AI phone agent that calls customer support on your behalf and reports back entirely in text. It runs against your own Twilio and OpenAI accounts, and its core invariant is that nothing important ever exists only in audio.

This release closes out a full read-only audit of the codebase and makes the v1 protocol real rather than aspirational.

Try it without credentials

git clone https://github.com/kaushika05/liaison.git && cd liaison
npm ci
npm run setup -- --defaults --output=.env
npm run dev

No Twilio account, no OpenAI key, no phone call. The deterministic simulator runs the whole product — intake, planning, one-time call authorization, live approvals, and an evidence-grounded outcome report.

The protocol is now served, not just published

The Universal Support Protocol schemas used to be generated and tested but unused by the running application. They are now returned by the API and validated at the boundary:

GET /api/cases/:caseId/execution-plan   ExecutionPlan
GET /api/attention/:id                  AttentionRequest
GET /api/cases/:caseId/commitments      Commitment[]

A new projection module is the single place where internal records become protocol documents, and every projection ends in a schema.parse, so a document that no longer satisfies the contract throws instead of shipping. An approved disclosure now also writes a metadata-only DisclosureEvent to the audit log, which survives the restart that clears the in-memory ledger.

Fixed

  • A hang-up could silently fail to end a real call. After an ambiguous call start, the database learned the Twilio call SID from a signed callback but the relay adapter did not, so endCall skipped termination and still reported success. The SID is now bound to the adapter, and endCall refuses to claim success when it has neither a SID nor an open relay socket.
  • Disclosure release used a weak purpose check. It ran term overlap against the representative's own words, and both the account-number and order-number policies contain the word "identification", so they were not category-discriminating. Release now requires a closed, disjoint classifier to identify that card's own category; unrecognised requests fail closed.
  • Tests could contact a real provider. The suite read the developer's local .env and issued a billable OpenAI request. Provider-free defaults are now forced before any test imports configuration.
  • CallService.shutdown no longer throws on an active call row the process never owned in memory.
  • Two unhandled promises in the client polling loop, found by newly enabled type-aware linting.
  • busy_timeout so the retention job and backup tooling wait instead of failing with SQLITE_BUSY.
  • The session store is scoped to the app instance and cleared on close.

Removed

MAX_CONCURRENT_CALLS and UNAUTHORIZED_SENDER_RESPONSE are gone. Neither was read anywhere in the codebase. The first looked like a safety limit but concurrency has always been enforced by the single-active-call check, and the second implied that replies to unauthorized senders were configurable when they are always silent by design. Configuration that does nothing is worse than no configuration in a product like this.

ESTIMATED_SMS_COST_PER_SEGMENT_USD was in the same category and has been wired up instead: it now drives a running estimate over segments actually submitted to a carrier. Rejected inbound provider requests, previously recorded but never shown, are surfaced as a count.

Housekeeping

  • The whole tree is Prettier-formatted, gated in CI.
  • ESLint runs type-aware rules including no-floating-promises.
  • db.ts split into schema.ts, records.ts, and the query layer.
  • defaultAuthority moved to a schema-free module, so the browser bundle no longer pulls in Zod through a value import — 590 kB to 517 kB.
  • Coverage tooling added (npm run test:coverage).

Verification

Format, lint, typecheck, 152 tests across 24 files, the deterministic demo, and the production build all pass.

As always: passing tests are not live-provider validation. Nothing in CI registers a sender, sends a carrier SMS, places a PSTN call, or spends OpenAI credits.

Full changelog: v1.0.0...v1.1.0