Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
0fbb744
feat: add provider adapter interface
wryan2986 Jul 22, 2026
8f87ddf
feat: add structured event logging
wryan2986 Jul 22, 2026
63a2d44
feat: persist task budget ledgers
wryan2986 Jul 22, 2026
43549f8
feat: define structured event schema
wryan2986 Jul 22, 2026
2f5b5b2
feat: add event log query utility
wryan2986 Jul 22, 2026
d1f1a51
docs: add custom provider adapter example
wryan2986 Jul 22, 2026
09f46a2
docs: document provider adapters
wryan2986 Jul 22, 2026
98a4a85
docs: document structured event logging
wryan2986 Jul 22, 2026
217f521
docs: add platform support guide
wryan2986 Jul 22, 2026
63997c5
docs: expand runtime troubleshooting
wryan2986 Jul 22, 2026
d27f09f
test: cover v0.2 reliability features
wryan2986 Jul 22, 2026
f7bd4b5
ci: add OpenCode patch compatibility workflow
wryan2986 Jul 22, 2026
7bba5b2
ci: verify patched OpenCode contracts
wryan2986 Jul 22, 2026
72ea8ff
ci: test portable runtime across operating systems
wryan2986 Jul 22, 2026
fc6e0e6
feat: persist budgets and cap workflow calls
wryan2986 Jul 22, 2026
c58fa99
fix: resolve providers and executables portably
wryan2986 Jul 22, 2026
b2649fe
feat: enforce retries and structured runtime events
wryan2986 Jul 22, 2026
cc9f5b4
feat: guard workflow calls and emit lifecycle events
wryan2986 Jul 22, 2026
5ac0160
feat: configure persistent budgets and events
wryan2986 Jul 22, 2026
ccdcb25
feat: validate v0.2 runtime policy
wryan2986 Jul 22, 2026
044d1b8
chore: add v0.2 release migration
wryan2986 Jul 22, 2026
5ab59d6
chore: apply v0.2 release migration
wryan2986 Jul 22, 2026
e9aaea9
feat: finish v0.2 reliability foundations
github-actions[bot] Jul 22, 2026
25faa8f
docs: update roadmap for v0.2
wryan2986 Jul 22, 2026
6746612
docs: document v0.2 policy and state
wryan2986 Jul 22, 2026
9eeeb40
docs: describe v0.2 runtime architecture
wryan2986 Jul 22, 2026
8c14959
docs: present v0.2 reliability features
wryan2986 Jul 22, 2026
8f9f2a8
docs: document automated patch compatibility
wryan2986 Jul 22, 2026
d6cf019
docs: finalize v0.2.0 changelog
wryan2986 Jul 22, 2026
59424c8
chore: remove one-time v0.2 migration workflow
wryan2986 Jul 22, 2026
92101c3
chore: remove one-time v0.2 migration script
wryan2986 Jul 22, 2026
9ebfaac
docs: add v0.2 release notes
wryan2986 Jul 22, 2026
29d4a04
docs: add database migration workflow example
wryan2986 Jul 22, 2026
da7e8c9
test: construct credential fixtures safely
wryan2986 Jul 22, 2026
9b21b50
feat: allow event log path overrides
wryan2986 Jul 22, 2026
6a30f9e
test: verify same-model retries before failover
wryan2986 Jul 22, 2026
ab94a17
chore: add final v0.2 hardening migration
wryan2986 Jul 22, 2026
9de2d07
chore: apply final v0.2 hardening
wryan2986 Jul 22, 2026
f765eb1
fix: finalize v0.2 runtime hardening
github-actions[bot] Jul 22, 2026
655e9f8
feat: add provider-specific retry policies
wryan2986 Jul 22, 2026
143f66e
chore: remove one-time hardening workflow
wryan2986 Jul 22, 2026
c7ef6df
chore: remove one-time hardening script
wryan2986 Jul 22, 2026
e6abb7f
chore: add provider retry integration migration
wryan2986 Jul 22, 2026
42ebfe1
chore: apply provider retry integration
wryan2986 Jul 22, 2026
1227c9a
feat: support provider-specific retry policies
github-actions[bot] Jul 22, 2026
8d94d99
ci: exercise installer in platform POSIX shells
wryan2986 Jul 22, 2026
11c7c63
chore: remove one-time provider retry workflow
wryan2986 Jul 22, 2026
0bc3bc2
chore: remove one-time provider retry migration
wryan2986 Jul 22, 2026
d1fbd67
test: make fake OpenCode executable portable
wryan2986 Jul 22, 2026
9fdd970
test: use executable prefix arguments portably
wryan2986 Jul 22, 2026
194ae08
chore: add portable executable prefix migration
wryan2986 Jul 22, 2026
2ac7f03
chore: apply portable executable prefix support
wryan2986 Jul 22, 2026
6020d3a
fix: support portable worker executable prefixes
github-actions[bot] Jul 22, 2026
b6aa875
chore: remove one-time executable workflow
wryan2986 Jul 22, 2026
d5d8bab
chore: remove one-time executable migration
wryan2986 Jul 22, 2026
5c6f1c4
ci: split portable checks for actionable failures
wryan2986 Jul 22, 2026
9089fa7
test: handle Windows termination semantics
wryan2986 Jul 22, 2026
45d1711
ci: verify OpenCode patch on compatibility changes
wryan2986 Jul 22, 2026
2770b1f
ci: identify compatible OpenCode patch base
wryan2986 Jul 22, 2026
2625dc3
ci: pin supported OpenCode revision
wryan2986 Jul 22, 2026
7677567
ci: build pinned OpenCode and probe upstream drift
wryan2986 Jul 22, 2026
0054180
chore: remove compatibility discovery workflow
wryan2986 Jul 22, 2026
d91f0b7
docs: use pinned supported OpenCode revision
wryan2986 Jul 22, 2026
c25caa3
ci: report upstream patch drift without failing
wryan2986 Jul 22, 2026
ccc6090
docs: align event names and compatibility
wryan2986 Jul 22, 2026
f7f45f3
fix: require a non-null workflow call ceiling
wryan2986 Jul 22, 2026
d4cd72c
fix: require workflow call budget in validation
wryan2986 Jul 22, 2026
34f8833
test: cover provider-specific retry policy
wryan2986 Jul 22, 2026
407f8bd
ci: build native patched OpenCode binary
wryan2986 Jul 22, 2026
8e677cd
chore: add retry default migration
wryan2986 Jul 22, 2026
7c57421
chore: apply configured retry defaults
wryan2986 Jul 22, 2026
1c2f913
chore: apply retry-default migration in release CI
wryan2986 Jul 22, 2026
99cc903
chore: commit retry-default transformation before matrix validation
wryan2986 Jul 22, 2026
6a218fe
fix: use configured retry defaults
github-actions[bot] Jul 22, 2026
ff8a10c
chore: remove one-time retry migration workflow
wryan2986 Jul 22, 2026
bcf26d9
chore: remove one-time retry migration script
wryan2986 Jul 22, 2026
dd4db5f
chore: restore standard CI workflow
wryan2986 Jul 22, 2026
00442c5
chore: preserve low-level executor retry default
wryan2986 Jul 22, 2026
1babed8
chore: apply low-level retry default in release CI
wryan2986 Jul 22, 2026
06b1beb
fix: preserve internal failover retry default
github-actions[bot] Jul 22, 2026
ed7c629
chore: remove internal retry migration script
wryan2986 Jul 22, 2026
49cf546
chore: restore standard CI after retry migration
wryan2986 Jul 22, 2026
67ff873
test: pass retry override to low-level executor
wryan2986 Jul 22, 2026
0bf739c
ci: preserve patched OpenCode build diagnostics
wryan2986 Jul 22, 2026
7eb89d5
chore: add Windows test path migration
wryan2986 Jul 22, 2026
d6b304e
chore: apply Windows-safe runtime test paths
wryan2986 Jul 22, 2026
def42f7
test: use Windows-safe repository paths
github-actions[bot] Jul 22, 2026
4937cb5
chore: remove Windows path migration script
wryan2986 Jul 22, 2026
f233a39
chore: restore standard release CI
wryan2986 Jul 22, 2026
e782267
ci: preserve platform runtime-test diagnostics
wryan2986 Jul 22, 2026
7807d69
fix: persist paid fallback audit state portably
wryan2986 Jul 22, 2026
3279c34
chore: add paid fallback state migration
wryan2986 Jul 22, 2026
13bc5a8
chore: apply portable paid fallback state
wryan2986 Jul 22, 2026
0ee6aca
fix: persist paid fallback audit state portably
github-actions[bot] Jul 22, 2026
95eccdf
chore: remove paid fallback migration workflow
wryan2986 Jul 22, 2026
2988c78
chore: remove paid fallback migration script
wryan2986 Jul 22, 2026
dcc80ff
test: normalize bypass scan paths on Windows
wryan2986 Jul 22, 2026
6388295
fix: window persistent paid fallback counters
wryan2986 Jul 22, 2026
d1a48a3
test: cover persistent paid fallback windows
wryan2986 Jul 22, 2026
1a59050
test: include persistent paid fallback coverage
wryan2986 Jul 22, 2026
deae927
config: define paid fallback counter windows
wryan2986 Jul 22, 2026
e32aaa5
docs: explain paid fallback counter windows
wryan2986 Jul 22, 2026
514a759
config: validate paid fallback state windows
wryan2986 Jul 22, 2026
f2374d1
ci: use Bun required by supported OpenCode
wryan2986 Jul 22, 2026
1f8c596
ci: verify actual patched OpenCode contracts
wryan2986 Jul 22, 2026
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
88 changes: 88 additions & 0 deletions .github/workflows/cross-platform.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Cross-platform runtime

