0.7.4 — Duffel order enrichment + activity/transfer agents
Workspace-wide patch bump 0.7.3 → 0.7.4 so npm picks up #123.
@otaip/adapter-duffel
- Enrich
BookResponsefrom the live Duffel order already returned bybook(): optionalticketNumbers,segments(fare basis),baseAmount/taxAmount,recordLocator,passengerNames,refundable/changeable, timestamps. - Add
getOrder(orderId)→GET /air/orders/{id}mapped through the same helper. - Existing five fields unchanged; new fields optional. Unit tests cover the order mapper.
@otaip/agents-lodging
- Agent 20.8
ActivitySearchAgent— typed Hotelbeds Activities search wrapper. - Agent 20.9
TransferSearchAgent— typed Hotelbeds Transfers search wrapper. agents.manifest.jsonregenerated (77 agents).
CI / hygiene
pnpmoverrides for highpnpm auditfindings (postcss,brace-expansion,fast-uri,find-my-way,@fastify/static).- Public-repo comment guard: no internal product-name refs in lodging agent docs.
0.7.2 — Duffel Cars + auto-publish workflow + post-#93 release fix
Patch bump. Three pieces of substantive work in this window:
@otaip/adapter-duffel — Cars API (#99)
Duffel launched Cars as a full API vertical alongside Flights and Stays. DuffelAdapter now spans both surfaces. Five new direct methods on the same class:
- Cars (
/cars/) —searchCars,quoteCar,bookCar,getCarBooking,cancelCarBooking. Three-step flow (search → quote → book) unlike the two-step flight flow. Geo-coordinate based — no IATA codes — per the brief; IATA-to-coordinate conversion is explicitly deferred (DQ-C8). - The shared private
request()helper now accepts an optionalAbortSignal(pre-flight check; in-flight cancel needs an upstream change tofetchWithRetry, same caveat the Hotelbeds adapter carries).
New canonical types (CarRate, CarQuote, CarBookResponse, etc.), wire types, decimal.js-backed mapper, and a full mock three-step flow on MockDuffelAdapter (two synthetic fixtures: Toyota Corolla compact + VW Tiguan SUV). 18 new test cases covering body shapes, headers, mapper output, abort handling, 429 retry-then-error, and round-trip mock flow. Domain knowledge captured verbatim in docs/knowledge-base/cars.md with 8 numbered DOMAIN_QUESTION markers (DQ-C1..C8) for the gaps the brief leaves open. Per the constitution's no-invent rule, those are surfaced rather than guessed.
Auto-publish workflow wiring (#97, #98)
Post-mortem on why @otaip/core etc. sat at v0.6.4 on npm despite v0.7.0 and v0.7.1 GitHub releases existing:
- #97 —
scripts/count-agents.tshad been importingdiscoverAgentsthrough the CLI re-export added in #93. The Release workflow's Count agents step ran before any package was built, and the indirection forced tsx to resolve@otaip/corethrough itspackage.jsonexportsmap →dist/index.js(which doesn't exist on the runner) →ERR_PACKAGE_PATH_NOT_EXPORTED. Result: every push-to-main since #93 was failing the Release workflow silently, blocking tag creation. Fixed by importing directly from the source path. - #98 — The Release workflow's
gh release createwas authenticated withGITHUB_TOKEN, but GitHub Actions intentionally does not trigger downstream workflows for events fired byGITHUB_TOKEN. That's why the Publish workflow never auto-fired offrelease: publishedfor v0.7.0 or v0.7.1 — packages only made it to npm when someone clicked Run workflow manually. Switched the create-release step to a Personal Access Token (RELEASE_PATsecret) so the event is "user-fired" and propagates. Falls back toGITHUB_TOKENwhen the secret is unset, so the workflow still functions in environments without the PAT.
After v0.7.2 merges and tags, this is the first release that will auto-publish to npm without a manual workflow_dispatch.
Verification
pnpm exec vitest run packages/adapters/duffel— 58 passed / 3 skipped (sandbox-gated).pnpm -r typecheck— clean across the workspace.pnpm exec eslint packages/adapters/duffel/src— clean.
Versioning note
Patch bump off v0.7.1 per VERSIONING.md. The Cars vendor brief asked for a "next minor" (0.8.0) bump; we honoured the policy and stuck with 0.7.2 instead. The next release is v0.7.3.
0.7.1 — Platform UI + agent-discovery promoted to @otaip/core
Patch bump per the policy reset in v0.7.0. The headline of this window is the Platform UI (#93) — a new React/Vite app at examples/platform-ui/ that gives developers a visual control plane for an OTAIP instance: agent registry, adapter status, health sidebar, and an interactive Playground for searches/agents/adapters. The Platform UI is private (examples/* is not published to npm); the npm-visible delta is one additive export on @otaip/core.
Published-package changes
@otaip/core— newdiscoverAgents()/DiscoveredAgentexports undersrc/discovery/. Filesystem-only walk of the workspace; no agent code executes. Repo root is resolved by walking up topnpm-workspace.yamlrather than by counting parent directories, so the helper works the same way fromsrc/(tsx/vitest) and fromdist/(built consumer).@otaip/cli—agent-discovery.tsnow re-exports from@otaip/core. Behavior unchanged; every existing import path keeps working. Thepnpm cli agentsoutput is byte-identical.- All other published packages bumped 0.7.0 → 0.7.1 with no source changes — workspace-wide version sync.
Reference OTA additions (#93)
examples/ota (private) gains the read-only telemetry routes the dashboard consumes: /api/platform/{agents,adapters,health,stats} (rate limit raised to 5 000/min so dashboard polling cannot trip the global cap) and /api/playground/{catalog,search,agent,adapter}. Catalog surfaces every discovered agent plus an executable_ids whitelist; the playground starts at one wired agent (0.1 AirportCodeResolver — the canonical reference pattern from CLAUDE.md). Non-whitelisted IDs return a clear 501 rather than pretending to run. 12 new tests across platform.test.ts + playground.test.ts follow the existing Fastify inject() + MockOtaAdapter shape.
Dependency hygiene (#95)
Dependabot bumped postcss 8.5.8 → 8.5.10 (dev-only, transitive through tailwindcss).
Verification
pnpm -r typecheck— clean across the workspace.pnpm exec vitest run examples/ota— 100 passed across 9 files.pnpm --filter @otaip/platform-ui typecheck— clean.
Versioning note
Patch bump off v0.7.0 per the rule reaffirmed in the v0.7.0 release. The next release is v0.7.2.
0.7.0 — Reference OTA hardening + Hotelbeds Activities/Transfers
Re-sync release. @otaip/adapter-hotelbeds@0.7.0 shipped as a single-package minor bump in #90 ahead of the repo-wide release; this PR aligns root + every other workspace package with that version so the GitHub front page, npm, and package.json files all agree. Per-package, the only API surface change since v0.6.4 lives in @otaip/adapter-hotelbeds. The rest of the work in this window targeted the reference OTA — see below.
@otaip/adapter-hotelbeds — Activities + Transfers (#90)
HotelbedsAdapter now spans the full APItude product family. New direct methods on the same class:
- Activities (
/activity-api/3.0) —searchActivities,bookActivity,cancelActivity. Cancellation policy narrows to'NOR' | 'NRF'; unknown values default to'NRF'per DQ-A5. - Transfers (
/transfer-api/1.0) —searchTransfers,bookTransfer,cancelTransfer. UnknowntransferTypevalues pass through verbatim rather than being coerced into the documentedPRIVATE | SHARED | LUXURYset. - The shared
request()helper now accepts an optionalAbortSignal(pre-flight check; in-flight cancel needs an upstream change tofetchWithRetryand is left as future work).
New canonical types (ActivityOffer, ActivityModality, TransferOffer, etc.), wire types, mappers (decimal.js for all amounts), capability manifests (hotelbedsActivitiesCapabilities, hotelbedsTransfersCapabilities), MockHotelbedsAdapter fixtures for both surfaces, and unit + sandbox-gated integration tests. Domain knowledge captured verbatim from the vendor brief in docs/knowledge-base/activities.md and docs/knowledge-base/transfers.md with 13 numbered DOMAIN_QUESTION markers (DQ-A1..A5, DQ-T1..T8) for the gaps the brief leaves open. Per the constitution's no-invent rule, those questions are surfaced rather than guessed.
Reference OTA — Path B hardening (#88)
Three pieces:
- Durable persistence —
MockOtaAdapternow optionally stores bookings in SQLite vianode:sqlite(Node 24+). Auto-loads whenDATABASE_PATHis set; falls back to in-memory otherwise. Survives process restarts. - Real Stripe payments —
PaymentServiceruns against Stripe whenSTRIPE_SECRET_KEYis set. PaymentIntents created at booking time, confirmed at pay time. Mock-mode preserved when no key. - Security headers + input schemas —
helmet,@fastify/cors(env-driven),@fastify/rate-limit(100/min global, 20/min on/api/book, 10/min on/api/pay), AJV body schemas on every state-mutating route, custom error formatter that preserves the existing{ error, details }envelope.
Reference OTA — wire real DuffelAdapter (#89)
When DUFFEL_API_KEY is set, examples/ota's search/price/book flow now runs against the live Duffel sandbox via a new DuffelOtaAdapter wrapper. Without the key it falls back to MockOtaAdapter exactly as before. New BookingLifecycle interface lets Payment / Ticketing / Manage services type against OtaAdapter & BookingLifecycle instead of the concrete mock — drops the as MockOtaAdapter casts in server.ts. Standardizes the env var to DUFFEL_API_KEY (matches the demo); DUFFEL_API_TOKEN still works with a deprecation warning. ADAPTERS=duffel is now a valid value for live multi-source search.
Docs & CI
- README — "Build on OTAIP" promoted to the hero slot; the domain-flex framing pushed to the bottom (#87).
- Publish CI — verify step now auto-discovers packages from the workspace instead of being hand-maintained, and
repository.urlis canonicalized across everypackage.json(#86).
Verification
- Workspace typecheck clean.
@otaip/adapter-hotelbeds: 100 unit tests pass, 14 sandbox-gated tests skip without credentials.examples/ota: 88 tests pass.- All PRs in the v0.7.0 window were merged green individually before this release PR was cut.
Versioning note
The patch-bump-only rule from VERSIONING.md was broken when @otaip/adapter-hotelbeds shipped as 0.7.0 in the per-package PR. This release ratifies that bump for the rest of the workspace and updates VERSIONING.md to acknowledge the deviation. Going forward, all releases are patch bumps off v0.7.x until v1.0 — the next release is v0.7.1.
0.6.4 — Hotelbeds Distribution Adapter
Patch bump per the pre-v1.0 policy in VERSIONING.md. Adds a new adapter package — @otaip/adapter-hotelbeds, the first lodging-distribution adapter on OTAIP. Verified end-to-end against the real Hotelbeds APItude sandbox: full booking lifecycle (search → checkrate → book → retrieve → cancel) returns a real Hotelbeds reference and cleans up after itself.
New
@otaip/adapter-hotelbeds— Hotelbeds APItude adapter for the Hotels API. Implementsavailability,checkRate,book,getBooking,listBookings, andcancelBooking(SIMULATIONandCANCELLATION). Search bridges into the lodging search aggregator via the existingHotelSourceAdaptershape. Mock adapter ships in the same package for upstream tests that don't have credentials.- SHA256 request signing —
signRequest/buildAuthHeadersregenerate theX-Signatureper request (Hotelbeds rejects signatures more than ~5 minutes off server time). Backed bynode:crypto, no extra dependencies. - Field mapper — Hotelbeds wire types → canonical OTAIP lodging types (
RawHotelResult,RawRate,CancellationPolicy,BookingSummary). All money stays inDecimal/ string land; nonumberfor prices. - Demo script at
packages/adapters/hotelbeds/demo/index.ts— runnable withHOTELBEDS_API_KEY=… HOTELBEDS_SECRET=… npx tsx demo/index.ts. Walks the full lifecycle end-to-end in a single command.
Domain decisions applied
- DQ11 (mandatory fees) — when Hotelbeds returns
taxes.allIncluded === false, same-currency mandatory fees are folded into the rate'stotalRate/nightlyRateso the top-level price is what the traveler actually owes. Fees stay onmandatoryFeesfor transparency. Cross-currency fees are listed but not folded (the adapter has no FX rate). - DQ13 (cancellation markup) — Hotelbeds cancellation
amountis net (cost-to-us). Adapter applies a documentedHOTELBEDS_CANCEL_FEE_MARKUP = 1.25and stores both the gross (penaltyValue) and the net (netPenaltyValue) so settlement and reporting can reconcile margin. New optionalnetPenaltyValue?: numberfield added toCancellationDeadlinein@otaip/agents-lodging— additive, all existing callers still typecheck. - DQ14 (booking status) — explicit mapping for
CONFIRMED/CANCELLED/ON_REQUEST/PENDING/MODIFIED; unknown values fall back topendingand emit aconsole.warnso unhandled states don't get silently swallowed.
Verification
- Unit tests: 61 against mocked fetch (auth, field mapper, full adapter surface, mock adapter).
- Integration test: 8/8 passing against the real Hotelbeds sandbox at
https://api.test.hotelbeds.com. Auto-skipped when credentials aren't set, so CI never hits the live API. Self-cleans the booking it creates. - Repo-wide: 3153 tests passing, 0 failed, 11 skipped (the 8 integration + 3 pre-existing).
Deferred
- Hotelbeds Transfers API and Activities API — tracked as a follow-up so this adapter stays focused on the bedbank content path that unblocks the lodging pipeline.
0.6.3 — Codex review closeout, first npm publish, per-transaction routing
Eleven PRs (#73–#83) addressing all 12 findings from a full-repo Codex review, the first-ever publish of the @otaip/* scope to npm, and a set of CI/publish hardening fixes. Several engines widen their output types and a couple of agents add required input fields — see Potentially-breaking below if you were depending on the previous behaviour.
Published to npm
All 15 @otaip/* packages are now live on npm at 0.6.2 — previously the source existed but nothing had shipped. npm install @otaip/core (and friends) works.
@otaip/core,@otaip/connect,@otaip/cli,@otaip/adapter-duffel@otaip/agents-reference,@otaip/agents-search,@otaip/agents-pricing,@otaip/agents-booking,@otaip/agents-ticketing,@otaip/agents-exchange,@otaip/agents-settlement,@otaip/agents-reconciliation,@otaip/agents-lodging@otaip/agents-tmc,@otaip/agents-platform
Removed invented domain logic (CLAUDE.md compliance)
Codex review flagged five engines that were computing fare/penalty/compensation amounts from invented "common industry pattern" data rather than authoritative sources. This release replaces all of them with either caller-supplied authoritative inputs or published-law constants.
@otaip/core: EU 261/2004 + US DOT 14 CFR §250 modules (new).applyEU261()encodes the published distance bands (€250/€400/€600), 3h arrival-delay trigger, Article 7(2) rerouting 50% reduction, 14-day cancellation safe harbour, and extraordinary-circumstances exemption.applyUsDotIdb()encodes the current 14 CFR §250.5 denied-boarding tables ($1,075 / $2,150 caps, effective 2025-01-22). PlusgreatCircleDistanceKm()haversine helper.@otaip/core: newDomainInputRequiredtype + helpers (domainInputRequired,isDomainInputRequired) — shared sentinel for engines that refuse to synthesise numbers when authoritative inputs are missing.- Fare Construction (2.2): removed the ROE 1.0 fallback (was silently multiplying every non-USD currency by 1), the per-mile-rate HIP heuristic, and the city-revisited BHC heuristic. Engine now returns
DomainInputRequiredwhen ROE is missing and populatesmissing_inputson HIP/BHC checks that need intermediate-point fare lookups. The EMS mileage-surcharge formula (5%/5%, max 25%) stays — confirmed IATA standard. Output type widened toFareConstructionResult = FareConstructionOutput | DomainInputRequired. - Change Management (5.1) + Refund Processing (6.1): removed the "$200 default" fallback, the waiver-=-zero special case, and the residual = original − penalty formula. Engines now read
cat31_rules/cat33_rulesfrom input and apply them as filed. When rules are absent, they fall back to the ATPCO default — permitted at no charge for voluntary changes, fee waived for involuntary. Invented rule data moved out ofsrc/data/into__tests__/fixtures/with a "TEST FIXTURE — do not use in production" banner. - Involuntary Rebook (5.3): removed the hardcoded 60-minute IRROP threshold. Caller now supplies
thresholds.time_change_minutesper carrier. Real EU261 compensation is calculated via the core module wheneu261_inputs(distance, delay, extraordinary circumstances, notice days, rerouting) are provided;regulatory_flags[].missing_inputslists what's needed otherwise. Clarified that US DOT IDB applies to denied boarding only — not delays/cancellations. - Feedback & Complaint (6.5): replaced ~250 lines of inline EU261 / US DOT compensation math with calls to the core regulation modules. DOT IDB caps updated from pre-amendment $775 / $1,550 to current $1,075 / $2,150. Article 10(2) downgrade reimbursement (30/50/75%) kept — published law not covered by the Article 7 helper.
GDS/NDC router per-transaction routing (CLAUDE.md compliance)
The router previously treated carrier → channel_priority as unconditional. Replaced with per-transaction routing: different transaction types (shopping, booking, ticketing, servicing, group, corporate) route differently for the same carrier. Built-in defaults cover shopping and booking; every other type requires caller-supplied capability_overrides or the engine returns domain_input_required: true. Unknown carriers no longer default silently to GDS/AMADEUS.
HTTP hardening
- New
@otaip/corefetchWithRetry(input, init?, options?)— wrapsfetchwith per-attemptAbortControllertimeout (default 30s) + retry on 5xx / 429 / network errors via the existingwithRetry. Response stays aResponse; callers still inspectresponse.ok. - TripPro defaults now HTTPS. Search and calendar-search URLs were
http://mas.trippro.com/...— switched tohttps://. Reprice/book were already HTTPS. - Sabre auth, Navitaire create+refresh, Duffel adapter, TripPro SOAP client all route through
fetchWithRetryinstead of rawfetch.
Correctness fixes
booking/api-abstractionrate limiter — the counter incremented once perexecute()call, before the retry loop, so retries against the upstream provider went uncounted. Moved the increment and the rate-limit guard inside the loop so each actual outbound attempt is charged against the quota.- Stub agents now throw
UnimplementedDomainInputError(new@otaip/coreexport) instead of rawError. Four agents migrated:DisruptionResponseAgent(5.4),DynamicPricingAgent(2.6),RevenueManagementAgent(2.7),InterlineSettlementAgent(7.4).
CLI
otaip agentsregistry is now auto-discovered from source metadata (packages/cli/src/agent-discovery.ts) rather than a hand-maintained array that had drifted (claimed 71, listed 69, several names didn't match the exported agent classes). Walkspackages/agents/*/src/*/index.ts,packages/agents-platform/src/*/index.ts,packages/agents-tmc/src/*/index.ts, andpackages/core/src/agents/shopping/*/index.ts— grepsreadonly id,readonly name,readonly version. Today: 75 agents across 12 stages.- CLI now included in lint (
--ignore-pattern 'packages/cli/**'removed).packages/cli/tsconfig.jsonadded to the ESLint parser project list. CLI-scoped override allowsconsole.*(CLI stdout is the contract). - 7 new tests for the discovery walk — count floor, unique IDs, file-resolves-to-real-path, stage matches ID prefix, sort order, etc.
Bootstrap + counts
- New
scripts/count-agents.ts(pnpm run count:agents) — single source of truth for agent counts. Replaces afindinrelease.ymlthat undercounted by skippingagents-platformandagents-tmc. All agent/stage counts in README, docs, and release notes now derive from one script. - Removed root
postinstall. Withignore-scripts=truein.npmrc(shipped in 0.5.x for supply-chain safety), thepostinstallnever ran anyway. Docs updated to instruct an explicitpnpm run data:download. - Publish prep — every workspace package now has
"type": "module"so tsup's ESM output (index.js/index.d.ts) matches themain/typesfields;exports["."].typesmoved from./src/index.ts(not in the tarball) to./dist/index.d.ts; all 15 workspace packages aligned to the root version so the first publish was coherent.
CI / release
release.ymlno longer swallows test failures. The previous step usedpnpm test 2>&1 || true, masking failed tests so a broken release could publish with a misleading test count. Split into two steps that fail fast.publish.ymlnow verifies packages are actually live. Afterpnpm -r publish, polls the registry for each of the 15@otaip/*packages and expectsdist-tags.latestto equal the released version. Fails loudly if pnpm reports success but the registry doesn't have the package. (Caught a "published successfully but 404 on registry for 5 minutes" class of bug on the first live publish.)ci.ymlbuilds before typecheck. Withexports.types → ./dist/index.d.ts, cross-package@otaip/*imports needdist/to exist for TS to resolve types. Doubles as clean-tree build validation.
Docs
- README header numbers synced — 75 agents across 12 stages, 3,092 tests, 16 packages. Test badge updated. Install instructions now say
pnpm install --frozen-lockfile && pnpm run data:download. - Accurate tsconfig strictness claim — README and CLAUDE.md previously said "all strict flags ON" but
exactOptionalPropertyTypesis intentionally off. Replaced with the explicit list of enabled flags. - docs/getting-started.md — added the explicit data-download step and documented the supply-chain trade-off.
- docs/agents.md — header updated to "12 stages" with a note pointing future editors at
pnpm run count:agentsso the total cannot drift again.
Potentially-breaking
Pre-1.0 policy allows breaking changes in patch bumps; flagged here so downstream consumers can update.
FareConstruction#execute()return type widened toAgentOutput<FareConstructionOutput | DomainInputRequired>. Consumers must narrow onresult.data.status.GdsNdcRouterinput now requirestransaction_type(new enum). Previously-working inputs without it will be rejected by the validator.ChangeManagementandRefundProcessingnow requirecat31_rules/cat33_rulesin input to apply filed penalties. Absent rules → ATPCO default (no penalty for voluntary, waived for involuntary) — was a$200default in 0.6.2.InvoluntaryRebooknow requiresthresholds.time_change_minutesto mark a time-change as involuntary. Absent → non-involuntary with a warning.@otaip/coreexportsfetchWithRetry,UnimplementedDomainInputError,DomainInputRequired,applyEU261,applyUsDotIdb,greatCircleDistanceKmand related types.- TripPro
searchUrl/calendarSearchUrldefaults switched fromhttp://tohttps://. Override explicitly for local dev. - US DOT IDB compensation caps updated to 14 CFR §250.5 (effective 2025-01-22):
$1,075/$2,150(was the pre-amendment$775/$1,550). - 11 packages that previously lacked
"type": "module"now have it. CommonJS consumers will need to import viaimport(...)or upgrade their resolution.
Tests
- 3,092 total passing (was 3,034). 58 new tests:
- 18 for the new EU261 + US DOT IDB regulation modules
- 8 for
fetchWithRetry - 5 new EU261 compensation tests in involuntary-rebook
- 7 for CLI agent discovery
- 7 for the ATPCO-default branches in change-management + refund-processing
- 5 for per-transaction GDS/NDC routing + new validators
- 3 for the rate-limit retry counting + stub
UnimplementedDomainInputError
0.6.2 — Reference OTA Multi-Adapter Fixes
Three bugs in the Sprint H multi-adapter integration caught by Codex review, plus the follow-up to make booking adapter-aware. No behavior change to the single-adapter path; no breaking changes to public interfaces.
Fixed
buildApp()now wiresMultiSearchServiceinto the search route —?multi=truewas previously unreachable in production. When theADAPTERSenv var is set, aMultiSearchServiceis constructed automatically; test callers can inject one viabuildApp({ multiSearch }).- Multi-adapter search now caches its offers —
GET /api/offers/:idandPOST /api/bookno longer 404 on offers returned from the multi path. NewSearchService.cacheOffers()is called from the multi branch. returnDatepreserved on the multi path — round-trip requests now reach adapters with both segments. Previously the return leg was silently dropped.- Adapter-aware booking routing — bookings now route back to the adapter that produced the offer. An offer from a search-only adapter (no
book()method) is rejected with HTTP409and the adapter name, not silently routed to the default adapter. NewAdapterNotBookableError+SearchService.getOfferAdapterSource()+BookingService(defaultAdapter, searchService, bookingAdapters?)registry param. - Stale
adapterSourcecleared on re-cache — a single-adapter search after a multi-adapter search no longer leaves a stale source entry behind that would misroute a subsequent booking.
Documented
offer_idcollision semantics within aMultiSearchService.search()call are explicitly last-write-wins. Production deployments that need stronger guarantees should namespace IDs withadapterSourceat the aggregation boundary.
Tests
- 12 new tests in
examples/ota/src/__tests__/search.test.tspinning each fix. 3034 total passing, 0 failing.
0.6.1 — Stub Replacements: HotelCarSearch, AITravelAdvisor, SelfServiceRebooking, WaitlistManagement
Four stub agents replaced with real implementations. No new agents; no breaking changes to public interfaces. Existing imports continue to work.
Changed
- Agent 1.7
HotelCarSearchAgent— Aggregator over injectable hotel/car adapters.Promise.allSettledfan-out with per-adapter timeouts, filters, and sort. Partial-failure tolerant (returns available results with per-source status). New Zod schemas +AgentContract. - Agent 1.8
AITravelAdvisorAgent— Rule-based recommender (NOT LLM). OrchestratesAvailabilitySearch+FareShopping, scores offers on price / schedule / airline / connections with business/leisure/default weight profiles.LLMProvider/MockLLMProvidertypes removed. - Agent 5.5
SelfServiceRebookingAgent— Rebooking orchestrator overAvailabilitySearch+ChangeManagement. Ranks priced alternatives bychangeFee + fareDifference + taxDifference. Involuntary reasons (schedule change / missed connection / cancellation) waive the change fee. - Agent 5.6
WaitlistManagementAgent— Stateful in-memory queue (reference implementation, not durable). Four operations:addEntry,clear,queryStatus,expire. Priority = status tier + fare class type + recency bonus; ties broken by earliestrequestedAt. Clearance probability estimated asrate^positionwith per-booking-class overrides.
Tests
- 77 new tests across the four agents (19 + 20 + 17 + 21). 3022 total passing, 0 failing.
0.6.0 — Sprint H: Multi-Adapter, OOSD-Native, Full Distribution
The mid-term build plan is complete. Navitaire gains native ONE Order operations, Duffel bridges its order model to OTAIP's AIDM-aligned types, and the Reference OTA searches multiple adapters in parallel with source attribution.
Added
- Navitaire OrderOperations —
NavitaireOrderOperationsclass implements the full AIDM 24.1OrderOperationsinterface:orderCreate,orderRetrieve,orderChange,orderCancel,orderViewHistory. Mock in-memory implementation withNAV-ORD-*IDs, status lifecycle, andOrderEventtracking. Navitaire is ONE Order certified; this lets OTAIP speak to them natively. - Duffel Order Bridge —
DuffelOrderBridgeclass bridges Duffel's native order model to OTAIP's AIDM-alignedOrdertypes.DFL-ORD-*IDs, double-cancel prevention, passenger/payment/offer-item mapping. - ChannelCapability Order fields —
supportsOrders?: booleanandorderOperations?: ('create' | 'retrieve' | 'change' | 'cancel')[]onChannelCapability.GdsNdcRoutercan use these to decide PNR vs Order path per channel. - Multi-adapter search in Reference OTA —
MultiSearchServicefans out search requests to multipleDistributionAdapterinstances viaPromise.allSettled, merges results withadapterSourceattribution, includes per-source status with timing and error reporting. Activated viaADAPTERSenv var (comma-separated) or?multi=truequery param. - Updated capability manifests — Navitaire and Duffel now declare
supportsOrders: trueand the full set of order operations. - docs/adapters/oosd-navitaire.md — Navitaire ONE Order adapter documentation.
- docs/offers-and-orders.md — updated with Sprint H completion section.
Tests
- 33 new tests (15 Navitaire + 10 Duffel + 8 multi-search). 2985 total passing, 0 failing.
Build plan complete
| Sprint | Version | Delivered |
|---|---|---|
| A | v0.3.2 | Pipeline validator, 9 agent contracts, capability registry |
| B | v0.3.2.1 | Tool bridge, catalog generator, EventStore, PnrRetrieval |
| C | v0.3.3 | Fallback chain, governance agents, CLI |
| D+E | v0.3.4 | Docs overhaul, Reference OTA search flow |
| F | v0.5.0 | Reference OTA booking, payment, ticketing |
| G | v0.5.1 | Offers & Orders data model (AIDM 24.1) |
| H | v0.6.0 | OOSD-native adapters, multi-adapter search |
Final stats: 76 agents, 2985 tests, 16 workspace packages, 6 adapters, 14 contracted agents, 2 OOSD-native adapters.
0.5.1 — Sprint G: ONE Order Ready — PNR + Orders Coexist
Native Offers & Orders data model in @otaip/core, aligned with IATA AIDM 24.1 terminology. PNR and Order models coexist through a unified BookingReference bridge — agents accept either and let the adapter decide the underlying model.
Added
- Order/Offer types —
Offer,OfferItem,Order,OrderItem,Service(atomic unit: flight, seat, baggage, meal, lounge, insurance, ancillary),OrderPassengerwithTravelDocument+LoyaltyInfo,TicketDocument(ET, EMD-A, EMD-S),OrderPayment,Money(decimal string + ISO 4217) - AIDM 24.1 message names —
OrderCreate,OrderRetrieve,OrderChange,OrderCancelon theOrderOperationsinterface. Adapters that support ONE Order implement this directly. - OrderEvent — event-driven status changes for Orders (
order.created,order.confirmed,order.ticketed,order.changed,order.cancelled,order.payment_received,order.payment_failed,order.refunded). Queue management stays PNR-only. - BookingReference bridge —
PnrReference | OrderReferenceunion type with constructors (createPnrReference,createOrderReference), type guards (isPnrReference,isOrderReference), accessors (getBookingIdentifier,getBookingOwner), andpnrPassengerToOrderPassenger()converter. - Zod schemas for every Order/Offer type — ready for
zodToJsonSchema()LLM tool generation. - docs/offers-and-orders.md — explains the dual model, AIDM alignment, bridge utilities, and Sprint H roadmap (Navitaire as OOSD adapter target).
Design decisions
- JSON, not XML. AIDM concepts, not the AIDM XML schema.
- Queue management stays PNR-only. Orders use
OrderEvent. - No agent modifications in this release — types and bridge only. Agent integration via
BookingReferencelands in Sprint H. - Navitaire is the target OOSD adapter for Sprint H — they're ONE Order certified and the adapter already exists.
Tests
- 47 new tests (30 schema validation + 17 bridge utilities). 2952 total passing, 0 failing.
0.5.0 — Sprint F: Reference OTA — Book, Pay, Fly
The reference OTA is now a complete booking application. Users can search flights, select an offer, enter passenger details, pay, and receive a ticket — the full travel e-commerce lifecycle running on OTAIP agents.
Added
- Booking flow —
POST /api/bookcreates a booking from a search offer with passenger details (title, name, DOB, gender) and contact info. Validates the offer exists in the search cache before booking. Returns a booking reference. - Payment flow —
POST /api/payprocesses a mock payment against a booking reference. Structured for future Stripe integration (PaymentService abstraction) but ships with a mock that always succeeds. No external payment SDK dependency. - Ticketing flow (Option B) —
POST /api/ticketchecks booking status first. If already ticketed, returns existing ticket numbers (idempotent). If not, generates mock 13-digit ticket numbers and updates status. Ticketed bookings cannot be cancelled. - Booking management —
GET /api/booking/:refretrieves booking details.POST /api/cancelcancels confirmed (not yet ticketed) bookings. - 4 frontend pages — passenger details form (
book.html), payment summary + Pay Now (payment.html), full confirmation with tickets + itinerary (confirmation.html), booking lookup + cancel (manage.html). Plain HTML + vanilla JS + Pico CSS. - OtaAdapter interface — extends
DistributionAdapterwithbook(),getBooking(),cancelBooking(). MockOtaAdapter extends MockDuffelAdapter with in-memory booking store, reference generation, and status lifecycle (confirmed → ticketed/cancelled). - 14 integration tests — booking CRUD, payment, idempotent ticketing, cancellation rules, 2 full end-to-end flows (search → book → pay → ticket, search → book → cancel).
Tests
- 2905 total passing (14 new + 2891 existing), 0 failing
0.3.4 — Sprint D+E: Docs Overhaul + Reference OTA
Two sprints shipped together: Sprint D rewrites all documentation so the platform's scope is visible at a glance. Sprint E ships a deployable reference OTA that proves OTAIP works end to end — fork it, add your Duffel token, search real flights.
Sprint D — Documentation Overhaul
- README.md rewrite — 6-adapter comparison table with exact test counts (456 total), 75-agent domain overview across 12 stages, pipeline contract system explanation, CLI usage examples, "Build on OTAIP" section
- docs/architecture.md — 4 Mermaid diagrams: high-level architecture, pipeline gate sequence, tool bridge flow, EventStore integration
- docs/agents.md — complete table of all 75 agents with ID, class name, description, contract status (14 contracted)
- docs/getting-started.md — clone to working demo in 5 minutes
- docs/adapters/{amadeus,sabre,navitaire,trippro,duffel,haip}.md — 6 adapter docs with capabilities, auth, config, usage, test counts, limitations
- Agent ID collision fix — PluginManager keeps 9.5, governance agents renumbered to 9.6-9.9
Sprint E — Reference OTA: Search Flow
- Fastify server (
examples/ota/) —POST /api/search,GET /api/offers/:id,GET /health - Services — SearchService orchestrates AirportCodeResolver → AvailabilitySearch; OfferService caches offer details
- Adapter config — DuffelAdapter when
DUFFEL_API_TOKENis set, MockDuffelAdapter when not (works out of the box with zero config) - Frontend — plain HTML + vanilla JS + Pico CSS via CDN. Search form with IATA validation, results page with sort-by-price/duration/departure, offer details with price breakdown. No React, no build step.
- 10 integration tests using Fastify inject + MockDuffelAdapter
Monorepo fix
- exports.types → src/index.ts — 14 packages had
exports["."].typespointing to./dist/index.d.ts(only exists after build). Changed all to./src/index.tsmatching@otaip/core's pattern.pnpm -r run typechecknow works without a prior build step.
Tests
- 2891 total passing (10 new OTA tests + 2881 existing), 0 failing
0.3.3 — Sprint C: Governance Agents, Fallback Chain, CLI
The OTAIP build plan is now complete. Sprint C ships the final three steps: the fallback chain engine for automatic channel recovery, four governance agents that monitor the platform's own performance, and a CLI tool for zero-code developer access.
Added
- Fallback chain engine (
executeFallbackChain()) — when the primary routing channel fails, automatically tries each fallback in order. Skips channels whose circuit breaker is open (integrates with ApiAbstraction agent 3.5). Returns a full audit trail of every attempt with durations and error details. 6 tests. - Performance Audit Agent (9.5) — reads EventStore
agent.executedevents, computes success rate, error rate, avg/p95/p99 latency, identifies degraded agents (error rate >15% or p95 >8000ms). AgentContract from day one. 10 tests. - Routing Audit Agent (9.6) — reads
routing.decided+routing.outcomeevents, correlates by session, computes per-channel success rates and fallback frequency. AgentContract from day one. 7 tests. - Recommendation Agent (9.7) — takes performance + routing audit reports as input, applies deterministic rules, produces typed recommendations (
route_adjustment,adapter_health,capacity,config_update). Allauto_applicable: falsein v1. Confidence based on data volume. AgentContract from day one. 10 tests. - Alert Agent (9.8) — configurable threshold monitoring with defaults from the master plan: GDS error rate 5%/15% (warning/critical), NDC 10%/25%, adapter latency p95 >8000ms, 3+ consecutive failures, pipeline rejection rate >20%. AgentContract from day one. 13 tests.
- CLI tool (
@otaip/cli) — new package with 6 commands:otaip search,otaip price,otaip book,otaip adapters,otaip agents(lists all 75 agents with contract status),otaip validate(dry-run pipeline validation). Table format by default,--jsonfor machine output,--verbosefor gate details.
Fixed
- Semver compliance — version
0.3.2.1(4-part, not valid semver) broke pnpm'sworkspace:*matching. All packages now use proper 3-part semver0.3.3. - Unused import in
chain-engine.ts(FallbackStatus). - CLI ESLint config — typescript-eslint can't resolve workspace deps when CWD is a nested package. CLI excluded from root lint glob; covered by
pnpm typecheckinstead.
Tests
- 46 net new tests across 5 files (2881 total passing, 0 failing)
Build plan status
All six steps from the master plan are now shipped:
| Step | Deliverable | Sprint |
|---|---|---|
| 1 | Pipeline validator (6 gates) | A (v0.3.2) |
| 2 | LLM tool layer (bridge + catalog) | B (v0.3.2.1) |
| 3 | Routing (capability registry + fallback chain) | A + C |
| 4 | EventStore | B (v0.3.2.1) |
| 5 | Governance agents (4 agents) | C (v0.3.3) |
| 6 | CLI tool | C (v0.3.3) |
0.3.2.1 — Sprint B: LLM Tool Layer + EventStore
The pipeline validator can now talk to LLMs. Sprint B connects the contract infrastructure (shipped in v0.3.2) to the tool-dispatch layer, adds persistent event logging, ships a new agent, and delivers the first demo that runs the full architecture end-to-end.
Added
- Agent-to-Tool bridge (
agentToTool()) — wraps anyAgentContract+Agentpair into aToolDefinitionthatAgentLoopcan register and dispatch. Every tool call runs through the six pipeline gates. Failures throwAgentToolErrorwith structured reason + issues for LLM self-correction.registerAgentTools()batch-converts all contracted agents into aToolRegistry. - Catalog generator —
generateMcpTools()(Claude MCP),generateOpenAiFunctions()(OpenAI strict mode, draft-7),generateCatalog()(standalone JSON Schema). All schemas fromzodToJsonSchema()— zero hand-written JSON anywhere in the pipeline. - EventStore —
OtaipEventdiscriminated union with 6 event types (agent.executed,routing.decided,routing.outcome,booking.completed,booking.failed,adapter.health).InMemoryEventStorewith filter-by-type/session/agent/time-window queries and percentile aggregation (p50/p95/p99). Optional auto-logging from the tool bridge viaeventStoreoption. - Agent 3.8 PnrRetrieval — new agent that retrieves an existing PNR/booking by record locator across distribution adapters.
AgentContractfrom day one (actionType: 'query', Zod schemas, semantic validation). Stub retrieval engine — wires to real adapters whenConnectAdaptergainsretrieveBooking(). - GdsNdcRouter registry adapter —
buildCarrierCapabilities()converts the existingcarrier-channels.jsonlookup table intoChannelCapabilityentries for theCapabilityRegistry. 8 equivalence tests prove NDC-preferred, GDS-preferred, and DIRECT-only carriers are correctly encoded. Infrastructure for the full scoring-engine swap. - Full pipeline demo (
demo/book-flight-full.ts) — first demo that uses the Sprint A/B architecture end-to-end: contract-driven tool definitions,agentToTool()bridge, 6-gate pipeline validator, EventStore logging, pipeline summary. Run withpnpm --filter @otaip/demo book:full. demo/README.md— documents all 5 demo scripts with credential requirements.AGENT_TOOL_NAMES— stable snake_case name map for all 10 contracted agents (e.g.'1.1' → 'availability_search').
Fixed
@otaip/connectmissing from vitest alias map — the only workspace package without an alias, causing CI to fail when tests importedCapabilityRegistryfrom@otaip/connect(resolved todist/which doesn't exist without a build step).
Tests
- 39 net new tests across 7 files (2835 total passing, 0 failing)
0.3.2 — Sprint A: Pipeline Contract Foundation
OTAIP moves from a library to a platform: every agent that participates in an LLM-orchestrated or pipeline-composed flow can now declare a machine-verifiable AgentContract, enforced at runtime by six gates (schema, semantic, intent lock, cross-agent consistency, confidence, action classification). Agents without contracts continue to work as direct function calls — the change is purely additive.
Added
- Pipeline validator (
@otaip/core/pipeline-validator) — six-gate runtime withPipelineOrchestrator, session-scoped intent lock, cross-agent consistency checker, confidence gate (floors: query 0.7 / reversible 0.9 / irreversible 0.95 / reference 0.9), action classifier with approval-token enforcement for irreversible mutations, and a 3-retry-per-gate budget - Zod → JSON Schema bridge backed by Zod 4's native
z.toJSONSchema()— single source of truth for both runtime validation and LLM tool definitions, no new dependencies - Shared semantic validators —
validateFutureDate,validateIataCode, asyncresolveAirportStrict,resolveAirlineStrict,resolveFareBasisStrict ReferenceDataProviderinterface in@otaip/corewith concreteReferenceAgentDataProviderin@otaip/agents-referencewrapping the Stage 0 agents (no duplicated dataset)- Channel capability registry —
ChannelCapabilitytypes in@otaip/core,CapabilityRegistryclass in@otaip/connect, manifests next to each of the 6 adapters (Amadeus, Sabre, Navitaire, TripPro, HAIP, Duffel) - 9 agent contracts covering the end-to-end demo flow (search → price → book → ticket):
- 0.1
AirportCodeResolver, 0.2AirlineCodeMapper, 0.3FareBasisDecoder(reference agents) - 1.1
AvailabilitySearch, 2.1FareRuleAgent, 2.4OfferBuilderAgent(query) - 3.1
GdsNdcRouter(query), 3.2PnrBuilder(mutation_reversible), 4.1TicketIssuance(mutation_irreversible)
- 0.1
- Sprint A end-to-end integration test in
@otaip/agents-ticketingproving every one of the six gates fires at least once across a full offline run, plus targeted rejection cases (unknown airport → semantic gate; destination drift → intent-lock gate; past date → semantic gate; ticketing without approval → action-class gate)
Fixed
OfferEvaluatorAgenttime bomb —evaluateOffers()now accepts an optionalevaluation_time?: string(ISO 8601). Fixtures dated 2026-04-14 were silently expiring in CI once wall-clock time passedexpires_at. Default remainsnew Date()so existing callers are unaffected.
Scope narrowing
GdsNdcRouter(3.1) gets the contract and is now a platform citizen; the internals swap from lookup-table to registry-driven weighted scoring lands in Sprint B (callers unchanged, 467-line test fixture set updates will come with it)
Tests
- 59 new tests across 9 files (pipeline validator units, capability registry, Sprint A E2E)
- Full suite: 2796 passing, 3 skipped, 0 failing
0.3.0 — Stage 9: Platform Upgrade
Added
- Schema-Aware Tool Interface — Zod-validated tool definitions with runtime input/output checking (
packages/core/src/tool-interface/) - Agent Execution Loop — Deterministic message→tool→response cycle with typed state transitions (
packages/core/src/agent-loop/) - Lifecycle Hooks — Pre/post hooks on agent actions for logging, metrics, and guardrails (
packages/core/src/lifecycle/) - Context Budget Manager — Token-aware context management with pluggable compaction strategies (
packages/core/src/context/) - Retry with Jitter — Shared retry engine with exponential backoff and full jitter, replacing inline retry in BaseAdapter (
packages/core/src/retry/) - Sub-Agent Spawning — Parent agents can spawn scoped child agents with controlled tool access (
packages/core/src/sub-agent/)
Changed
BaseAdapter.withRetry()now uses the shared retry engine from@otaip/core(adds jitter, no breaking API changes)