Skip to content

fix(agent): let the gateway own the developer role - #1268

Draft
gewenyu99 wants to merge 2 commits into
mainfrom
posthog/gateway-owns-developer-role
Draft

gewenyu99 wants to merge 2 commits into
mainfrom
posthog/gateway-owns-developer-role

Conversation

@gewenyu99

Copy link
Copy Markdown
Collaborator

Must merge with PostHog/ai-gateway#455

This PR and ai-gateway#455 are one change in two repositories. Neither is complete alone.

  • ai-gateway#455 makes admission refuse a caller message at the developer role while an operator prompt applies.
  • This PR stops the wizard from sending that role, so the wizard is never the caller that gets refused.

Order: this PR merges and ships in a released wizard before AI_GATEWAY_PROMPT_POLICY_ENABLED is turned on for a deploy that serves wizard tokens. The policy is off by default, so merging #455 alone changes nothing in the field. Enabling it while an older wizard is in use would answer 400 to every OpenAI-model run.

Why

On reasoning models the developer role supersedes an earlier system message. A caller message at that rank therefore outranks the gateway's spliced operator prompt, which is the bypass the gateway now refuses.

pi picks the role from the model spec: the Responses provider sends the system prompt as developer whenever model.reasoning is true and the spec does not say otherwise. Every openai/* wizard model is a reasoning model and runs over the Responses transport, so today the wizard sends exactly the message the gateway is about to refuse.

The denial is deliberately indistinguishable from a malformed body: a bare 400, no reason, nothing that names a policy. The wizard therefore cannot classify it after the fact, and cannot give the user a useful message. The defence has to be preventive.

What is here

  • buildGatewayModel declares compat: { supportsDeveloperRole: false }, so pi sends the wizard's instructions as system on both the Responses and Messages transports. The wizard keeps its instructions; they sit one rank below the operator prompt, which is the intended hierarchy.
  • A test asserts the flag for an OpenAI model and an Anthropic model, so a future spec change cannot quietly reintroduce the role.
  • The wizard-development skill records the rule: the operator rank belongs to the gateway, and the guard is the model spec plus its test rather than error handling.

Still planned in this draft

  • Wire-level guard: assert that the JSON pi builds for a gateway run carries no developer role, instead of asserting the spec field. That survives a pi refactor which renames the compat key; the current test does not.
  • Confirm the same holds on the one-shot path in triage-provider.ts, which builds a model through buildGatewayModel and calls completeSimple.
  • Re-check after the next pi version bump. supportsDeveloperRole is pi's contract, not ours, and a rename there is silent.
  • Decide where the required wizard version is recorded, so that enabling the gateway policy has an explicit version precondition rather than a remembered one.

Verification

  • vitest run src/lib/agent/runner/harness/pi/__tests__/gateway.test.ts: 18 passed.
  • Prettier clean on the changed files.
  • End-to-end proof of the pair runs on the local stack (gateway built from chore(main): release 2.13.1 #455 with the policy on, wizard from this branch): a run completes with X-PostHog-Prompt-Policy set and no 400 at admission.

Created with PostHog Desktop

pi sends the system prompt as a developer message on a reasoning model,
and the gateway refuses a caller message at that rank while an operator
prompt applies. The gateway model spec now declares
supportsDeveloperRole: false, so both transports send `system` and the
wizard keeps its instructions one rank below the operator prompt.

Generated-By: PostHog Desktop
Task-Id: 3cd2649f-0bee-4b78-a698-9309de41961c
Generated-By: PostHog Desktop
Task-Id: 3cd2649f-0bee-4b78-a698-9309de41961c
@github-actions

Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci ai-observability
  • /wizard-ci basic-integration
  • /wizard-ci mcp-analytics
  • /wizard-ci replay-vision
  • /wizard-ci revenue
  • /wizard-ci self-driving
  • /wizard-ci warehouse
  • /wizard-ci warehouse-seeded

Test an individual app:

  • /wizard-ci ai-observability/anthropic
  • /wizard-ci ai-observability/google-adk
  • /wizard-ci ai-observability/groq
Show more apps
  • /wizard-ci ai-observability/manual-capture
  • /wizard-ci ai-observability/openai
  • /wizard-ci ai-observability/openai-agents
  • /wizard-ci ai-observability/opentelemetry
  • /wizard-ci ai-observability/vercel-ai
  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/flutter
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci replay-vision/javascript-node
  • /wizard-ci replay-vision/next-js
  • /wizard-ci replay-vision/react-vite
  • /wizard-ci revenue/stripe
  • /wizard-ci self-driving/astro
  • /wizard-ci self-driving/fastapi
  • /wizard-ci self-driving/nuxt
  • /wizard-ci self-driving/react-router
  • /wizard-ci self-driving/sveltekit
  • /wizard-ci warehouse/monorepo-env
  • /wizard-ci warehouse/multi-source-next
  • /wizard-ci warehouse/stripe-node
  • /wizard-ci warehouse/zero-source
  • /wizard-ci warehouse-seeded/next-stripe
  • /wizard-ci warehouse-seeded/next-stripe-declined

Test against a Context Mill branch:

  • /wizard-ci all context-mill:my-branch

Add context-mill:<branch> to any command above to pin the Context Mill branch. It defaults to main.

Results will be posted here when complete.

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