Skip to content
The Assistants API shut down on 26 Aug 2026.

Old API.
New PR.

Landpatch is a GitHub App. It finds sunset OpenAI Assistants calls in your repository, stitches them onto the Responses API, checks the seams in a sandbox, and opens a pull request only when verification passes.

  • $2 LLM cap per run
  • PR only after verification
  • Self-hostable

hi, I'm Patch →

src/lib/server/assitant.tsverified

ai.beta.threads.runs.create(threadId, …)

ai.responses.create({ model, instructions, … })

PrabalParihar/chatbot#1 · +83 −64

Detects: beta.threads.runs.steps.*, beta.threads.runs.*, beta.threads.messages.*, beta.threads.*, beta.assistants.*, → responses.create, → conversations.create, $2 cap per run, 10-minute sandbox, PR only after verification.

How it works

Watch a real run land.

An installation webhook, or a scan you queue from the dashboard, starts a run. Scroll to replay the one behind PrabalParihar/chatbot#1, rebuilt from what the pull request records.

04

Pull request

replay · reconstructed from the pull request100%
runlandpatch run PrabalParihar/chatbotscanshallow clone, openai-assistants-to-responses packheldscripts/assistant.ts:37foundsrc/lib/server/assitant.ts:7foundsrc/lib/server/assitant.ts:24foundsrc/lib/server/assitant.ts:29foundsrc/lib/server/assitant.ts:34foundsrc/lib/server/assitant.ts:44patchfunction-scoped diff from the modelpatch5 call sites moved to the Responses APIheldscripts/assistant.ts:37 left for a maintainerverifynpm install --ignore-scripts --no-audit --no-fundverifytype check, lint, and test command, compared with the unpatched treeverifycontract testverifyverification passedpropened PrabalParihar/chatbot#1

● pull request opened

fix: migrate from sunset OpenAI Assistants API to Responses API

landpatch[bot] · PrabalParihar/chatbot · +83 −64 in 3 files

Four steps, no guessing.

Find every call

ast-grep rules match the Assistants surface, then the receiver is confirmed. 5 rules ship with the pack. These are the call sites it found in PrabalParihar/chatbot:

  • scripts/assistant.ts:37needs-human
  • assitant.ts:7simple-run
  • assitant.ts:24simple-run
  • assitant.ts:29simple-run
  • assitant.ts:34simple-run
  • assitant.ts:44thread-history

Patch on a budget

$2

LLM cap per run. A call over the estimate never starts.

Test the seams

10′

Sandbox cap: install, typecheck, lint, tests, contract test.

Then, and only then, a PR

landpatch[bot] opened fix: migrate from sunset OpenAI Assistants API to Responses API: +83 −64 across 3 files on 22 Oct 2026.

Hard cases wait for you

One call site attached uploaded files to an assistant. Moving them into a vector store needs a person, so Landpatch left it alone and wrote down why.

Proof, not promises

A pull request Landpatch actually opened.

landpatch[bot] opened this on PrabalParihar/chatbot on 22 Oct 2026. 5 call sites moved to the Responses API. 1 was held for a maintainer, with the reason written into the pull request.

fix: migrate from sunset OpenAI Assistants API to Responses API #1

PrabalParihar/chatbotlandpatch[bot]+83 −64 in 3 filesverification passed

Open on GitHub
Call sites in the pull request
FilePattern
scripts/assistant.ts:37needs-human
src/lib/server/assitant.ts:7simple-run
src/lib/server/assitant.ts:24simple-run
src/lib/server/assitant.ts:29simple-run
src/lib/server/assitant.ts:34simple-run
src/lib/server/assitant.ts:44thread-history
src/lib/server/assitant.ts    console.log('Working on question', question);-   let run = await ai.beta.threads.runs.create(threadId, { assistant_id: ASSISTANT_ID });+   const response = await ai.responses.create({+     model,+     instructions,+     conversation: conversationId,+     input+   });  -   console.log('Check status of run', run.id);-   console.time(run.id);-   do {-     run = await ai.beta.threads.runs.retrieve(run.thread_id, run.id);-     await new Promise<void>((resolve) => setTimeout(() => resolve(), 500));-   } while (run.status === 'in_progress' || run.status === 'queued');+   console.log('Check status of run', response.id);+   console.time(response.id);+   console.timeEnd(response.id);

Behaviour

  • Threads the code creates are now conversations, and the conversation id is handed back where the thread id was.
  • A stored assistant id has no model or instructions in the repository, so the code reads OPENAI_MODEL and OPENAI_INSTRUCTIONS and throws if either is unset.
  • The project's own test command needs secrets the sandbox does not have, so it was compared against the unpatched tree rather than required to pass.

Verification

  • npm install --ignore-scripts --no-audit --no-fund
  • type check, lint, and test command, compared with the unpatched tree
  • contract test
Seeded local exampleThe draft the product stores for nedon/nedon

server/services/ai.ts:18 · simple-run · assistants-to-responses → main · $0.044865 · 18,420 tokens