on:
pull_request:
branches: [main, master]
push:
branches: [main, master]
workflow_dispatch:

permissions:
contents: read

jobs:
portable-runtime:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [20, 22]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- name: Validate routing policy
run: npm run validate:routing
- name: Validate budget policy
run: npm run validate:budget
- name: Validate feature contract
run: npm run validate:feature
- name: Validate documentation links
run: node scripts/check-doc-links.mjs
- name: Test v0.2 reliability features
run: node tests/reliability-v020-tests.mjs
- name: Test budget enforcement
run: node tests/budget-tests.mjs
- name: Test budget edge cases
run: node tests/budget-audit-tests.mjs
- name: Test routing
run: node tests/routing-tests.mjs
- name: Test runtime
shell: bash
run: |
set +e
node tests/runtime-tests.mjs 2>&1 | tee runtime-test.log
status=${PIPESTATUS[0]}
set -e
exit "$status"
- name: Upload runtime-test diagnostics
if: failure()
uses: actions/upload-artifact@v4
with:
name: runtime-test-${{ matrix.os }}-node-${{ matrix.node-version }}
path: runtime-test.log
- name: Test plugin integration
run: node tests/tool-integration-tests.mjs
- name: Test bypass detection
run: node tests/bypass-detection.mjs
- name: Test installer in POSIX shell
shell: bash
run: |
set -euo pipefail
test_home="$PWD/.installer-test-home"
test_bin="$PWD/.installer-test-bin"
mkdir -p "$test_home" "$test_bin"
: > "$test_home/.bashrc"
cat > "$test_bin/opencode" <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
chmod +x "$test_bin/opencode"
HOME="$test_home" PATH="$test_bin:$PATH" bash scripts/install.sh
grep -F 'OPENCODE_CONFIG_DIR=' "$test_home/.bashrc"
rm -rf "$test_home" "$test_bin"

