Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions .github/workflows/opencode2-real-adapter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ on:
pull_request:
paths:
- "src/source/opencode2/**"
- "src/source/runtime/events.js"
- "scripts/fixtures/opencode2-real-adapter-probe.js"
- "scripts/fixtures/opencode2-real-session-probe.js"
- "scripts/opencode2-real-session-canary.mjs"
- ".github/workflows/opencode2-real-adapter.yml"
push:
branches: [main]
paths:
- "src/source/opencode2/**"
- "src/source/runtime/events.js"
- "scripts/fixtures/opencode2-real-adapter-probe.js"
- "scripts/fixtures/opencode2-real-session-probe.js"
- "scripts/opencode2-real-session-canary.mjs"
- ".github/workflows/opencode2-real-adapter.yml"

permissions:
Expand All @@ -19,7 +25,7 @@ permissions:
jobs:
adapter:
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -66,10 +72,13 @@ jobs:
if [ -f "$OPENCODE_LOOP_V2_MARKER" ]; then
cat "$OPENCODE_LOOP_V2_MARKER"
node -e 'const fs=require("node:fs"); const value=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); if(value.id!=="bybrawe.opencode-loop.v2.experimental" || value.activated!==true || value.commandTransform!==true || value.eventSubscribe!==true || value.sessionPrompt!==true) process.exit(1)' "$OPENCODE_LOOP_V2_MARKER"
exit 0
break
fi
sleep 0.2
done
cat "$RUNNER_TEMP/opencode2-real-command.json" >&2 || true
cat "$RUNNER_TEMP/opencode2-real-plugin.json" >&2 || true
exit 1
test -f "$OPENCODE_LOOP_V2_MARKER"
- name: Verify real V2 session prompt and event stream
run: |
node --check scripts/fixtures/opencode2-real-session-probe.js
node --check scripts/opencode2-real-session-canary.mjs
node scripts/opencode2-real-session-canary.mjs
96 changes: 96 additions & 0 deletions scripts/fixtures/opencode2-real-session-probe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { writeFile } from "node:fs/promises"

function eventStream(registration) {
const stream = registration?.stream ?? registration
if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
throw new TypeError("V2 event subscription did not return an async stream")
}
return stream
}

function rawType(value) {
return value?.payload?.type || value?.event?.type || value?.type
}

function rawSessionID(value) {
const event = value?.payload || value?.event || value
return event?.properties?.sessionID || event?.properties?.info?.id
}

export default {
id: "bybrawe.opencode-loop.v2.real-session-probe",
async setup(ctx) {
const runtimeURL = process.env.OPENCODE_LOOP_V2_RUNTIME_URL
const marker = process.env.OPENCODE_LOOP_V2_SESSION_MARKER
if (!runtimeURL) throw new Error("OPENCODE_LOOP_V2_RUNTIME_URL is required")
if (!marker) throw new Error("OPENCODE_LOOP_V2_SESSION_MARKER is required")

const { createOpenCode2RuntimeAdapter } = await import(runtimeURL)
const state = {
ready: false,
promptStarted: false,
promptCompleted: false,
rawTypes: [],
normalized: [],
}
let markerWrite = Promise.resolve()
const persist = () => {
markerWrite = markerWrite.then(() => writeFile(marker, JSON.stringify(state, null, 2), "utf8"))
return markerWrite
}

const adapter = createOpenCode2RuntimeAdapter(ctx, {
onEvent: async (event) => {
state.normalized.push(`${event.kind}:${event.action}`)
if (state.normalized.length > 80) state.normalized.shift()
await persist()
},
onError: (error) => {
state.error = String(error?.stack || error)
void persist()
},
})
await adapter.start()

const registration = await ctx.event.subscribe()
const iterator = eventStream(registration)[Symbol.asyncIterator]()
let stopped = false
const pump = (async () => {
while (!stopped) {
const next = await iterator.next()
if (next?.done) break
const type = rawType(next.value)
if (type) {
state.rawTypes.push(type)
if (state.rawTypes.length > 120) state.rawTypes.shift()
}
const sessionID = rawSessionID(next.value)
if (!state.promptStarted && type === "session.created" && sessionID) {
state.promptStarted = true
await persist()
try {
await adapter.prompt({ sessionID, text: "V2_REAL_SESSION_CANARY", delivery: "queue", resume: true })
state.promptCompleted = true
} catch (error) {
state.promptError = String(error?.stack || error)
}
}
await persist()
}
})().catch((error) => {
state.streamError = String(error?.stack || error)
void persist()
})

state.ready = true
await persist()

return async () => {
stopped = true
await iterator.return?.().catch?.(() => undefined)
await adapter.dispose("real-session-probe-cleanup")
await pump.catch(() => undefined)
await markerWrite.catch(() => undefined)
}
},
}
Loading
Loading