--- a/server/services/ai.ts+++ b/server/services/ai.ts@@ -16,8 +16,14 @@ export async function extractReceiptData(text: string) {-    const run = await openai.beta.threads.runs.createAndPoll(thread.id, {-      assistant_id: "asst_receipt_fixture",-    });-    if (run.status !== "completed") throw new Error(`OpenAI run failed with status: ${run.status}`);+    const model = process.env.OPENAI_MODEL;+    const instructions = process.env.OPENAI_INSTRUCTIONS;+    if (!model || !instructions) {+      throw new Error("Set OPENAI_MODEL and OPENAI_INSTRUCTIONS. Stored assistant: asst_receipt_fixture.");+    }+    const response = await openai.responses.create({ model, instructions, input: text });+    if (response.status !== "completed") throw new Error(`The model run failed with status: ${response.status}`);+    const jsonText = response.output_text;

pnpm db:seed writes this run so the dashboard has a verified patch to inspect. The title and body below come from the same pull request builder the worker uses. Seeding does not open a GitHub pull request.

fix: migrate from sunset OpenAI Assistants API to Responses API

The OpenAI Assistants API shut down on 26 August 2026. Calls to `/v1/assistants` and `/v1/threads` fail. This pull request moves the detected call sites onto the Responses and Conversations APIs.

Migration guide: https://developers.openai.com/api/docs/assistants/migration
Deprecations: https://developers.openai.com/api/docs/deprecations

## Call sites

| File | Pattern | Change |
| --- | --- | --- |
| `server/services/ai.ts:18` | simple-run | Moved the receipt extraction call onto the Responses API. |

## Behaviour

- Detected Assistants calls now use the Responses API.
- A stored assistant id with no instructions in the file reads OPENAI_INSTRUCTIONS and throws if it is unset.

## Configure

- Set OPENAI_MODEL and OPENAI_INSTRUCTIONS for each stored assistant id named in the diff.

## Verification

Verification passed.
- `pnpm exec tsc --noEmit --pretty false`
- `contract test`

## Rollback

Revert this pull request. The previous code still calls the sunset Assistants endpoints, so rollback restores the broken calls rather than a working older API.

Guardrails

Hard limits, sewn into the pipeline.

Later stages refuse work that would cross a limit. They do not just report it afterwards.

$2 budget per run

A model call that would pass the estimate does not start. Every run stores its tokens and cost.

Seeded sample run$0.045 of $2

10-minute sandbox

Install, typecheck, lint when configured, existing tests, and a contract test. Docker is used when the daemon is available, otherwise a local process. A longer request is cut down to the cap.

  • Install dependencies
  • Typecheck
  • Lint, when the project has a config
  • Existing tests
  • Contract test

Verification gate

No passing verification, no pull request. A flow held for a human is not verified and is not opened.

Held, not guessed

An assistant id with no local config becomes a TODO(landpatch) and throws until OPENAI_MODEL and OPENAI_INSTRUCTIONS are set.

Run it yourself

A root Dockerfile runs the web, API, or worker process, and a Compose file starts Postgres. Register your own GitHub App.

Least-privilege GitHub App

Contents
Read & write
Pull requests
Read & write
Checks
Read
Metadata
Read
Issues
Read

Subscribed events: Pull request, Issue comment. Installation events arrive as well. Deliveries are signature-checked and stored once per delivery id. The app does not ask for administration, secrets, or Actions.

Migration pack in this build

openai-assistants-to-responses

openai <6.x any → ^6 · 5 detection rules · patch scope is one function

FAQ

Questions engineers ask first.

Self-hosting can point the install button at another app with NEXT_PUBLIC_GITHUB_APP_INSTALL_URL.

What can it migrate today?

The OpenAI Assistants API to the Responses and Conversations APIs: calls on beta.assistants, threads, runs, messages, and steps. Another breaking change needs another pack. This repository does not ship a second one.

Will it open a pull request on its own?

Only after sandbox verification passes and that repository has a GitHub App installation. An unverified run does not open one. A patch held for a human is not verified. Without the app configured, the worker stores the title and body and does not call GitHub.

What does the GitHub App get access to?

The permissions and events listed under Guardrails are the manifest this repository registers. Installation events arrive as well. The app does not ask for administration, secrets, or Actions.

What happens to an assistant id?

Instructions already written in the file are kept. An assistant id with no local model or instructions becomes a TODO(landpatch) comment that names the id. The patched call reads OPENAI_MODEL and OPENAI_INSTRUCTIONS and throws if a required value is unset. Prompt ids are not used.

How much can one run spend?

The LLM budget is $2 per run. A call that would pass the estimate does not start. The sandbox is capped at 10 minutes, and a longer request is cut down to that cap. 3 pipeline jobs can run at a time.

Does it search all of GitHub?

No. GitHub code search is not connected. A run scans a repository the app was installed on, or a public repository you queue from the dashboard.

Can I run it myself?

Yes. The repository includes a root Dockerfile and a Compose file that starts Postgres. The image runs one of the web, API, or worker processes. E2B and Daytona sandbox runners are present and unconfigured. A live patch needs an inference key; the test suite does not call a model.

Got Assistants calls? We've got thread.

Install the app on a repository. Review the pull request when it arrives.