bash-installation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run validate
140 changes: 140 additions & 0 deletions .github/workflows/opencode-patch-compat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: OpenCode patch compatibility

on:
pull_request:
branches: [main, master]
paths:
- patches/opencode-subagent-failure-exposure.patch
- config/supported-opencode.json
- scripts/check-opencode-patch-contract.mjs
- .github/workflows/opencode-patch-compat.yml
workflow_dispatch:
inputs:
opencode_ref:
description: Optional OpenCode branch, tag, or commit. Empty uses the pinned supported revision.
required: false
default: ''
schedule:
- cron: '23 6 * * 1'

permissions:
contents: read

jobs:
supported-compatibility:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Check out agent loop
uses: actions/checkout@v4

- name: Resolve supported revision
id: supported
shell: bash
env:
REQUESTED_REF: ${{ inputs.opencode_ref }}
run: |
set -euo pipefail
configured_ref=$(node -e "const fs=require('fs'); const value=JSON.parse(fs.readFileSync('config/supported-opencode.json','utf8')); process.stdout.write(value.ref)")
selected_ref=${REQUESTED_REF:-$configured_ref}
echo "ref=$selected_ref" >> "$GITHUB_OUTPUT"
echo "Testing OpenCode revision $selected_ref"

- name: Check out supported OpenCode
uses: actions/checkout@v4
with:
repository: anomalyco/opencode
ref: ${{ steps.supported.outputs.ref }}
path: upstream-opencode
fetch-depth: 1

