Skip to content

Feature ai-gateway-v2: Phase 1 — Gateway core 2.0: capability API, projector dispatch, delete provide (hy-oy5h4)#25

Merged
philcunliffe merged 1 commit into
integration/ai-gateway-v2from
polecat/hy-oy5h4
May 22, 2026
Merged

Feature ai-gateway-v2: Phase 1 — Gateway core 2.0: capability API, projector dispatch, delete provide (hy-oy5h4)#25
philcunliffe merged 1 commit into
integration/ai-gateway-v2from
polecat/hy-oy5h4

Conversation

@philcunliffe
Copy link
Copy Markdown
Contributor

Summary

Feature ai-gateway-v2: Phase 1 — Gateway core 2.0: capability API, projector dispatch, delete provide

Implemented: gateway core 2.0 — new capability surface (registerUpstreamPreset/registerExchangeProjector/registerClient + getClient/listClients/localEndpoint), projector dispatch with priority+registration-order sort, first-non-empty-wins, fallback identity (hash message_id + linear previous_message_id + attributes.gateway.identity_source='gateway_fallback') only for messages omitting identity. Deleted from gateway package: Anthropic/OpenAI/Codex parsing, ClaudeSessionContext + /_hypaware/session-context endpoint, localContextForRequest, registerExchangeContextProjector, registerMessageEnricher, hardcoded detectProvider, attributes.client.claude_version. Schema unchanged. Tests: ai-gateway-session-context.test.js deleted; ai-gateway-message-projector.test.js rewritten for new dispatch surface + schema. ai_gateway_passthrough smoke updated for 2.0.0 + zero-row-without-projector contract. npm test 266/266, smoke green, typecheck/lint clean.

Delivery

  • Issue: hy-oy5h4
  • Branch: polecat/hy-oy5h4
  • Integration target: integration/ai-gateway-v2
  • Eventual target: master

