chore: save plan
This commit is contained in:
parent
835f1ff4a7
commit
4eb1b468ea
157
.plans/opencode-ai-cli-full-integration-plan.md
Normal file
157
.plans/opencode-ai-cli-full-integration-plan.md
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
# Plan: Full AI CLI Integration Through Real Opencode
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Run the real ComputerCraft `ai` CLI against a real `opencode serve` process through the WebSocket bridge proxy, while using the fake provider/model fixture proven by `opencode-fake-provider-direct-plan.md`.
|
||||||
|
|
||||||
|
This test should cover the actual runtime chain used in-game.
|
||||||
|
|
||||||
|
## Dependency
|
||||||
|
|
||||||
|
Do not implement this plan until `.plans/opencode-fake-provider-direct-plan.md` has produced a working fake provider fixture.
|
||||||
|
|
||||||
|
Update this plan first with the concrete results from plan 1:
|
||||||
|
|
||||||
|
- Working fake provider plugin code shape.
|
||||||
|
- Working opencode startup command/env.
|
||||||
|
- Working readiness endpoint.
|
||||||
|
- Any internal prompt behavior discovered.
|
||||||
|
|
||||||
|
## Desired Boundary
|
||||||
|
|
||||||
|
Real:
|
||||||
|
|
||||||
|
- CraftOS-PC harness
|
||||||
|
- `/programs/ai.lua`
|
||||||
|
- `/apis/libai.lua`
|
||||||
|
- `/apis/libhttpws.lua`
|
||||||
|
- `tools/mcp-bridge` opencode proxy
|
||||||
|
- `opencode serve`
|
||||||
|
- opencode sessions/messages/agents/model selection
|
||||||
|
|
||||||
|
Fake:
|
||||||
|
|
||||||
|
- The provider/model response behavior only, through the reusable fake provider fixture from plan 1
|
||||||
|
|
||||||
|
## Runtime Chain
|
||||||
|
|
||||||
|
```text
|
||||||
|
CraftOS /programs/ai.lua
|
||||||
|
-> libai.lua
|
||||||
|
-> libhttpws.lua
|
||||||
|
-> mcp-bridge opencode proxy
|
||||||
|
-> real opencode serve
|
||||||
|
-> fake provider/model
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Fixture
|
||||||
|
|
||||||
|
Reuse the fake provider workspace generator from plan 1.
|
||||||
|
|
||||||
|
Response mappings needed for the CLI cases:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{ "match": "reply with exactly: pong", "reply": "pong" },
|
||||||
|
{ "match": "fresh start", "reply": "new reply" },
|
||||||
|
{ "match": "continue please", "reply": "plain reply" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep the mapping fixture easy to extend so future CLI cases can add entries without changing provider code.
|
||||||
|
|
||||||
|
## CraftOS Wrapper
|
||||||
|
|
||||||
|
Create or update a Lua wrapper under:
|
||||||
|
|
||||||
|
- `tools/mcp-bridge/test-integration/lua/ai-cli-check.lua`
|
||||||
|
|
||||||
|
The wrapper should:
|
||||||
|
|
||||||
|
1. Accept the WebSocket proxy URL as its first argument.
|
||||||
|
2. Clear stale settings:
|
||||||
|
- `opencc.server_url`
|
||||||
|
- `opencc.session_id`
|
||||||
|
3. Set:
|
||||||
|
- `opencc.bridge_url`
|
||||||
|
- `opencc.request_timeout_seconds`
|
||||||
|
4. Run:
|
||||||
|
- `ai sessions`
|
||||||
|
- `ai ping`
|
||||||
|
- `ai new fresh start`
|
||||||
|
- `ai continue please`
|
||||||
|
5. Print markers around each command.
|
||||||
|
6. Print persisted session markers after commands.
|
||||||
|
7. Call `os.shutdown()`.
|
||||||
|
|
||||||
|
Expected marker examples:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
print('--- sessions ---');
|
||||||
|
shell.run('/programs/ai.lua', 'sessions');
|
||||||
|
|
||||||
|
print('--- ping ---');
|
||||||
|
shell.run('/programs/ai.lua', 'ping');
|
||||||
|
print('SESSION_AFTER_PING=' .. tostring(settings.get('opencc.session_id')));
|
||||||
|
|
||||||
|
print('--- new ---');
|
||||||
|
shell.run('/programs/ai.lua', 'new', 'fresh', 'start');
|
||||||
|
print('SESSION_AFTER_NEW=' .. tostring(settings.get('opencc.session_id')));
|
||||||
|
|
||||||
|
print('--- ask ---');
|
||||||
|
shell.run('/programs/ai.lua', 'continue', 'please');
|
||||||
|
print('SESSION_AFTER_ASK=' .. tostring(settings.get('opencc.session_id')));
|
||||||
|
```
|
||||||
|
|
||||||
|
## Node Test Implementation
|
||||||
|
|
||||||
|
Add or replace the current CLI integration test under:
|
||||||
|
|
||||||
|
- `tools/mcp-bridge/test-integration/ai-cli.test.ts`
|
||||||
|
|
||||||
|
Test steps:
|
||||||
|
|
||||||
|
1. Create temp fake-provider opencode workspace using the plan 1 fixture.
|
||||||
|
2. Start `opencode serve` on a random local port.
|
||||||
|
3. Poll until opencode is ready.
|
||||||
|
4. Start `startOpencodeProxy({ opencodeUrl })`.
|
||||||
|
5. Start CraftOS with:
|
||||||
|
- `mountRepo: true`
|
||||||
|
- `shellArgs: [proxyUrl]`
|
||||||
|
- a generous timeout, likely `30_000` or higher depending on measured opencode startup time
|
||||||
|
6. Assert CLI output includes:
|
||||||
|
- `pong`
|
||||||
|
- `new reply`
|
||||||
|
- `plain reply`
|
||||||
|
- session markers proving `ai new` replaces the session and plain `ai ...` reuses it
|
||||||
|
7. Stop CraftOS, proxy, and opencode in `finally`.
|
||||||
|
|
||||||
|
## Useful Assertions
|
||||||
|
|
||||||
|
- `ai sessions` exits without an opencode transport/config error.
|
||||||
|
- `ai ping` prints `pong`.
|
||||||
|
- `ai new fresh start` prints `new reply`.
|
||||||
|
- Plain `ai continue please` prints `plain reply`.
|
||||||
|
- `SESSION_AFTER_NEW` is non-empty.
|
||||||
|
- `SESSION_AFTER_ASK` equals `SESSION_AFTER_NEW`.
|
||||||
|
- If `SESSION_AFTER_PING` is printed, decide whether ping should persist a session or whether `ai ping` should become non-persistent in a separate behavior change.
|
||||||
|
|
||||||
|
## Current Open Questions
|
||||||
|
|
||||||
|
- Should `ai ping` persist `opencc.session_id`? Current `programs/ai.lua` calls `ai.ping(askOptions())`, and `libai.ping` behavior must be checked before asserting this too tightly.
|
||||||
|
- Should `ai sessions` be expected to show no sessions, one session, or just avoid failing before messages are created? Real opencode behavior may differ from the old fake HTTP server.
|
||||||
|
- Does opencode generate a title/summary for each message during the synchronous `/message` call? If yes, the fake provider fallback must make that harmless.
|
||||||
|
- What is the most stable way to choose a free opencode port in CI?
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
After implementation, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx tsx --test test-integration/opencode-fake-provider.test.ts
|
||||||
|
npx tsx --test test-integration/ai-cli.test.ts
|
||||||
|
npm run check
|
||||||
|
just check
|
||||||
|
```
|
||||||
|
|
||||||
|
If the full test is too slow for the default integration suite, keep it as a separately named test command or document why it is excluded from `npm run test:integration`.
|
||||||
121
.plans/opencode-fake-provider-direct-plan.md
Normal file
121
.plans/opencode-fake-provider-direct-plan.md
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
# Plan: Direct Fake Provider Integration
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Prove that a real `opencode serve` process can run with a deterministic fake provider/model and answer HTTP API requests without calling an external LLM.
|
||||||
|
|
||||||
|
This plan deliberately stops before CraftOS, the WebSocket bridge, or `/programs/ai.lua`. It validates only the opencode-side fixture that the full integration test will reuse.
|
||||||
|
|
||||||
|
## Desired Boundary
|
||||||
|
|
||||||
|
Real:
|
||||||
|
|
||||||
|
- `opencode serve`
|
||||||
|
- opencode config validation and loading
|
||||||
|
- opencode session/message HTTP endpoints
|
||||||
|
- opencode model/provider selection
|
||||||
|
- opencode agent/title/summary plumbing as far as it is triggered by simple messages
|
||||||
|
|
||||||
|
Fake:
|
||||||
|
|
||||||
|
- The provider/model response behavior only
|
||||||
|
|
||||||
|
## Proposed Test Fixture
|
||||||
|
|
||||||
|
Create a test-only temporary opencode workspace during the test. Do not modify the project `.opencode/opencode.json` for this.
|
||||||
|
|
||||||
|
Files generated under a temp directory:
|
||||||
|
|
||||||
|
- `opencode.json`
|
||||||
|
- `fake-provider.ts` or `fake-provider.js`
|
||||||
|
- `fake-responses.json`
|
||||||
|
|
||||||
|
Example response mapping:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{ "match": "reply with exactly: pong", "reply": "pong" },
|
||||||
|
{ "match": "fresh start", "reply": "new reply" },
|
||||||
|
{ "match": "continue please", "reply": "plain reply" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The fake provider should return the first response whose `match` appears in the final model prompt. Unknown prompts should return a deterministic fallback such as `ok` or `unhandled fake prompt`, not fail immediately, because opencode may issue title/summary/internal prompts.
|
||||||
|
|
||||||
|
## Config Shape To Validate
|
||||||
|
|
||||||
|
Use the published schema as the source of truth before finalizing fields.
|
||||||
|
|
||||||
|
Candidate config:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"model": "traptest/fake",
|
||||||
|
"small_model": "traptest/fake",
|
||||||
|
"enabled_providers": ["traptest"],
|
||||||
|
"plugin": ["./fake-provider.ts"],
|
||||||
|
"provider": {
|
||||||
|
"traptest": {
|
||||||
|
"name": "Trap Test",
|
||||||
|
"models": {
|
||||||
|
"fake": {
|
||||||
|
"id": "fake",
|
||||||
|
"name": "Trap Test Fake Model",
|
||||||
|
"limit": { "context": 100000, "output": 10000 },
|
||||||
|
"cost": { "input": 0, "output": 0 },
|
||||||
|
"status": "active"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"agent": {
|
||||||
|
"build": { "model": "traptest/fake" },
|
||||||
|
"title": { "model": "traptest/fake" },
|
||||||
|
"summary": { "model": "traptest/fake" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Open question: the exact plugin provider hook shape must be verified against opencode's runtime/plugin API. Do not guess this implementation from the config schema alone.
|
||||||
|
|
||||||
|
## Test Implementation
|
||||||
|
|
||||||
|
Add a Node integration test, likely under:
|
||||||
|
|
||||||
|
- `tools/mcp-bridge/test-integration/opencode-fake-provider.test.ts`
|
||||||
|
|
||||||
|
Test steps:
|
||||||
|
|
||||||
|
1. Create a temp directory.
|
||||||
|
2. Write the test `opencode.json`.
|
||||||
|
3. Write `fake-responses.json`.
|
||||||
|
4. Write the fake provider plugin.
|
||||||
|
5. Start `opencode serve` on `127.0.0.1` with a random free port.
|
||||||
|
6. Wait for readiness by polling an HTTP endpoint such as `GET /session`.
|
||||||
|
7. Call opencode HTTP directly:
|
||||||
|
- `POST /session`
|
||||||
|
- `POST /session/:id/message` with `reply with exactly: pong`
|
||||||
|
- `POST /session/:id/message` with `fresh start`
|
||||||
|
8. Assert the responses contain `pong` and `new reply`.
|
||||||
|
9. Stop the opencode process and clean up the temp directory.
|
||||||
|
|
||||||
|
## Useful Assertions
|
||||||
|
|
||||||
|
- `opencode serve` starts successfully with the generated config.
|
||||||
|
- `POST /session` returns a usable session ID.
|
||||||
|
- `POST /session/:id/message` returns text from the fake mapping.
|
||||||
|
- Unknown/internal prompts do not break the test fixture.
|
||||||
|
- No external provider credentials are required.
|
||||||
|
|
||||||
|
## Result To Capture For Plan 2
|
||||||
|
|
||||||
|
After this plan is run, record:
|
||||||
|
|
||||||
|
- Exact working fake provider plugin API shape.
|
||||||
|
- Exact command/env used to start `opencode serve` reliably.
|
||||||
|
- Confirmed readiness endpoint and polling logic.
|
||||||
|
- Whether title/summary/internal model calls happen during simple message requests.
|
||||||
|
- Any required config fields not listed above.
|
||||||
|
|
||||||
|
Plan 2 should be updated with these facts before implementation.
|
||||||
Loading…
Reference in New Issue
Block a user