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
Key DATA
| DATA | Default | Description |
|---|---|---|
cModel | "mimo-v2.6-flash-free" | Zen free model |
cUrl | Zen chat completions | Endpoint |
nTemperature | 0.7 | 0.0–2.0 |
aFallbackModels | other free models | Used 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) |
nTimeout | 90000 | Per-request timeout, ms (SetTimeout()) |
lHistory | .T. | Send() uses and updates the aMessages conversation |
Methods
| Method | Description |
|---|---|
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
| Sample | Description |
|---|---|
samples/ai/opencode1.prg | Three agents, each with its own TOpenCode, SendAsync + timer. Build hb32 mt. |
samples/ai/opencode2.prg | TWebView2 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
- Best effort: the free tier of OpenCode Zen only answers requests that look like they come from the OpenCode client, so TOpenCode sends the headers and tool list that tier expects. Zen may change this or the free model list at any time; if a model stops answering, call
SetModel()with another free model. - No API key. OAuth /
auth.jsonon TOpenCode is unused (Zen rejectssk-keys). - Async requires
hbvmmt— passmttobuild_new.bat. - 32-bit samples need the x86
libcurl.dllbeside the exe (samples\ai). - Do not call the Zen API from inside
TWebView2:bOnBind; queue the work and run it from a timer (see opencode2).