TOpenCode (OpenCode Zen)

Source: source/classes/topencode.prg

TOpenCode talks to the OpenCode Zen chat API (https://opencode.ai/zen/v1/chat/completions) with Authorization: Bearer public. No API key. Free models (mimo-v2.6-flash-free, space-bunny-free, big-pickle, beam-free, glm-5.3-flash-free, deepseek-v4.1-flash-free; checked 2026-10-17). Uses hb_curl. Async methods need the Harbour MT VM (build_new.bat <sample> hb32 mt).

Transport: hbcurl over a static libcurl (Schannel) — no libcurl.dll to ship. Curl sources and build scripts live in 3rdparty/ (outputs: lib/libcurl_static*.{lib,a} for bcc32/bcc64/msvc32/ msvc64/gcc).

Architecture

flowchart LR A[TOpenCode] --> B[hb_curl] B --> C["opencode.ai/zen/v1/chat/completions"] A -->|SendAsync| D[hb_threadStart] D --> E[oc_*.json]

Key DATA

DATADefaultDescription
cModel"mimo-v2.6-flash-free"Zen free model
cUrlZen chat completionsEndpoint
nTemperature0.70.0–2.0
aFallbackModelsother free modelsUsed on rate limit
lVerifySSL.T.Verify the server certificate. Leave it on; set .F. only to diagnose a certificate problem.
lRunning.F.Async in flight
cBaseUrl""opencode serve URL (local server mode); empty = Zen
cProviderID""Server mode: provider for cModel (empty = server default)
nTimeout90000Per-request timeout, ms (SetTimeout())
lHistory.T.Send() uses and updates the aMessages conversation

Methods

MethodDescription
New( cModel )Own curl handle per instance (safe to run several in parallel).
Send( cPrompt )Sync. Retries with cFallbackModel() on rate limit.
SendWithSystem( cSys, cUser )Sync with a system prompt (agent role).
SendAsync( cPrompt )Returns at once. Poll IsRunning().
SendWithSystemAsync( cSys, cUser )Async + system prompt.
IsRunning().T. until the worker finishes. If the worker ends without a result, it returns .F. and GetValue() reports an error instead of waiting forever.
GetValue()Assistant text from JSON; UTF-8 → ACP.
Wait() / End()Join the thread; cleanup curl.
SetModel() / SetTemperature()Change model / temperature.
SetServer( cBaseUrl [, cProviderID [, cModel ]] )Use a local opencode serve server instead of Zen (empty URL = back to Zen).
SessionStart() / SessionEnd()Manage the server session by hand (automatic on Send() / End()).
SetTimeout( nMs )Per-request timeout in milliseconds.
ClearMessages()Forget the conversation; in server mode the next Send() starts a fresh session.

Async writes oc_<ms>_<seq>.json via a .part file then rename, so three SendAsync() calls in the same tick do not collide and IsRunning() never reads a half-written body.

Local server mode (opencode serve)

With SetServer() the class talks to a local opencode serve instance (default http://127.0.0.1:4096) instead of OpenCode Zen. The class keeps a persistent session (POST /session), so the server remembers the whole conversation, and any provider/model configured in opencode can be used (set cProviderID and cModel; empty = server default). If the session is lost (HTTP 404) it is recreated once automatically, and End() deletes it (DELETE /session/{id}). Basic auth is supported through cServerUser / cServerPass (OPENCODE_SERVER_PASSWORD). In this mode the SendAsync() methods run synchronously. This backend follows the approach of Charly 9000's TOpenCode class (MIT license).

o := TOpenCode():New()
o:SetServer( "http://127.0.0.1:4096" )
o:Send( "Hello" )
o:Send( "What did I just say?" )   // the server remembers it
o:End()

Conversation history

By default (lHistory := .T.) every successful Send()/SendWithSystem() appends the turn to aMessages and the next request carries the whole conversation, so the model has memory. Call ClearMessages() to start a new conversation, or set lHistory := .F. for stateless requests.

Example

#include "FiveWin.ch"

function Main()
   local o := TOpenCode():New( "mimo-v2.5-free" )
   o:Send( "Say hello in one sentence." )
   MsgInfo( o:GetValue() )
   o:End()
return nil

Samples

SampleDescription
samples/ai/opencode1.prgThree agents, each with its own TOpenCode, SendAsync + timer. Build hb32 mt.
samples/ai/opencode2.prgTWebView2 UI (WhatsApp layout): agents left, chat right, add/edit roles. Persists opencode2.json. Needs opencode2.html next to the exe. Build hb32 mt.
cd samples\ai
build_new.bat opencode1 hb32 mt
build_new.bat opencode2 hb32 mt
:: run from this folder (libcurl.dll)

Notes

See Also