- name: Verify patch applies
working-directory: upstream-opencode
run: git apply --check ../patches/opencode-subagent-failure-exposure.patch

- name: Apply patch
working-directory: upstream-opencode
run: git apply ../patches/opencode-subagent-failure-exposure.patch

- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: '1.3.14'

- name: Install upstream dependencies
working-directory: upstream-opencode
run: bun install --frozen-lockfile

- name: Build native standalone OpenCode
working-directory: upstream-opencode
shell: bash
run: |
set +e
bun ./packages/opencode/script/build.ts --single --skip-install --skip-embed-web-ui 2>&1 | tee ../opencode-build.log
status=${PIPESTATUS[0]}
set -e
exit "$status"

- name: Verify patched source contracts
run: node scripts/check-opencode-patch-contract.mjs upstream-opencode

- name: Record tested revision
if: always()
shell: bash
run: |
git -C upstream-opencode rev-parse HEAD > opencode-tested-revision.txt
printf 'requested_ref=%s\n' '${{ steps.supported.outputs.ref }}' >> opencode-tested-revision.txt
printf 'tested_at=%s\n' "$(date -u +%FT%TZ)" >> opencode-tested-revision.txt

- name: Upload compatibility evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: opencode-supported-compatibility
path: |
opencode-tested-revision.txt
opencode-build.log

upstream-dev-probe:
if: github.event_name != 'workflow_dispatch' || inputs.opencode_ref == ''
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: anomalyco/opencode
ref: dev
path: upstream-opencode-dev
fetch-depth: 1
- name: Probe patch against moving dev branch
id: probe
working-directory: upstream-opencode-dev
shell: bash
run: |
set +e
git apply --check ../patches/opencode-subagent-failure-exposure.patch > ../opencode-dev-patch-check.txt 2>&1
status=$?
set -e
if [ "$status" -eq 0 ]; then
echo "compatible=true" >> "$GITHUB_OUTPUT"
echo "### Upstream dev probe: compatible" >> "$GITHUB_STEP_SUMMARY"
else
echo "compatible=false" >> "$GITHUB_OUTPUT"
echo "### Upstream dev probe: patch rebase needed" >> "$GITHUB_STEP_SUMMARY"
echo "The pinned supported revision remains the release target. See the uploaded probe artifact for details." >> "$GITHUB_STEP_SUMMARY"
fi
exit 0
- name: Record upstream revision
if: always()
shell: bash
run: |
git -C upstream-opencode-dev rev-parse HEAD > opencode-dev-probe.txt
printf 'patch_compatible=%s\n' '${{ steps.probe.outputs.compatible }}' >> opencode-dev-probe.txt
cat opencode-dev-patch-check.txt >> opencode-dev-probe.txt
- name: Upload upstream probe evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: opencode-dev-probe
path: opencode-dev-probe.txt
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Thumbs.db
!.opencode/command/feature.md
.opencode/node_modules/
.opencode/agent-loop-logs/
.opencode/agent-loop-state/
*.bak