This PR is auto-merged into the integration branch once CI is green. Human
review for the full feature happens on the downstream PR
(integration/ai-gateway-v2 -> master, draft PR #24), which is opened separately when the feature
is ready to ship.

…te provider parsers

Refactor @hypaware/ai-gateway into a generic HTTP/SSE capture and
row-storage owner. All client/protocol semantics move into adapter
plugins through the new full exchange projector API.

Capability bumped to breaking 2.0.0. New surface:
- registerUpstreamPreset(preset) — adapter-owned routing via
  optional preset.match() (replaces hardcoded detectProvider()).
- registerExchangeProjector(projector) — adapter-owned
  exchange → conversation messages projection.
- localEndpoint, getClient, listClients, registerClient unchanged.

Removed from the capability surface: registerExchangeContextProjector
and registerMessageEnricher.

Projector dispatch in message_projector.js:
- Matching projectors sorted by descending priority, then
  registration order.
- First successful non-empty projection wins.
- Throws / undefined / invalid output: warn and skip.
- No projector matches: zero rows, but pass-through telemetry
  (aigw.exchange log, aigw.exchange_bytes meter) still fires.

Fallback identity (post-projection): when an adapter returns
normalized messages without message_id, compute hash id,
linearize previous_message_id (only if absent), and stamp
attributes.gateway.identity_source = "gateway_fallback".
previous_message_id supplied as [] is preserved (root marker).
Projector-supplied identity is authoritative — never overridden.

Deleted from the gateway package:
- Anthropic HTTP+SSE parsing (message_projector.js).
- OpenAI Chat and Responses + SSE reconstruction.
- Codex header / path / turn-metadata parsing.
- Generic context-fallback path, provider inference,
  Claude-specific attributes.client.claude_version.
- /_hypaware/session-context endpoint (proxy.js).
- ClaudeSessionContext, localContextForRequest, request-body
  session lookup, recorder cwd/git_branch injection.

Schema unchanged: ai_gateway_messages, SCHEMA_VERSION = 4,
proxy_messages_v4. No migration, no backfill.

Tests:
- ai-gateway-session-context.test.js deleted (endpoint removed).
- ai-gateway-message-projector.test.js rewritten to cover the
  new dispatch surface (priority/seq ordering, error/empty
  skipping, fallback identity stamping, schema strip) plus the
  schema column contract. Provider-specific assertions moved
  out of the gateway and will return in phases 2/3 plugin tests.
- ai_gateway_passthrough smoke updated to assert: capability
  registered at 2.0.0, no cache.append, zero rows for the
  dev_run_id, and pass-through telemetry (aigw.exchange log
  with rows_written=0, aigw.exchange_bytes meter) still fires.

This phase intentionally breaks Claude + Codex capture (their
manifests still require ^1.0.0) until phases 2 and 3 land
adapter exchange projectors on the same integration branch.
@philcunliffe philcunliffe merged commit 58b3252 into integration/ai-gateway-v2 May 22, 2026
6 checks passed
@philcunliffe philcunliffe deleted the polecat/hy-oy5h4 branch May 22, 2026 23:22
philcunliffe added a commit that referenced this pull request May 23, 2026
* chore: seed integration/ai-gateway-v2 for feature flow

* hy-oy5h4: Gateway core 2.0 — capability API, projector dispatch, delete provider parsers (#25)

Refactor @hypaware/ai-gateway into a generic HTTP/SSE capture and
row-storage owner. All client/protocol semantics move into adapter
plugins through the new full exchange projector API.

Capability bumped to breaking 2.0.0. New surface:
- registerUpstreamPreset(preset) — adapter-owned routing via
  optional preset.match() (replaces hardcoded detectProvider()).
- registerExchangeProjector(projector) — adapter-owned
  exchange → conversation messages projection.
- localEndpoint, getClient, listClients, registerClient unchanged.

Removed from the capability surface: registerExchangeContextProjector
and registerMessageEnricher.

Projector dispatch in message_projector.js:
- Matching projectors sorted by descending priority, then
  registration order.
- First successful non-empty projection wins.
- Throws / undefined / invalid output: warn and skip.
- No projector matches: zero rows, but pass-through telemetry
  (aigw.exchange log, aigw.exchange_bytes meter) still fires.

Fallback identity (post-projection): when an adapter returns
normalized messages without message_id, compute hash id,
linearize previous_message_id (only if absent), and stamp
attributes.gateway.identity_source = "gateway_fallback".
previous_message_id supplied as [] is preserved (root marker).
Projector-supplied identity is authoritative — never overridden.

Deleted from the gateway package:
- Anthropic HTTP+SSE parsing (message_projector.js).
- OpenAI Chat and Responses + SSE reconstruction.
- Codex header / path / turn-metadata parsing.
- Generic context-fallback path, provider inference,
  Claude-specific attributes.client.claude_version.
- /_hypaware/session-context endpoint (proxy.js).
- ClaudeSessionContext, localContextForRequest, request-body
  session lookup, recorder cwd/git_branch injection.

Schema unchanged: ai_gateway_messages, SCHEMA_VERSION = 4,
proxy_messages_v4. No migration, no backfill.

Tests:
- ai-gateway-session-context.test.js deleted (endpoint removed).
- ai-gateway-message-projector.test.js rewritten to cover the
  new dispatch surface (priority/seq ordering, error/empty
  skipping, fallback identity stamping, schema strip) plus the
  schema column contract. Provider-specific assertions moved
  out of the gateway and will return in phases 2/3 plugin tests.
- ai_gateway_passthrough smoke updated to assert: capability
  registered at 2.0.0, no cache.append, zero rows for the
  dev_run_id, and pass-through telemetry (aigw.exchange log
  with rows_written=0, aigw.exchange_bytes meter) still fires.

This phase intentionally breaks Claude + Codex capture (their
manifests still require ^1.0.0) until phases 2 and 3 land
adapter exchange projectors on the same integration branch.

* hy-zd4ga: Phase 3 - Codex plugin exchange projector (#28)

Port OpenAI Chat, OpenAI Responses (JSON + SSE), and ChatGPT Codex
(SSE + turn metadata) projection into a single
`AiGatewayExchangeProjector` owned by `@hypaware/codex`. Bumps the
plugin's capability requirement to `hypaware.ai-gateway@^2.0.0` and
adds the new projector to the activate() wiring.

Projector behavior:
- Matches `/v1/chat/completions`, `/v1/responses`, `/v1/models`,
  `/backend-api/codex/*`, or any request tagged with the
  `x-codex-turn-metadata` header.
- Parses Chat-shaped requests (`messages: []`) and Responses-shaped
  requests (`input: ...`) into normalized user-role blocks. Tool
  calls and tool results map onto `tool_use` / `tool_result` blocks.
- Reconstructs streamed assistant messages from
  `response.output_text.delta` events; falls back to body-level
  `output_text` or `output` arrays.
- Projects Codex headers + turn metadata into both first-class
  columns (`cwd`, `client_version`, `entrypoint`, `user_type`,
  `permission_mode`, `is_sidechain`, `request_id`, `prompt_id`) and
  the `attributes.codex.*` namespace (thread/session/turn ids,
  workspace, git origin + commit, sandbox, originator, window id).
- Always stamps `attributes.codex.identity_source = "gateway_fallback"`
  because the projector never supplies `message_id` today; the
  gateway computes hashes and linearizes history for symmetry with
  the @hypaware/claude adapter.
- Reserves a Codex SQLite / log-reader hook behind the
  `HYPAWARE_CODEX_SQLITE_READS=1` env flag; today no readers are
  shipped (no-op stub), keeping the real reader out of this bead.

Smoke + tests:
- `gateway_codex_capture` flow activates `@hypaware/codex` and renames
  its local fake upstreams to `local-openai` / `local-chatgpt` so they
  don't collide with the plugin's `openai` / `chatgpt` preset names.
  The local config entries appear first in the merged routing table
  and outrank the plugin presets at routing time.
- New `test/plugins/codex-exchange-projector.test.js` covers match
  surface, OpenAI Chat tool-call mapping, Responses SSE reconstruction,
  Codex header/metadata projection, subagent flipping is_sidechain,
  identity_source stamping symmetry, log-reader gating, and
  conversation-id fallback determinism.

Gateway core remains free of provider parsing (only
`chatgpt-account-id` appears as a default-redacted header, which is
a security setting rather than parsing logic).

\`npm test\` passes (280 tests). \`npm run typecheck\` clean.
\`npm run smoke -- gateway_codex_capture\` and
\`npm run smoke -- ai_gateway_passthrough\` both green.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* hy-hko73: Claude plugin exchange projector + state-file session-context (#30)

Phase 2 of the ai-gateway-v2 feature flow. Ports the Anthropic
HTTP+SSE parsing that phase 1 deleted from the gateway into the
`@hypaware/claude` adapter as a single `AiGatewayExchangeProjector`,
and moves the session-context channel from the (now-removed) HTTP
endpoint to a JSONL file under the plugin's state directory.

Plugin (`@hypaware/claude`, bumped to 2.0.0):
- New `projector.js` registers `AiGatewayExchangeProjector` and
  `AiGatewayUpstreamPreset` against `hypaware.ai-gateway@^2.0.0`.
  Match surface: `/v1/messages*` path OR `anthropic-version` /
  `x-api-key` / `Authorization: Bearer sk-ant-*` header signature.
- `anthropic.js` carries the ported Anthropic Messages HTTP body
  parse, SSE assistant reconstruction (`message_start`,
  `content_block_*`, `message_delta`, `message_stop`), conversation
  id / user id / system text / tools / client-version extraction.
- `transcripts.js` reads `<HOME>/.claude/projects/<repo>/<session>.jsonl`
  (or the exact `transcript_path` when the session-context record
  supplies one) for native DAG identity. Matches by uuid → projected
  message: `message_id = provider_uuid = uuid`,
  `previous_message_id = parentUuid ? [parentUuid] : []`.
- `session_context.js` reads/writes
  `<stateDir>/session-context.jsonl`. `pickLatestMatching` prefers
  `transcript_path` then `session_id`; newest matching line wins.
- On a missing transcript the projector returns messages without
  `message_id`, the gateway computes hash identity + stamps
  `attributes.gateway.identity_source="gateway_fallback"`, and the
  projector adds `attributes.claude.identity_source="gateway_fallback"`
  for Claude-specific debuggability.
- `settings.js` swaps the managed hook command from `--port <port>`
  to `--state-file <abspath>`; the `_hypaware` marker carries both
  port (still needed for `ANTHROPIC_BASE_URL`) and state_file.
- Deleted `enricher.js` (gateway 2.0 removed `registerMessageEnricher`;
  the projector inlines what enrichment did).

CLI:
- `hyp claude-hook session-context --state-file <path>` appends one
  JSONL record per hook event (session_id, cwd, optional
  transcript_path / git_branch / ts). No daemon required —
  the file is read at projection time.

Caller-side cleanup of stale `^1.0.0` ranges (phase 1 missed these):
- `src/core/cli/core_commands.js` and `src/core/cli/walkthrough.js`
  require `^2.0.0`.
- `src/core/config/validate.js` first-party metadata updated for
  ai-gateway (provides 2.0.0), claude and codex (both require
  ^2.0.0; codex projector landed in #28 alongside this).
- `walkthrough_*_to_first_query` smokes require `^2.0.0`; their
  remaining contract assertions (zero ai_gateway_messages rows
  because they POST to /v1/echo, not /v1/messages) are phase-4 work
  per the shared-harness scope.

Tests:
- `test/plugins/claude-projector-identity.test.js` covers native uuid
  identity, root `[]` previous_message_id, missing-log fallback
  marker, cwd/git_branch propagation from the state file, and the
  three `match()` paths (header signature, /v1/messages path, none).
- `test/plugins/claude-session-context-hook.test.js` is the
  hook→state-file→projector roundtrip — replaces the deleted
  `ai-gateway-session-context.test.js`.
- `test/core/command-dispatch.test.js` updated for the
  `--state-file` flag and the fake gateway capability bumped to
  2.0.0 surface (with `registerExchangeProjector`).
- `hypaware-core/smoke/flows/gateway_claude_capture.js` rewritten to
  activate both plugins, stage a JSONL transcript fixture, drive the
  hook, and assert both native-DAG and fallback-identity rows.
- `claude_attach_detach` smoke updated for the new marker shape and
  the `--state-file` hook command line.

277 tests / typecheck / lint clean. Smokes green:
- gateway_claude_capture
- claude_attach_detach
- ai_gateway_passthrough
- daemon_foreground_start_stop
- core_boot_noop

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* hy-1l4nl: Phase 4 - gateway API tests + ship gate (#31)

Adds dedicated gateway-package unit tests for the post-2.0 capability
API and proxy routing now that provider-specific projection logic lives
in the Claude and Codex plugins:

- test/plugins/ai-gateway-api.test.js covers registerExchangeProjector
  validation (name/match/project required, missing-priority preserved),
  registration-order _seq assignment, plus registerUpstreamPreset,
  registerClient/getClient/listClients, and localEndpoint shape.
- test/plugins/ai-gateway-proxy-routing.test.js covers compileUpstreams
  ordering (priority desc, then prefix length, then registration order),
  base_url validation, matchUpstream first-match-wins + short-circuit
  + throw-as-non-match + path-prefix fallback + lowercased array-valued
  header view.
- test/plugins/ai-gateway-message-projector.test.js gains three tests
  covering invalid-shape-skipped, all-projectors-fail returns zero rows
  with aigw.projector_error/_invalid_output/message_projection_skipped
  warnings, and skipping a non-matching projector without calling its
  project().

proxy.js exports matchUpstream and compileUpstreams so the routing
tests can exercise them in isolation; the runtime call sites are
unchanged.

The gateway package no longer asserts Anthropic/OpenAI/Codex protocol
details - phase 1 already stripped them and phases 2/3 added the
plugin-side coverage in claude-projector-identity, claude-session-
context-hook, and codex-exchange-projector. Pass-through telemetry on
the all-projectors-fail path is gated end-to-end by the existing
ai_gateway_passthrough smoke (no projector -> zero rows + aigw.exchange
log emitted).

Final check: npm test (319 pass), ai_gateway_passthrough,
gateway_codex_capture, and gateway_claude_capture smokes all green.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: address ai gateway v2 review findings

---------

Co-authored-by: feature-launch <feature-launch@gas.city>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant