Commit Graph
7 Commits
Author SHA1 Message Date
darmanandClaude Opus 4.8 230e828b81 harness-providers + app: dedicated opencode (Zen) provider
OpenAiProvider gains a custom id and a chat-only mode; `OpenAiProvider::opencode`
builds an OpenCode Zen provider (id `opencode`, defaults to Zen's gateway, always
chat-completions so even gpt-*/o* model ids route correctly). Registered from a
`providers.opencode` config block / OPENCODE_API_KEY env, so it coexists with a
real `openai` provider. Models are referenced as `opencode/<model>`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 06:59:46 +02:00
darmanandClaude Opus 4.8 fa417a347d harness-providers: Copilot device-flow, token exchange, provider (M3)
Offline-verifiable building blocks for GitHub Copilot; network paths are the
flagged live-verification risk and are covered by pure unit tests only.

- copilot/device_flow.rs: OAuth device flow (RFC 8628). No hardcoded client id —
  ai-harness must register its own GitHub OAuth app and pass it in. Pure
  parse_poll_response (pending/slow_down/success/failed) + slow_down interval bump;
  request_device_code/poll_once/poll_for_token (injected sleep).
- copilot/token.rs: copilot_internal/v2/token exchange with cache + single-flight
  refresh (tokio::Mutex) and direct-Bearer fallback on 401/403/404; needs_refresh
  honors a 120s skew and expires_at==0 = never.
- copilot/provider.rs: CopilotProvider over api.githubcopilot.com, routing each
  model to chat/responses/anthropic codec by its /models supported_endpoints
  (parse_models + codec_for_endpoints); headers (X-GitHub-Api-Version, Openai-Intent,
  x-initiator, Copilot-Vision-Request, anthropic-beta for anthropic models).
- 11 unit tests (device-flow parsing, token refresh math, codec routing, /models
  parsing).

Deferred (needs live API + TUI work): EngineHandle::login device-flow modal,
startup /models fetch, auth.json-backed registry wiring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 23:55:38 +02:00
darmanandClaude Opus 4.8 9301d387fa harness-providers: models.dev catalog + cost display wiring (M3)
- modelsdev.rs: ModelCatalog parses models.dev api.json → ModelInfo keyed by
  (provider, model); 24h file cache at ~/.cache/ai-harness/models.json with a
  baked assets/models-snapshot.json fallback so cost/limits work offline.
  load_cached_or_baked (no network) + refresh/refresh_default_cache (background).
- App: loads the catalog at init (cached-or-baked, never blocks), warms the cache
  in a background task, and passes the session model's pricing into RunConfig.cost
  so session cost accrues for real.
- TUI: AppState tracks session_cost/session_tokens from Session events; status bar
  shows "<tokens> · $<cost>". Snapshots updated.
- 7 modelsdev tests (parse, defaults, unknown-model, baked snapshot, cache TTL,
  cached-vs-baked load).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 23:51:23 +02:00
darmanandClaude Opus 4.8 39d62348d8 harness-providers: auth.json credential storage (M3)
- auth.rs: AuthRecord (OAuth {access, refresh, expires} | Api {key}) with
  is_expired(now, skew) honoring expires==0 = never; AuthStorage keyed by
  provider id over ~/.local/share/ai-harness/auth.json.
- Read-modify-write on every op so concurrent refresh/login don't clobber;
  writes go through a temp-file rename set to 0600 (unix) to avoid truncated
  auth files.
- 6 tempdir tests: missing→empty, set/get/overwrite, multi-provider coexist,
  scoped remove (+ no-op on absent), expiry skew/never, 0600 perms.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 23:37:34 +02:00
darmanandClaude Opus 4.8 e2706a1560 harness-providers: OpenAI chat + responses codecs, provider, registry wiring (M3)
- codec/openai_chat.rs: /chat/completions request builder (flat messages,
  tool_calls, system → leading system message, reasoning_effort) + SSE decoder
  (content/tool_calls-by-index/reasoning_content accumulation, [DONE] sentinel,
  include_usage). Also the fallback codec for OpenAI-compatible endpoints.
- codec/openai_responses.rs: /responses request builder (input items, tool calls
  and results as top-level function_call/function_call_output, instructions,
  reasoning {effort, summary}) + SSE decoder (output_item add/done, output_text
  and reasoning_summary deltas, function_call_arguments, response.completed usage
  with separate reasoning_tokens).
- openai.rs: OpenAiProvider routing gpt-*/o-* → responses, else chat; Bearer auth;
  error classification incl. 400 context_length_exceeded → ContextOverflow.
- Registered in harness-app from config.providers["openai"] (api_key + optional
  base_url), sourced from OPENAI_API_KEY by the existing config loader.
- 21 codec/provider unit tests (decode fixtures + request-builder assertions).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 23:35:49 +02:00
Erik Simon bbac60d744 harness-providers: Anthropic codec + provider
/v1/messages request builder (cache_control breakpoints on the first 2
system blocks + last 2 messages, tool schema -> input_schema, extended
thinking budget) and an SSE decoder built on eventsource-stream, mapping
content_block_start/delta/stop and message_delta into our normalized
LlmEvent stream (text, thinking+signature, streamed tool-call JSON
accumulated and parsed at content_block_stop, usage merged from
message_start + message_delta). AnthropicProvider wires this to reqwest
with x-api-key/anthropic-version/anthropic-beta headers and classifies
HTTP errors into RateLimited/Auth/Overloaded/Http. ProviderRegistry
resolves "provider/model" strings.

Also tightened processor::process_step's cancellation: the event loop now
selects the stream poll against ctx.cancel instead of only checking at the
top of the loop, so a blocked provider stream is actually interrupted by
abort (matches docs/02-engine.md's cancellation semantics).

127 tests passing, clippy clean.
2026-07-08 17:25:35 +02:00
Erik Simon 8b16348b4c M0: scaffold Cargo workspace, core types, event bus, permission engine, storage actor
Seven-crate workspace per docs/01-architecture.md. harness-core gets the
domain types (Session/Message/Part/ToolState), a broadcast EventBus, the
last-match-wins wildcard permission evaluator, and a SQLite storage actor
(dedicated thread + mpsc, JSON-blob rows) with roundtrip tests. All other
crates are compiling stubs. CI runs fmt/clippy -D warnings/test.
2026-07-08 16:28:42 +02:00