# Benchmark output (gitignored temp location)
Expand Down
9 changes: 5 additions & 4 deletions .opencode/plugins/agent-loop.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function concise(result) {
review: result.review,
requiresUserInput: result.requiresUserInput === true,
logPath: result.logPath,
eventLogPath: result.eventLogPath || undefined,
budget: result.budget || undefined,
steps
};
Expand All @@ -52,10 +53,10 @@ export default async function AgentLoopPlugin() {
description: 'Run the OpenCode agent-loop runtime for a development task. Use for non-trivial build/test/review work; do not use for simple questions.',
args: {
task: tool.schema.string().min(1).describe('The complete user request to run through the agent loop.'),
mode: tool.schema.enum(['build', 'test', 'review', 'smoke', 'escalate']).optional().describe('Which role to run: build, test, review, smoke (model test), or escalate (GPT-5.5 diagnosis)'),
maxRetries: tool.schema.number().int().min(0).max(5).optional().describe('Maximum task-level retry cycles. Provider failover is handled separately.'),
mode: tool.schema.enum(['build', 'test', 'review', 'smoke', 'escalate']).optional().describe('Which role to run: build, test, review, smoke (model test), or escalate (GPT-5.6 diagnosis)'),
maxRetries: tool.schema.number().int().min(0).max(5).optional().describe('Maximum same-model retries for transient failures. Provider failover begins after these retries are exhausted.'),
models: tool.schema.array(tool.schema.string()).optional().describe('Pre-verified model IDs from a prior smoke call to restrict which models are used.'),
taskId: tool.schema.string().min(1).max(128).optional().describe('Stable task ID used to share token and cost budgets across build, test, and review calls.')
taskId: tool.schema.string().min(1).max(128).optional().describe('Stable task ID used to share token and cost budgets across smoke, build, test, review, fix, and escalation calls.')
},
async execute(args, context) {
if (process.env.AGENT_LOOP_CHILD === '1') {
Expand Down Expand Up @@ -89,7 +90,7 @@ export default async function AgentLoopPlugin() {
const result = await runAgentLoop({
task,
mode: args.mode || 'build',
maxRetries: args.maxRetries || 0,
maxRetries: args.maxRetries,
cwd: context.directory,
parentSessionId: context.sessionID,
signal: context.abort,
Expand Down
37 changes: 30 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,41 @@

## [Unreleased]

## [0.2.0] - 2026-07-22

### Added

- Added configurable per-task token and cost budgets with live OpenCode usage tracking, model-registry pricing estimates, failover interruption, and structured budget output. Closes #4.
- Added the referenced free-first configuration schema and automated budget/feature contract validation.
- Added bounded in-memory budget-ledger retention with configurable TTL and task-count limits.
- Added configurable token, cost, and workflow-call budgets shared by one stable task ID.
- Added atomic budget persistence across OpenCode and plugin restarts with TTL and task-count retention limits.
- Added same-model transient retries with exponential backoff and jitter before provider failover.
- Added provider adapters for provider identity, aliases, timeout selection, local-model recognition, and error normalization.
- Added a custom provider-adapter example and extension documentation.
- Added a versioned structured event schema, append-only JSONL event stream, recursive redaction, and query utility. Closes #5.
- Added Linux, macOS, Windows, WSL, and Git Bash guidance plus a cross-platform Node CI matrix. Closes #6.
- Added scheduled and manually configurable patched-OpenCode compatibility builds.
- Added portable task-checkpoint paths.
- Added a workflow-call ceiling and reduced parent-orchestrator turn cap.
- Added v0.2 configuration, event, feature-contract, and reliability validation.

### Changed

- Routed every `/feature` worker stage through the budget-enforced `agent_loop` tool.
- Moved provider-specific runtime behavior behind the provider-adapter interface. Closes #7.
- Updated runtime state layout under `.opencode/agent-loop-state/`.
- Updated package and policy schema versions to 0.2.0 and 2.0.0 respectively.
- Updated architecture, configuration, platform, troubleshooting, event, and provider documentation.

### Fixed

- Treat local-unmetered models as zero-cost and account for a final usage event even when the JSON stream has no trailing newline.
- Route `/feature` delegation through the budget-enforced `agent_loop` tool and require one stable task ID across all stages.
- Continue recording usage emitted during worker shutdown after a budget-triggered termination.
- Mark budget totals explicitly as delegated-worker scope so parent-orchestrator usage is not misrepresented.
- Made the public `maxRetries` option perform actual same-model retries for transient failures.
- Corrected timeout selection for local/Ollama model IDs without a slash.
- Removed the hardcoded `/usr/local/bin/opencode` executable path so PATH resolution works across operating systems.
- Treat local-unmetered models as zero-cost.
- Account for final usage events emitted without a trailing newline or during budget-triggered shutdown.
- Stop retries, failover, and escalation immediately after `BUDGET_EXCEEDED`.
- Prevent replacement task IDs from being used to evade an exhausted feature budget.
- Strengthened recursive redaction for structured event and attempt logs.
- Clarified that exact parent-orchestrator token and dollar usage is unavailable, while parent workflow calls and turns are bounded.

## [0.1.1] - 2026-07-22

Expand Down
Loading
Loading