Русский · Install · Harness · OpenCode · Providers · Harness guide
One local server connects DeepSeek Harness or OpenCode to your DeepSeek, Qwen, Kimi and GLM accounts through an OpenAI-compatible API. Your client runs project tools; the bridge sends messages to the selected provider. Website adapters do not require a separate provider API key.
Harness works independently: you do not need OpenCode to use it. Local fixes and live verification are identified separately from published code below.
Security update, October 12, 2026: the local /v1 API now requires a bridge
token. After updating, reconfigure your client using
API access token. Account binding, replay protection,
shell handling and CLI cleanup fixes were verified offline; this update was
not tested against live accounts in Harness, OpenCode or a browser.
This is not an unlimited API. Account quotas and local budgets apply. Automating a website chat can lead to restrictions: our DeepSeek account was restricted repeatedly, with the exact cause undetermined. A passing test does not guarantee the next task. Account safety.
Current status, October 26: tool-call handling now uses one path for every
provider; the temporary TOOLCALL_LEGACY switch and the second path are removed.
Live traces from October 23–24 (DEEPSEEK_TOOL_PROTOCOL=dsml): 27 of 39 model
steps succeeded (69%); almost every failure was damaged tool-call markup. The web
bridge is therefore meant for work with a human in the loop, not for long
autonomous goals; see Operating the web bridge.
Measure reliability with python -m server.trace_report logs. The working bridge
runs with TOOLCALL_FORMAT_REPAIR=1 (one format correction at the cost of an
extra generation); its effect on live accounts is not yet measured.
Authorization and recovery contract, offline: history import and saved-answer
delivery share a contract binding exact field presence, effective thinking,
the tool protocol and schemas. Import also verifies the dispatch prompt hash.
Contract changes stop the operation; legacy permits without verified provenance
are rejected and never automatically rearmed. Existing history keys and consumed
permits are preserved. Recovery keeps omitted parameters omitted; account,
parameter or configuration changes do not authorize saved-answer delivery.
The installed Harness lifecycle is now checked separately with DSML and JSON:
read, one write, verification, acceptance and restarts. This uses a local fixture
provider, not a live generation reliability test. The fixes have been applied
to the running bridge and the bridge restarted; local /healthz responds.
History, counters, pauses and the Harness profile are byte-for-byte unchanged.
The goal was not resumed and no account requests were made.
Status, October 24: request provenance and the independent goal
review handoff were fixed offline (harness-contract-v8). Logging no longer
turns omitted thinking into explicit false; a separate receipt records the
effective dispatch settings. request_goal_review disarms automatic continuation
without completing the goal. The operator accepts the result with
/bridge-goal-review and a one-use /bridge-goal-complete command. The installed
Harness completed a local fake-provider lifecycle covering two rounds, write/read,
review readiness and restarts. This does not verify fresh DeepSeek output quality;
autonomous web generation remains unverified. The fixes are installed in the
working bridge and Harness; both were restarted. A local status command confirmed
harness-contract-v8; rejecting an empty request verified exact parameter
preservation in the running bridge without provider dispatch. The offline suite
ran 997 tests with 68 skips and no failures. Original history, counters and pause
were preserved; the goal was not continued. Harness appended only session-load
lifecycle and local status-command events.
Request provenance and goal review.
Subsequent v8 live check: one explicitly authorized retry from response #36
received #42 with confirmed thinking=true. The complete saved stream
(finish_reason=stop) already contains a malformed parameter closing tag:
</||DSML||DSML| parameter>. The entire batch was rejected, no tools executed,
and no further retry occurred. Parameter preservation worked, but DSML output
reliability remains unresolved. The goal is stopped; old history and pause were
preserved, and counters recorded one generation and one HTTP request. No paid
API was used.
October 23 live check: a fresh Harness session stopped at step four on October 23 because malformed DSML parameters were already present in DeepSeek's complete response. The previously completed goal and green CI do not establish reliable operation on arbitrary goals. The web transport sends textual instructions without an enforced generation schema. Protocol review and verification limits. The installed CLI was checked offline with three successful actions followed by a damaged fourth reply: the entire bad batch is rejected, completed work stays intact, and resubmitting the same history does not generate again. This verifies replay protection; it does not fix the text generator.
October 24 goal-control check: local harness-contract-v7 prevents the model
from closing the whole goal based on a partial report; separate
whole-scope acceptance is required. 50 offline
checks passed. After installation, one web-bridge continuation stopped on a
malformed create_goal.objective tag in complete response #28. After the offline
fix, the exact saved batch was recovered without generation: its original
create_goal and Bash calls succeeded. The successor goal with 241 remaining
rounds was left inactive. 159 parser/contract checks and 8 recovery checks passed
offline. History and usage were preserved. Full autonomy remains unverified;
these changes are local and were
not published to main during this check.
The exact spelling <|DSML| objective" string="true">…</|DSML| parameter>
is now decoded only with DeepSeek wrapper tolerance enabled and that literal
field name declared in an unambiguous object schema. Names, values and types
are not guessed; the entire batch retains the existing schema checks. Truncated
values, unknown fields, partially damaged attributes and quoted examples never
become calls. Updating the parser alone neither redelivers an old reply nor
resumes a goal.
Continuation after #28: two further steps successfully read the plan/backlog
and located related documents. At step three, complete response #34 expressed
read arguments as attributes (file_path="…" offset="192" limit="45") rather
than DSML parameters. The original stream reconstructs the same text without
truncation. Both decoders initially rejected the batch; the goal stopped without
executing the call or regenerating. Prior history and usage were preserved;
only actual usage from the three authorized steps was added. This run does not
establish autonomous execution through completion.
Recovery of #34: attributes are now accepted only as one complete record
inside invoke, with a single explicitly declared scalar type for every field.
Strings stay literal; numbers, booleans and null must parse as JSON and match the
schema type. Unions, $ref, absent types, unknown/duplicate fields, mixed
parameter records and truncated framing are rejected. All schema constraints
and whole-batch validation remain mandatory; XML entities and escape sequences
are not decoded. 167 parser/contract checks and 8 recovery checks passed offline.
The saved read from #34 executed successfully once in Harness without generation;
the goal remains inactive. History, usage and pauses were preserved. The normal
profile was restored and the working bridge restarted with the fix.
Next continuation: one Bash step succeeded, but response #38 at step two
entered a repetition loop: 1,344 description attributes, 141,907 characters,
and no closing invoke/calls tags. The provider ended it as INCOMPLETE
(length), while the local stream capture is complete. No calls from that batch
executed; the goal stopped without retry. Recovery was refused: discarding the
tail and inventing closure is not allowed. This is a generation failure, not
another accepted attribute spelling.
Offline protection against the #38 loop: the prompt now requires one canonical
DSML form, each field exactly once, and immediate invocation/batch closure.
Historical compatible forms remain parser-only. For a reply starting with calls,
the HTTP client stops reading on a repeated parameter declaration within one
invoke, or more than 16 consecutive redundant parameter closers. The entire
batch fails; the connection closes, the private prefix is retained, and existing
replay protection and safety pauses remain in force. No repair request is sent.
This bounds structural repetition, not every possible text loop; closing the
connection does not guarantee that provider-side computation stops.
206 tests passed offline. The exact saved #38 stopped at character 1,186 under
three stream chunk sizes. No closure was invented and no call executed.
The fix was then applied by restarting the working bridge from the updated tree;
the new process returns ok from /healthz. In the same environment, 45 focused
offline checks passed and exact #38 stops at character 1,186. History, profile,
usage and pause records remain unchanged; the goal was not resumed and no live
generation was performed during deployment.
One authorized live attempt after the fix: response #40 from parent #36
finished normally (stop) but contained malformed DSML closing tags. This reply
did not loop; the whole batch was rejected, no tools were delivered, and the goal
remains stopped. No further attempt or new chat was made. The exact stream was
replayed offline; 33 tests passed. The Harness journal, profile and every history
binding table were preserved. The budget recorded one generation and one HTTP
request; counters were not reset, with expired entries pruned by the normal
24-hour window. Successful autonomous operation after the fix is not yet verified.
Offline web-contract fixes, October 24: an authorized Harness catalog
upgrade now sends the new system instruction and complete schemas once, including
in compact mode. Every mode sends the current tool_choice; auto explicitly
replaces the preceding reply's tool-selection policy. Verification covered 238
tests and 35 pinned archived cases through the parser and API paths, with network
access denied. These fixes prevent lost request constraints; they do not repair
the confirmed parameter damage in the latest DeepSeek response. No account or
working history was used for the test run.
Evidence and the remaining limitation.
For API-key users, the separate official Harness route
completed a live native-tool task with all four tests passing.
On October 24, the resumed installer-development goal also completed: its final
continuation ran 12 steps without errors and reached native complete.
DeepSeek updated its website, app and API to V4.1-Flash-1017.
The bridge sends default (deepseek-chat) or expert (deepseek-expert),
not a weight revision: the service updates the selected mode. This does not
prove a particular response version or automatically add a new model ID.
DeepSeek recommends Harness 0.2.1-rc.1 or newer for the new model; compatibility
of an upgraded Harness with the plugins needs separate verification.
The offline review reproduced insufficient spacing between generations and a
missing PoW expiry check after solving/waiting. The fixes add an
independent generation deadline that survives restart, sign-in and budget reset.
Challenge request latency and solving time count against PoW lifetime, which is
checked again before sending. Expiry stops the request without automatically
fetching another challenge or generating again. These fixes are included in main.
The installed Harness upgrade from 0.2.0-rc.2 to 0.2.1-rc.2 was checked with
the profile and history preserved.
Main offline run: 887 tests, 835 passed, 52 skipped. A new installed Harness
CLI fixture separately verified exact file contents and tool-result delivery
without replay. The child-process check blocked by the sandbox passed separately
in an earlier run. Nine real CLI
scenarios were previously checked on Harness 0.2.1-rc.2 with a synthetic provider
and outbound network isolation: read/write, refusal handling, shell pipelines and
no retry on HTTP 503. Additional checks cover one-use history links during saved
reply recovery and permissions when resuming a CLI session. Incorrect DSML prompt
markers and accepting malformed framing as successful text were fixed.
October 23 live check completed: the same long Rentgen goal reached native
complete, and the CLI exited with code 0. Harness saved the retained report
through a real write, checked the workspace, called update_goal and returned
its final answer. All 14,959 report bytes match the retained reply; recovery
did not regenerate it. A new focused Rentgen test selection returned 228 passed,
64 skipped; previously passing checks were not repeated. This completes one
concrete task, not a guarantee of error-free web-model behavior on every task.
A complete DSML arguments input
object is accepted only for an unambiguous direct object schema. It must be the
only typed parameter, contain only advertised fields and pass the whole original
schema. A real field named arguments is never unwrapped; composed/reference
schemas or schemas without required fields do not enable this decoding.
Malformed JSON, missing fields and invalid values still reject the entire batch.
In DSML mode, the exact named closers </DSML| invoke> and </DSML| calls>
are also recognized only at their corresponding outer element boundaries.
This spelling cannot open an element, delimit a parameter or close an unnamed
element; argument contents are never rewritten.
Both API implementations validate the saved reply with identical argument values;
its one-use recovery link was verified on a database copy without account access.
One intermediate refusal is a different case: an unknown extra tag contains malformed
JSON, followed by repeated arguments in a valid tag. An independent fold of the
complete SSE matches the reply. Neither the first encoding nor an invalid call
may be silently discarded. Two new offline tests check exact stream preservation
and atomic refusal by both API implementations without corrective requests.
Final recovery preserved all 469 previous history rows; new links continued the
counter from 16 to 19, and all three Harness sessions retain their old byte
prefixes. Recovery was followed by three normal generations / six HTTP requests,
with no new chat. Counters were not reset; credentials and protective limits
did not change. The verification goal's local round cap was raised from 8 to 10
through the native API while retaining used rounds. Its relay was stopped after
completion. The web transport sends text without
enforced response_format or native tools: fully autonomous operation on arbitrary
tasks remains unproven. Schema validation and replay protection were not weakened.
Previous offline
checks completed two native Harness goal rounds using a fixture provider,
which does not establish live model reliability.
October 23 results and limits.
Thirty seconds is a local setting, not a provider-approved
safe quota. The exact cause of previous temporary restrictions remains unknown:
the original server codes were not retained. An elapsed restriction period does
not clear a local pause or establish the outcome of a pending request.
Evidence and remaining unknowns.
You need Python 3.9+ (3.12 recommended), Git, an account with your selected provider, and either DeepSeek Harness or OpenCode. These commands target macOS/Linux; see Windows and detailed setup.
git clone https://github.com/DmitrL-dev/opencode-deepseek.git
cd opencode-deepseek
python3 -m venv venv
source venv/bin/activate
python -m pip install -r requirements.txt
python -m playwright install chromium
cp .env.example .envWhen updating an existing installation, preserve .env and session/.
All providers start disabled; enable only the ones you intend to use.
This first-run example uses Qwen Flash:
python -m qwen.authComplete sign-in yourself in the opened window and wait for the session to be
saved. Change this setting in .env:
QWEN_ENABLED=1Initial sign-in needs a browser; requests do not need open tabs afterward.
Tokens are short-lived. Optionally set QWEN_RENEW_SESSION=1 to renew before a
new request; renewal runs inside that request's durable attempt and must return
the same account. Conversation links are bound to the Qwen account, so token
rotation keeps Harness goals resumable. Once the sign-in cookie expires, manual
sign-in is required again.
Renewal details.
Using DeepSeek instead? Run python -m deepseek.auth, sign in, and set
DEEPSEEK_ENABLED=1. Replace qwen3.8-omni-flash in the Harness command below
with deepseek-chat or deepseek-expert. DeepSeek has no automatic renewal;
saved sessions have no local age cutoff. Token rejection (40002/40003) stops requests without retry and requires manual sign-in. A new python -m deepseek.auth capture keeps saved Harness histories only for the same verified account; another account is refused (use --replace-account deliberately), and an unverified previous session needs --same-account. Stop if the
normal website shows an account restriction.
For Kimi, follow HTTP session setup; for GLM, use the Safari tab adapter.
python app.pyKeep this terminal running. One process serves all enabled providers; do not start another instance. In a separate terminal:
curl http://127.0.0.1:8000/healthz
curl -H "Authorization: Bearer $(cat "$(python -m server.api_auth path)")" http://127.0.0.1:8000/v1/modelsThese local checks send no model requests. Every /v1 request needs the
bridge API token (see API access token). Confirm your model appears in the
catalog, then connect your client below.
The /v1 API drives your signed-in provider accounts, so it requires
Authorization: Bearer <token> and a local Host. The server creates the
private token file session/api-token (0600) on first use;
python -m server.api_auth path prints its path, never the token. Requests
without the token, from a browser page (cross-site or DNS rebinding) or for
another Host are refused before any provider or rate-limit access.
Migration from the keyless API:
- Update the bridge and restart it.
- Harness: rerun
python -m integrations.harness --install <profile> ...with your previous options. It replaces theBearer unusedheader with the token in the private profile file. Restart Harness. - OpenCode: set
"apiKey": "{file:/absolute/path/session/api-token}"for each local provider (example). Other OpenAI SDK clients pass the token asapi_key. - Only while migrating,
API_AUTH=legacy-loopbackadmits tokenless requests from a loopback peer with a loopback Host and no browser headers. Remove it once clients send the token.
HOST other than loopback is refused unless API_ALLOW_PUBLIC_BIND=1 is set
(token mode only). Then also list the served names in API_ALLOWED_HOSTS and
restrict the port with a firewall. This bind check lives only in python app.py:
launching uvicorn server.api:app --host … directly bypasses it, while Host and
token checks (except the transitional legacy-loopback) still apply to every /v1.
- Install and open official Harness once. During initial setup, you can choose Add API Key → Set up later. Local account bridge requires neither a Harness account sign-in nor an official DeepSeek API key. If Welcome reappears after restarting, repeat this choice: in Harness 0.2.1-rc.2 it lasts for the current launch only and does not change the provider session stored in the bridge.
- In another terminal, enter the bridge directory, activate
source venv/bin/activate, and run this while the server is running:
python -m integrations.harness --install desktop --without-browser --default-model qwen3.8-omni-flash
python -m integrations.harness_shell_safety --profile desktop- Select your project folder, create a new session, and choose Local account bridge → your model in Harness. Restart Harness if the group has not appeared. Existing sessions may retain their previous model.
- Start with a small task, such as “Read this project's README and briefly explain its purpose.” Check the tool calls and final response.
The installer reads the local catalog, backs up the profile and updates
~/.dsh/profiles/desktop/cordis.patch.yml. It disables automatic retries and
model-generated titles. Setup sends no account requests.
The menu keeps one entry per model: a bare ID and its provider/model alias
are combined, preferring the bare ID. If duplicates appear, rerun setup with
your previous options; the profile reloads without changing history or budgets.
| Connection | Harness configuration |
|---|---|
| DeepSeek, Qwen, direct HTTP Kimi | Use the command above with --without-browser and an available --default-model |
GLM or kimi-web through a tab |
Set up the tab; run installation without --without-browser, selecting glm-web or kimi-web |
| Web UI / CLI | For an initialized profile, replace --install desktop with --install web / --install headless |
| Enabled or disabled another provider | Restart the bridge and reinstall the catalog with your intended flags |
--without-browser filters adapters; it does not remove initial sign-in or
turn GLM into a direct HTTP connection. Catalog visibility does not confirm
account availability. Full Harness guide.
With a funded API key, Harness can use its built-in DeepSeek provider directly. This route sends tool schemas and calls through the Messages API; the web bridge's textual DSML parser is not involved.
-
Open Settings → Models → DeepSeek, enter your API key and click Apply. Do not store the key in the repository or a conversation.
-
Close Harness and run from the repository root in an environment with the project dependencies installed:
python3 -m integrations.harness_official --install desktop --dry-run python3 -m integrations.harness_official --install desktop --default-model deepseek-flash
-
Start Harness and create a new chat. Choose DeepSeek → DeepSeek-V4.1-Flash, without the
· websuffix. Existing chats retain their selected model; the installer neither migrates their history nor resumes failed steps.
The installer supports initialized standard desktop, web, and headless
profiles; --home overrides DSH_HOME or ~/.dsh. Without --default-model,
the current selection is preserved. Other providers and profile options are
retained; changed files and backups are written with 0600 permissions.
An unchanged reinstall writes nothing. --dry-run creates no files. The installer
makes no network requests, reads no keys or bridge .env, and leaves history,
pauses, and counters untouched.
Symlinked paths, ambiguous entries, dynamic configuration in edited plugins, and custom bundle compositions are refused. For profiles with replaced plugins, manually merge entries from harness_official.patch.yml through Settings → Open configuration file, preserving your settings. Do not replace the entire profile with the example.
The configuration disables automatic retries, model-generated titles, and session
log uploads. Each response is capped at 32768 tokens, including reasoning.
A long task exhausted the previous 8192-token cap before producing a tool call,
so the cap was increased. Reaching the cap stops the goal without an automatic
retry. This is not a total spending cap: the provider bills API usage, while
/bridge-budget measures only the web bridge. strict schema mode is not enabled.
Verified October 23, 2026: installed Harness 0.2.1-rc.2 completed one live API
task in four steps: two read calls, edit, one test run, then a final answer.
All four tests passed and the test file remained unchanged. Separate offline
fixtures verified exact argument round-tripping, automatic goal continuation
between turns, and stopping after HTTP 503, an incomplete stream, or the output
limit. Separate temporary-profile tests cover installer preservation, backups,
idempotency, rejection without writes, and secret-safe errors. The goal
remains disarmed after failure; explicit resume in a new process retains its
history without repeating the completed file write. These scenarios run the
installed Harness with a temporary profile and a loopback test server, without
an account. This confirms those checks, not error-free operation for every goal.
Verified October 24, 2026: after local fixes, the same installer-development
goal resumed. Its final live turn completed 12 steps and 21 tool calls without
errors, finished documentation and diff review, and marked the original goal
complete. The previously passing 13 installer tests were not repeated; code
had not changed since those tests. Earlier runs required installer and stop-policy
fixes, so this does not claim fully autonomous execution from the first attempt.
Web-bridge history, pause and counters were preserved. Separate offline checks
cover goal completion after fixing an assertion and retaining the mandatory
read guard while precondition recovery is enabled.
The same day, a long Röntgen task completed through the official API:
73 steps, 89 tool calls, and the original goal reached complete.
A confirmed notification-receiver initialization defect was fixed and boundary
checks added; the final suite reported 265 passed and 8 skipped. Three local
stops (no grep matches, Python import path, and a temporary-directory symlink)
were diagnosed and resumed by the operator without asking the user; this was
not an unaided model run. There were no API errors or subagents, and web-bridge
history and counters were preserved. The offline driver's turn-end observer
race was also fixed: a delayed callback no longer finishes a later turn based
on its newer goal state. All 9 installed-Harness fixtures using a local test API
passed, including failure without retry.
After installing the local budget plugin, these commands run without calling a model:
| Command | Result |
|---|---|
/bridge-budget |
Six rolling DeepSeek budgets: generations, new chats and HTTP requests per hour/day, with remaining capacity |
/bridge-budget-reset |
Preview only; no counters changed |
/bridge-budget-reset CONFIRM |
Explicit confirmation in the same session within 60 seconds |
Reset clears local usage only. It does not clear provider pauses, change website quotas, request intervals or conversation history. Active or unresolved requests, and usage changes since the preview, prevent reset. The plugin requires its own private token; installing the model catalog alone does not install these commands.
On macOS, select deepseek-login in the Harness command menu or enter
/deepseek-login. Safari opens; sign in to the same account if needed. Transfer
accepts the RFC 6750 Bearer
alphabet, including +, / and trailing =; whitespace, line breaks, Unicode
and excess length remain rejected. This fixes local refusals for some tokens
after successful sign-in; same-account checks and the safety pause remain enforced. The
session transfers locally without an inspector or copying tokens. Check the
result with /deepseek-login-status. saved_paused means the credentials were
saved while the entire safety pause and pending request remain. Review account
restrictions and the previous outcome first; refreshing credentials does not
authorize replaying a generation.
An explicit 40002/40003 refusal during chat creation or challenge retrieval,
before generation, permits session refresh. Ambiguous network failures and
refusals after generation dispatch still require investigation; no automatic replay.
One-time setup: update the local budget plugin, install
DeepSeek session transfer in the
Userscripts app's Save Location, and allow access to chat.deepseek.com and
127.0.0.1. Restart the updated bridge and Harness after active work finishes.
This flow requires macOS and the bridge on 127.0.0.1. For a PORT other than
8000, run python -m providers.tab_bridge and install the exported copy from
session/browser-bridge/userscripts, and reinstall the budget plugin with
python -m integrations.harness_budget --base-url http://127.0.0.1:<PORT> ... (it
defaults to PORT from the environment or .env, then 8000).
An explicit --base-url takes precedence even when PORT is invalid; only
a loopback HTTP origin without credentials, a path, query or fragment is allowed.
Setup and recovery rules.
History, counters and request intervals are preserved; goals never resume automatically. The first transfer asks you to confirm the same account; later transfers compare its saved identifier. This is manual session recovery, not background renewal. Offline checks passed; this new flow has not yet been verified end to end with a live account.
Set DEEPSEEK_STREAM_DIAGNOSTICS=1 and restart the bridge to save failed SSE
streams privately in session/failed-streams/ (mode 0600). Storage is limited
to four captures and 8 MiB of received bytes each. Responses may contain private
source code; do not publish these files. Successful responses are not saved.
Offline inspection: python -m deepseek.stream_diagnostics session/failed-streams/<id>.json.
Only a complete capture with a recognized completion marker and valid response
identity can produce a private .review.json review candidate. It is not
permission to execute tools: inspection never changes history, counters or pauses,
and never resumes goals. Missing completion markers or interrupted captures are
refused. An old failure without captured bytes cannot be reconstructed this way.
Details.
DeepSeek context_length_exceeded means the provider conversation has exhausted
its context. It is not a local budget or expired session; resetting counters or
signing in again will not fix it. The bridge preserves history and stops, without
creating a new chat. Migrating the goal requires a separate manual decision.
Optional TOOLCALL_LOG=1 enables detailed conversation traces in logs/ (default
0, disabled). Each conversation gets one file named MMDDHHmmss-<provider>.log;
it accumulates agent requests, assembled provider prompts, raw provider replies,
final JSON/SSE responses to the agent, and errors, separated by from agent, to provider,
from provider, and to agent markers. Logs can contain source code, secrets and
tool results; enable only for local diagnosis and do not publish them. Git ignores
the directory. Implementation checks are offline; live-account exchanges were not
verified. Continuations are associated via the persisted conversation ID; if a
provider returns no ID, a later request cannot be linked to this trace.
New mode TOOL_INSTRUCTIONS_BROWSER=medium (the default stays light, unchanged:
tool names only). On chat continuations the reminder also includes each tool's
argument shape (field names, nesting, ? = optional; no descriptions) so the model
does not guess arguments from memory; the first turn still gets the full block, as
in light. Continuation prompts are longer. A reply like {"question": ..., "options": [...]} instead of {"questions": [...]}
is rejected with "Tool arguments do not satisfy the advertised schema". No extra
generation.
Every provider uses one call contract (server/api.py + server/tool_protocol.py):
one complete tool_calls block occupying the whole reply, DeepSeek tolerances
without guessing arguments, and an invalid call always ends as
invalid_tool_response. Summary in
SETUP §5;
longer notes (Russian): docs/toolcall-format-repair.md.
A complete native DeepSeek DSML reply passes the same schema, finish and
tool_choice checks, with no extra generation.
With TOOLCALL_FORMAT_REPAIR=1 (off by default), an erroneous DeepSeek reply gets
one format correction in the same conversation. A second correction is never
requested. Verified offline.
In compact browser mode, an agent's tool result (for example the option you picked)
is sent as Tool result (name id="..."): <result>. IDs retain result binding;
multiple calls to the same tool also receive an exact call mapping. Complete schemas
are normally omitted, but the current tool_choice follows the result even in
none mode. auto explicitly replaces an earlier per-reply selection; task
constraints and schema validation remain enforced. An authorized catalog upgrade
sends complete schemas once.
Verified offline.
For DeepSeek, disabling TOOLCALL_FORMAT_REPAIR prevents an extra format-correction generation;
it does not disable tool-markup validation. Malformed tool blocks stop the step
instead of being returned as successful plain text.
After a normal DeepSeek stop, commentary may precede one explicit terminal
tool_calls block. The bridge locally normalizes flat arguments against the
advertised schema and known closing DSML markers without rewriting values. Empty
arguments are accepted only when the schema permits them. Every call undergoes
JSON Schema validation (types, required fields, nested structures and constraints);
one invalid call rejects the entire batch. Duplicate keys, unknown tools,
ambiguous envelopes, trailing text and incomplete data are rejected. Generic JSON
fences and quotations do not become calls. Prose quotations may precede a separate
top-level tool_calls block with a blank line before its opening fence. Quoted,
nested and ambiguous code blocks are not executed; > markers are never stripped
from code. An explicit tool_calls block is an
action protocol: do not use it for explanatory examples. Browser providers follow the
rules above instead. Remote schemas are never fetched; local $ref references work.
On update, install requirements.txt (the new dependency is jsonschema) and
restart the bridge.
A normally finished DeepSeek response may also contain one whole native DSML
batch (calls → invoke → parameter). The bridge decodes it locally for any
advertised tool and applies the same complete schema and tool_choice checks.
Values marked string="true" remain literal; string="false" requires valid
JSON, with no inferred types. Incomplete tags, duplicate parameters, unknown tools,
trailing text or one invalid call reject the entire batch. Quotations, Markdown
examples are never extracted as actions. DSML after commentary is also inert by
default; the explicitly selected dialect has the narrow exception below. XML entities
are not expanded and external resources are never loaded. No format regeneration
is needed. This fixes the saved Unsupported tool-call markup failure with a
complete DSML invocation; upgrading does not redeliver the failed reply or resume
the goal. Verified offline using the saved response and synthetic cases.
Clients that omit thinking can opt into DEEPSEEK_TOOL_THINKING=1:
DeepThink then applies only to DeepSeek requests with tools. The default is 0;
explicit thinking=false takes precedence. Restart after changing it; the loaded
value appears in authenticated local /admin/budget as tool_thinking_default.
Selecting Expert alone does not enable DeepThink. The setting changes neither
history keys nor retry policy and does not guarantee valid tool output.
For a manually reviewed format failure, checkpoint(..., format_recovery=True)
archives the complete original request privately, preserves instructions and
observed action results, and replaces recent assistant notes with archive
references. Damaged markup is not used as an example for the next reply. This
only prepares context: it sends no request, clears no pause and never resumes a
goal automatically. Configuration and archive checks were performed offline.
DeepSeek can explicitly request DEEPSEEK_TOOL_PROTOCOL=dsml after a bridge
restart. The prompt uses the V4.1 reference vocabulary: <|DSML| calls>, with
one separator on each side of DSML and a space before the tag name
(DeepSeek reference).
The previous prompt incorrectly doubled those separators. Complete retained
packets using that older vocabulary remain readable for compatibility. With
dsml explicitly selected, both complete symmetric marker spellings are lexical
aliases at tag boundaries, including within one batch. Argument bytes are never
replaced; every parameter, invocation and batch must close. Asymmetric or
incomplete markers remain invalid. This asks for one complete native batch instead of a JSON fence;
literal command/file strings need no JSON quote or newline escaping. Complete
schemas, tool_choice, history, counters and replay prevention still apply.
Other providers and the json default stay unchanged. Historical calls retain
exact IDs as inert data rather than a second protocol example. Tool and field
names must fit DSML attributes (1–256 characters, no quotes, <, > or control
characters); strings containing DSML delimiters use a JSON string with
string="false" and escaped angle brackets. Broken mixed JSON/DSML packets fail
as a whole, without dropping a second invocation or demoting the failure to
successful text. Only with dsml explicitly selected may plain commentary precede
one complete packet whose opening tag starts on its own unindented line.
Blockquotes, Markdown fences, HTML, prefixes with backticks or a terminal colon
remain data. Trailing text, a second batch, incomplete tags and schema errors
remain disallowed. The prefix is never executed and parameter values are never
rewritten. With dsml explicitly selected and a normal stop, two envelope variants
are also recognized: one empty repeated calls header immediately after the
first opening, and </|DSML|> as an invocation closer. The latter is accepted
only after every parameter has closed; the root must still end with the named
</|DSML| calls> tag. No values or missing tags are added. A third header,
nested batches, unnamed parameter/root closers, incomplete values or any schema
error reject the entire reply. These tolerances are off under the json default.
Verified offline against a retained stream and general synthetic packets, with
no format regeneration. Damaged DSML markers, including asymmetric and ASCII
separators, are recognized as format failures and retained in private diagnostics,
rather than returned as successful text. They are never repaired into calls.
Quoted, fenced and indented examples remain data. The latest ambiguous batch
was not restored or executed; upgrading never repeats a generation.
A long live goal with this option is not yet verified.
Keep TOOLCALL_FORMAT_REPAIR=0 and
TOOLCALL_MAX_CONTINUATIONS=0 when generation retries are forbidden. The loaded
dialect and those settings are visible locally in the authenticated
/admin/budget response under tool_contract.
A normal stop with a closed fence also permits swapping the final ]} to }]
only when a string-aware scan confirms that arguments are complete and only the
call object and outer array remain open. No values or delimiters are added. The
entire corrected batch must pass JSON Schema validation; damaged strings, nested
arguments and ambiguous endings are rejected.
A normally finished DeepSeek reply with a closed fence may also recover
missing envelope } delimiters between calls or before the array end (up to 32),
only after each complete arguments object, with exactly
name and arguments in that call. Both the prefix and entire batch must parse
strictly after insertion. Argument values are never added or guessed. Every call
is validated against its schema; other syntax defects or damage inside arguments
reject the whole batch without partial execution.
Every reply passes one offline contract in server/tool_protocol.py: envelope
parsing → schema validation for every call → tool-choice and finish policy →
release of the entire batch. Transport and private regression replay share this
validator. Incomplete arguments and fabricated tool results remain errors.
This emulates calls through web-chat text, not a native tool API; passing offline
tests does not guarantee that the model's next response will be valid.
Opt-in private diagnostics retain up to 32 distinct failures in
session/toolcall-failures/ (up to 4 MiB each), without deleting old evidence;
last-toolcall-error.json still updates. Replies may contain source code: the
directory is private and excluded from Git. DeepSeek diagnostics also retain
the exact response ID and incoming request hash, without the request text or
HTTP headers. These are binding-review metadata, never permission to redeliver
tools. With DEEPSEEK_STREAM_DIAGNOSTICS=1 also enabled, the original response
stream for a failed tool reply is kept separately in session/failed-tool-streams/
(up to four files, up to 8 MiB of raw stream each, mode 0600). This does not
consume network-failure evidence slots; duplicates use no additional slots and
old evidence is never deleted. Successful calls do not write streams to disk.
Upgrading cannot recover IDs already lost by an older version, fetch history,
or resume the goal. Run sanitized shared regressions:
python -m unittest tests.test_tool_protocol tests.test_api tests.test_diagnostics.
Replay a private corpus with reviewed expectations and pinned SHA-256 hashes:
python -m server.offline_corpus session/protocol-corpus.json.
This manifest is private, prepared during diagnostic review and not shipped in
the repository; parser output alone must not establish the expected result.
Each case can pin protocol: "json" | "dsml"; without it json applies,
independently of .env. The implementation field accepts only legacy: the
second implementation no longer exists. Pin immutable diagnostic
copies with SHA-256, rather than the overwritten last-toolcall-error.json.
matched_outcomes separately counts accepted batches, plain text and expected
refusals. A passing refusal check proves that a bad batch stops, not that a goal
succeeds. Invalid evidence and unknown policies fail verification.
When that directory is full, earlier evidence is never deleted. Private
diagnostics record stream_capture_status=storage_full when raw bytes could not
be saved. Authenticated local /admin/budget exposes stream_capture_storage
with the file count, cap and state, without paths or content. Enabling capture
does not itself guarantee available storage. Set DEEPSEEK_STREAM_CAPTURE_FILES
explicitly to change capacity (1–32, default 4), without deleting earlier files.
Each capture remains limited to 8 MiB of raw data.
An intentional stop after delivering a recovered answer returns a normal message
with finish_reason=stop, no error and no new provider call. It finishes the
recovery turn, not the user's goal. Existing historical Failed entries are not
rewritten. JSON and SSE behavior is checked offline; automatic goal continuation
in live Harness has not been verified by these tests.
On explicit continuation, the local notice does not replace the upstream reply
ID: the bridge verifies the original binding and all tool results, preserving
the single-use claim. No duplicate binding that could replay actions is created.
The offline audit also fixed Qwen session construction during login and renewal:
expires_at is passed by name instead of occupying the conversation-binding
field. Existing session files are not automatically changed; no live login was
performed for this verification.
Before each reply, the model receives a compact required-field checklist derived
from the schemas. Harness edit requires a successful native read of the same
file_path first. Viewing a file through bash (cat, grep, git diff, etc.)
does not count. The dependent edit must wait for the read result in a separate
reply; an already satisfied read must not be repeated without a reason. Harness's
own guard stays enabled; using another writer to bypass it is forbidden. When
instructed to stop on errors, the model must not automatically read and retry.
The contract was checked offline and in one real Harness continuation: README
read → successful result → a separate edit → normal Completed, with no tool
errors. This does not guarantee full plan compliance: the same turn unnecessarily
repeated an already passed test run despite instructions not to rerun unchanged tests.
The verification reuse contract now keeps successful evidence valid until relevant
code, tests, dependencies, configuration or environment change, a concrete new
concern appears, or the user explicitly requests a fresh run. README wording alone,
a new turn or "final verification" is not a reason to repeat tests. Repeats must
not be hidden in commands with git diff or git status; a justified repeat
states its reason in the command description. Reused results must be identified
as previous evidence, and skipped tests are not passes. This contract is checked
offline and in one live turn: the model reused prior results, ran no tests and
read existing CI results instead. This is guidance, not a semantic blocker for
duplicate test runs. To stop on shell errors, install the local Harness plugin:
python -m integrations.harness_shell_safety --profile desktopRestart Harness after installation. /bridge-shell-status checks the active executor locally, without generation. There is no mandatory runtime gate: if the installed plugin copy in <DSH_HOME>/plugins/opencode-local-bridge/ is deleted or corrupted, Harness only logs the error and keeps running without the protection. Check /bridge-shell-status after every Harness start before resuming a goal. The plugin adds set -e -o pipefail to Bash
commands: a successful tail no longer hides an earlier pipeline failure.
A nonzero foreground result, timeout or tool error stops the current turn using
native Stop, without asking the model to retry or diagnose it. History, completed
actions and counters are preserved. if, !, && and || retain normal Bash
semantics: an explicitly handled nonzero status need not fail the whole command.
When the user authorizes autonomous local test repair, run
bridge_unittest /path/to/venv/bin/python -v tests.test_module as a separate command.
This explicit unittest helper delivers completed assertion failures as
status: tests_failed, preserving the original report and child exit code.
Shell exit 0 means the result was delivered, not that the tests passed.
The model can fix code and rerun affected tests after a change; do not repeat
passing tests without a new reason. Loading errors, test exceptions, interruption
and missing completion receipts still stop the goal. Ordinary python -m unittest,
other shell commands and API retry policy are unchanged. The helper does not
disable networking; offline verification requires a separate sandbox. A fully skipped suite is
reported as tests_skipped, not as a passing run.
Offline checks cover assertions, loading errors, exceptions and early exits.
To allow correction of a confirmed rejection before any write, enable:
python -m integrations.harness_shell_safety --profile desktop --allow-read-repairAfter restarting, /bridge-shell-status must show FS_NOT_OBSERVED read repair enabled.
Only native FsError / FS_NOT_OBSERVED from edit/write returns to the model
for correction. Harness still rejects writes until an actual read succeeds.
The error stays in history; arguments are not filled in and no tools run on the
model's behalf. Other errors and uncertain outcomes still stop the goal.
This is off by default; --stop-on-read-error restores stopping, and ordinary
reinstallation preserves the choice. Checks use the installed Harness offline
with a local fixture server and a separate test profile.
Plain gh pr checks (including --required/--repo) fetches status once and returns JSON: validated failing or pending checks do not stop the goal. With a valid schema and empty stderr, exit 0 means the JSON was retrieved successfully even when checks are pending or fail; their statuses remain in the response. Nonzero exit codes 1/8 are normalized only when they match the check statuses. Network/authentication errors, malformed responses and other pipeline failures still stop the turn. --watch, --web and custom formatting retain native gh behavior. Requires python3 on PATH; /bridge-shell-status must report harness-contract-v5. The pending plus exit 0 case that stopped PR #80 check retrieval was fixed and verified offline using the saved JSON and a shell pipeline, without re-querying CI to test the fix. This does not mean the CI checks themselves passed.
When checking whether a branch contains a fix or locating optional markers/sections, no match is a discovery result even when presence is unknown beforehand. The model contract requires bridge_grep_optional for this case; plain grep remains for mandatory assertions. Use -E 'marker_a|marker_b' for alternatives. Producer errors (git show, etc.) still stop the pipeline. This is a prompt selection rule, not a guarantee of model behavior; shell safety does not override plain grep.
For an optional search, use the bridge_grep_optional shell function: it
normalizes only grep's own no-match status 1. Invalid patterns, unreadable
files (status 2) and upstream failures remain failures. Quiet mode
(-q/--quiet/--silent) is refused with status 2: on a match grep would
exit 0 even if another input failed. Use -c or -n output instead. Early
exit options (-m, -l) after a large producer make the producer exit 141
(SIGPIPE); that is a reported failure, never normalized. The function requires
Bash (both Harness executors run bash -c); another shell stops with status 2. Plain grep is not
overridden; assertions requiring a match still stop. Never append || true to
the entire pipeline. Prefer sed -n '1,40p' to head for bounded output while
draining the stream. Offline checks cover empty/matching searches, invalid
patterns, missing files, producer statuses 1/17, and native Harness
continuation versus stopping.
The plugin does not classify output text or promise to catch background-process
failures. It sets XDG_CACHE_HOME for gh to a private temporary directory
(0700), without changing authentication or sandbox permissions. The old cache
is retained; the new cache remains until OS temporary-file cleanup. Offline tests
in the installed Harness verified that pipeline failure cancels the remaining
command and the next call in its batch, with no second model request; cache
writes succeed.
A subsequent live check successfully read the CI log using gh run view --log-failed.
The next gh api | grep | head pipeline exited with code 1; Harness automatically
showed Stopped and sent no third generation. The unfiltered stream was not retained,
so the tool result does not establish the cause of that status. grep also returns
1 for no matches: handle an expected case explicitly in the command instead of
disabling protection globally. Prior history and usage records were preserved;
only two generations and two HTTP requests were added.
After the optional-search fix, the next live turn completed without new errors:
five tool calls inspected the saved CI log and local source, without downloading
the log again or rerunning tests. The original grep pattern matched the saved log;
this does not establish the cause of the earlier gh api pipeline status. The CI
report identifies a new failure relative to its baseline but lacks the assertion
traceback. Fixing search handling does not mean that the CI failure is fixed.
For the separate PowerShell failure in the test project, the pytest artifact
confirmed a subprocess TimeoutExpired after 10 seconds, not an assertion failure.
With PowerShell 7.6.6 installed, its test fixture now bounds startup separately
(30 s) from projection execution (10 s), without retries or baseline changes.
Focused local checks passed, including an injected startup delay; this does not
establish a green CI rerun or native Windows acceptance.
A later turn exposed a separate protocol limitation: the model returned a
calls fence as text. Harness displayed Completed but executed no tool. A complete top-level calls fence now aliases tool_calls only for normally
finished DeepSeek replies, with full advertised-schema validation. No arguments
are inferred; malformed JSON, multiple blocks, quoted or nested code cannot execute.
Invalid framing no longer becomes a successful text response. Offline checks cover
parser boundaries and real Harness execution with a fixture provider. One-use
recovery of previously delivered text preserves its reply ID, history and usage:
it sends no generation, does not increase the conversation reply count, and stops
locally after the tool result.
Live recovery of this case was verified: one saved bash call executed
successfully, followed by a local stop. No new DeepSeek generation was sent;
the budget-state file was unchanged, prior history links were retained, and
the recovered link kept the same reply ID and conversation reply count.
The model also receives a reminder to wait for a write result before its verifier.
This is guidance, not a guarantee: missing file_path or another required field
still rejects the batch. The bridge never infers a path from content or another
command and does not automatically retry.
The prompt separates past transcript data from the next assistant message and
forbids simulated tool results. Only Harness supplies actual execution results.
A Tool (...): suffix after a call block is never stripped: the entire batch is
rejected without execution. Prompt guidance does not guarantee model compliance.
Verified offline against saved replies without another generation. The fix does not resume a stopped goal, replay an old reply, or reset history or counters.
The web bridge is meant for work with a human in the loop. Autonomous Harness
goals on the web generator are unproven: DeepSeek periodically returns damaged
tool-call markup, and such a step stops with invalid_tool_response without
running tools. The bridge does not guess repairs and does not accept new damaged
markup spellings (the rule is in AGENTS.md).
Recommended mode:
- short goals, with a human checking the result of each stage;
- after a stop, check the state manually and continue with a new message;
TOOLCALL_FORMAT_REPAIR=1allows one format correction in the same conversation at the cost of an extra generation on the account (see ACCOUNT_SAFETY); off by default.
Reliability is measured from live traces. With TOOLCALL_LOG=1,
python -m server.trace_report logs counts step outcomes offline and reports the
share of successful model steps (model_step_success) per day; trace content is
never printed. Bridge-state refusals and invalid client requests are not counted
as model steps. Traces from October 23–24 (DEEPSEEK_TOOL_PROTOCOL=dsml): 27 of
39 model steps succeeded (69%); 11 of 12 failures were damaged markup. The sample
is small and has no json data for that period. At this rate a 10-step goal
finishes without a stop about 2.5% of the time.
bin/ds-goal runs one Harness task headless and resumes it after a format failure:
DS_GOAL_PYTHON=/path/to/venv/bin/python bin/ds-goal --dir ~/work/project "Implement SPEC.md"
bin/ds-goal --continue session/goal-runs/<run> "Fix the failing test and finish"DS_GOAL_PYTHON is the bridge environment's interpreter; it defaults to
venv/bin/python in the repository root. Each run gets its own Harness home in
session/goal-runs/: the bridge route, shell safety and no subagents. The
desktop profile is unchanged; the file holding the bridge token is removed after
the run. The task gets a rule: any failing command stops the task, and Python
unittest suites run through bridge_unittest python3 discover -s tests, so failing
assertions go back to the model for repair; import errors still stop it. After
the run, a timeout (--timeout) or Ctrl-C, every command Harness started is
stopped as well, background ones included.
Only a stop caused by the model's own reply (format, schema, unfinished reply) is
resumed automatically — with a new "continue" message in the same session, never
by replaying a stored reply, at most --max-resumes times (default 2). Each resume
is an extra account request. Shell safety, budget, pause and history stops are not
resumed: review the project and continue with --continue. At the end a summary
prints the status, resume and format-correction counts, model steps and their
success rate; the full summary is the run's summary.json. Corrections and steps
are counted across the whole bridge during the run, so concurrent work in another
client lands in the same numbers.
Verified offline against a fake Harness and on a live account on October 26: a
four-stage task finished after one manual --continue, and its tests pass.
| Message / symptom | Check |
|---|---|
Local request budget exhausted |
Rolling budgets, conversation reply cap or prompt size. A new chat or restart does not clear rolling usage |
MISSING_CREDENTIAL for deepseek-official |
The official API route needs a key. To use the bridge, select Local account bridge |
login_required |
Saved authentication expired; sign in to the selected provider again |
History is unknown / History already consumed |
The former means no local binding was found; the latter means it was found but is already used. Signing in or resetting budgets cannot repair this. After upgrading Harness, use the verified local schema compatibility below; consumed links remain closed |
quota_exceeded, user is muted, security refusal |
Stop and inspect the normal website; do not retry automatically |
| Missing group or model | Check .env, the running server and /v1/models, then reinstall the catalog |
headless has no interactive approval channel: a sandbox_permissions
escalation may stop before the command starts. Harness persists permissions in
the session; changing the launch-time DSH_PERMISSION_MODE does not replace an
already saved mode. Use the selected session's native permission control,
preserve filesystem/network boundaries, and never fabricate a tool result.
This is local Harness state rather than a DeepSeek authentication failure.
For a long goal, use Harness's native Continue goal action. An ordinary
"continue" message does not arm a saved goal. In the checked 0.2.1-rc.2 build,
the one-shot --profile headless runner can exit with code 0 after the first
turn even while another goal round is starting. That exit code does not prove
goal completion: check its durable state and deliverables. The offline verification
driver waited for native complete; it is not installed as an automatic workaround
in users' profiles.
Upgrading Harness from 0.2.0-rc.2 to 0.2.1-rc.2 changed the catalog from 27 to 32 tools and also changed the leading system instruction. Both fields are included in the history key. A verified version pair can be registered locally:
python -m server.conversation_schemas --model deepseek-expert --catalogs /private/path/catalogs.jsonThe private JSON must contain old and current: exact OpenAI tool arrays
(type: function, function: {name, description, parameters}) from saved request
headers. If the leading system instruction changed, also include both exact
strings as old_system and current_system. Never reconstruct them from memory
or publish the file: descriptions and instructions may contain local paths.
The command only reads the local DeepSeek session and scopes the upgrade to its
account and the selected model; it does not sign in or contact the provider.
The bridge verifies the full old history and tool results, then claims the
original single-use link. It creates no extra replay ticket. That request
sends complete current schemas and, if changed, the exact new leading system
instruction. Compact mode and none cannot hide the contract upgrade. The next
reply binds to the new contract; ordinary continuations do not resend prior work.
New replies are
validated against current schemas. Unregistered changes, ambiguous parents,
consumed links and previously attempted migrations remain closed. Upgrading
schemas does not reset conversation limits, rolling budgets or migration journals,
and never resumes a goal automatically. Verified offline, including the saved
large history: the old binding was located while preserving its consumed state.
Successful account-backed continuation after this change has not been tested.
DeepSeek defaults are 12/60 generations, 3/6 new chats and 40/200 HTTP requests
per hour/day, with a shared 20 accepted replies per conversation
(CONVERSATION_MAX_REPLIES) and a 30-second interval. DEEPSEEK_MAX_* budget
ceilings and CONVERSATION_MAX_REPLIES belong to the bridge process environment.
Raising a local ceiling does not raise the website's allowance. PROVIDER_ACCESS_PAUSE=0
disables provider pause and pending blocks, including persisted ones; it does not
bypass budgets, provider quotas or conversation-history safeguards, and can allow
retries after an uncertain outcome.
Configuration and error guide.
Use the same bridge and account setup above. Put the example
opencode.json in your project. For existing configs,
merge settings while preserving your provider, model and small_model.
Set small_model explicitly: title generation can also consume messages.
Do not point title/small_model at a browser model — that opens a second tab
chat (Harness disables session-title-llm in its profile).
cd /path/to/your-project
opencode --agent plan --model local-qwen/qwen3.8-omni-flashFor DeepSeek, select local-deepseek/deepseek-chat or
local-deepseek/deepseek-expert. API URL: http://127.0.0.1:8000/v1.
apiKey must be the bridge API token, e.g. "{file:/absolute/path/session/api-token}"
(API access token). It is not a provider account key.
Agents and tool plugin.
Implementation and historical checks as of October 11, 2026. A passing test on one account does not establish current access or reliable unlimited goals. Provider documentation retains detailed qualification results.
| Provider / model ID | Browser during requests | Status |
|---|---|---|
DeepSeek · deepseek-chat, deepseek-expert |
Not needed after manual sign-in | Browserless HTTP; small Harness tasks passed; repeated account restrictions occurred. No automatic renewal |
Qwen · HTTP via QWEN_MODELS / qwen-web |
HTTP: not needed after sign-in; qwen-web: connected tab |
HTTP Flash/Max and the browser adapter verified; Max hit a daily limit. qwen3.7-plus is catalogued without a separate live check. Session |
Kimi · kimi-instant |
Not needed after capture | Experimental HTTP: reply, renewal and Harness read passed |
Kimi · kimi-k3, kimi-k2.8 |
Not needed after capture | HTTP implemented, not qualified end to end: K3 refused free-user capacity; K2.8 was not attempted afterward. Details |
Kimi · kimi-web |
Connected tab required | Browser adapter verified; large composer pastes (~4KB+, system+tools) become a TXT attachment on the site — confirmed. See browser/README.md and .env.example |
GLM · glm-web |
Connected tab required | Browser adapter verified; browserless access remains incomplete. Blockers |
Mistral · mistral-web |
Connected tab required | Browser adapter verified — see browser/README.md |
Grok · grok-web |
Connected tab required | Browser adapter verified — see browser/README.md |
ChatGPT · chatgpt-web |
Connected tab required | Browser adapter verified — see browser/README.md |
Claude · claude-web |
Connected tab required | Browser adapter verified (ProseMirror + /api/*) — see browser/README.md |
Gemini · gemini-web / Antigravity CLI |
gemini-web: connected tab; CLI: agy |
Browser adapter verified. CLI was rejected by region in our test |
All connections start disabled. Website versions, quotas and availability may change. Chinese providers also have limits; the exact Qwen Max daily allowance and reset time have not been established.
Harness or OpenCode runs tools on your computer. Prompts and tool-read data
are sent to the selected provider. The bridge validates responses and
tool_calls, saves conversation state and sends new tool results to the existing
chat. History keys keep the original main format: updating requires no database
cleanup, sign-in or counter reset. system instructions, <system-reminder> wrappers
and assistant commentary are matched exactly; changed Harness instructions or
schemas need an explicit local compatibility registration.
Unknown or already consumed history stops instead of silently creating a new chat.
Refusal pauses survive restart. Client setup, ordinary sign-in and local budget
reset do not clear them. Safari transfer also preserves the entire pause, pending
request and counters. A fresh token does not prove that account restrictions ended. Inspect pauses with
python -m providers.access status.
Resume rules.
The API binds to 127.0.0.1 by default (local only) and requires the bridge
API token. Non-loopback HOST needs API_ALLOW_PUBLIC_BIND=1,
API_ALLOWED_HOSTS and a firewall; never expose it without them. session/ and .env are excluded from Git.
Do not publish them, personalized Userscripts or budget credentials.
Supported: text and emulated tool calls. Images, audio and video are not uploaded,
even for models named Omni.
Known limits: the browser observer runs in the provider page, so scripts already
running in that origin can forge its response events; the bridge binds a job to
the tab, not to an authenticated answer source. DeepSeek/Qwen streams stop after
300 seconds total or 15 seconds without bytes; such a stop pauses the provider
and is not replayed. A first request (no assistant message yet) has no history
link, so resending it can open a second chat; continuations are one-use.
Set CONVERSATION_CLEAR_USED_ON_STARTUP=1 in the bridge process environment to
delete consumed/closed history links at startup; the default 0 preserves them.
A repeated first request with exactly identical history otherwise has the same
cache key, so the bridge will not open another provider chat for it while the link
is retained. It may reuse an unused link or reject an already-consumed one. Deleting
used links allows a repeat to start another upstream chat,
but a later continuation may bind to the wrong chat and fail. Links with
used=0 are not deleted.
Historical OpenCode 1.18.34 captures of a small fixture-file read. These are not Harness screenshots or proof of current account availability.
| Qwen Flash | Qwen Max |
|---|---|
![]() |
![]() |
GLM/Kimi captures and recording method.
A repeated initial request with exactly identical history has the same cache key.
For an assistant message with tool_calls, the key is bound to the calls themselves
(ID, name, arguments) and the prior history, not to the text around them: a client
may reassemble streamed text differently (without \n\n, or with no text at all).
This does not weaken the binding — call IDs are unique.
While its link is retained, the bridge does not open a new provider chat for that
history: it may reuse a still-unused link or reject a repeat whose link was already
consumed. By default consumed/closed links are retained to avoid binding a repeat to
another provider chat. With CONVERSATION_CLEAR_USED_ON_STARTUP=1, startup deletes
only consumed links (used=1), so a repeated prompt may create a new upstream chat;
a later continuation may then bind to the wrong chat. For a complete manual
cleanup, run python -m server.conversation_admin -clear: it deletes all links,
associated log-path mappings (trace_links), and migration records (rollovers)
from session/deepseek-conversations.sqlite3. It does not delete log files or chats
at the provider. This operation is destructive.
- Harness: setup, sessions, budgets, offline verification
- Full setup, OpenCode, API and autostart
- Kimi HTTP · GLM · Safari Userscripts
- Account safety · API examples
- Standalone DeepSeek terminal chat
CI covers Python 3.9/3.12, PoW WASM, shell/JavaScript and isolated browser fixtures.
Locally: python -m unittest discover -s tests -t . -v. The run is hermetic: .env
and exported provider flags are ignored, so results match CI. Outside the suite the
same mode is BRIDGE_SKIP_DOTENV=1.
Real Harness CLI fixtures with mocked providers.
DOM fallback is disabled: an old visible answer cannot complete a new request. Claude requires a network terminal marker; truncated, partial or malformed answers are not reported as complete. Uncertain dispatched outcomes retain pending state and a safety pause across restart. There is no automatic retry. Verified offline without live accounts. Re-export and update the userscripts to apply the browser changes (controller 0.3.35).
Mistral accepts only a request-bound stream with a terminal trailer.
Harness --without-browser excludes browser models and their aliases.
The browser controller waits for text insertion, preserves user edits and resumes
only explicitly bound conversations. These cases passed 28 isolated browser
fixtures without accounts.
GET /v1/models lists bare IDs and provider/model aliases, for example
qwen3.8-max and qwen/qwen3.8-max, or deepseek-chat and
deepseek/deepseek-chat. Both forms work with the API; the Harness installer
keeps one menu entry per pair, preferring the bare ID. If the catalog advertises
only an alias, that exact ID is retained.
The prefix must match the provider and does not enable disabled models.
DeepSeek aliases share the same history binding and continuation accounting:
changing the spelling neither creates a new chat nor resets replay protection.
Verified offline; this does not verify model access for a live account.
Before contacting any provider, the bridge validates tool-call history: every
call needs exactly one result with the same ID. Missing, duplicate and unrelated
results are rejected locally. Parallel batch results may arrive out of order.
This also applies to explicit conversation_id requests; arguments are not
invented and completed actions are not replayed.
Harness shell safety disarms automatic goal continuation before cancelling
a turn on a tool error. The durable goal phase and revision remain unchanged;
resuming requires a separate user action. Install with
python -m integrations.harness_shell_safety --profile desktop, then restart
Harness. /bridge-shell-status checks shell settings without sending a request;
the current version reports harness-contract-v5 and goal disarm before native stop.
Model reconfiguration preserves the safety plugin.
Verified offline against Harness 0.2.0-rc.2 contract fixtures: SSE, exact call IDs, existing-conversation continuation and duplicate-step rejection without counter changes. This is not a live account check or a guarantee that arbitrary goals will complete.
Selecting DeepSeek Expert · web changes the route; it does not create a web
conversation for existing API history. History is unknown after this switch
requires an explicit import, not a budget reset or another goal resume.
The local command python -m server.history_import --request REQUEST.json --archive-root PRIVATE_ARCHIVE_DIR --source-provider deepseek-official authorizes
one exact request for 10 minutes. First retain and review the client's actual
request while it is stopped. Resuming a goal may change that request; a ticket
for the earlier request will then refuse it. Use a private, git-ignored archive
directory that the current Harness workspace's read tool can access.
Authorization is offline: it archives the complete request and every observed
result. Only a later submission of that same request creates a web conversation
and binds its response to the original Harness history. Existing links, rolling
budgets and pauses survive. The ticket binds account, model, history, schemas and
parameters; it is consumed before dispatch and cannot rearm after failure or
restart. Format repair and truncated-output continuation are disabled. An exact
repetition of an imported call rejects the entire new batch. The exception is
get_goal, which reads current goal state after a resume. Goal mutations and
all other calls retain replay protection; rejected replies are saved privately
when diagnostics are enabled.
Verified offline, including retained real history: 181 messages, all 91 results,
a 72.6 KB prompt. One live probe on October 24 created one web conversation and
received one reply; no tools executed because the guard incorrectly rejected
get_goal. The cause was fixed offline: 145 tests and validation of the saved
reply passed. Response #2 was then recovered after its prompt and reply exactly
matched provider history. Harness successfully executed bash and get_goal
once; the next model step was stopped locally. Locating the binding required two
read requests, with no new generations or paid API calls. History and global
counters were preserved, and the new continuation link was checked offline.
This verifies one recovered step, not reliable long-running autonomous operation.
Subsequent live probes exposed terminal duplicate DSML parameter closing tags in
responses #4 and #6. Complete raw streams were retained; each failure stopped Harness
before executing tools. History and accumulated usage were preserved.
A structural decoder tolerance now accepts a terminal run of repeated closing
tags for the last fully parsed parameter, before a named invoke closing tag.
Both already supported markers, |DSML| and ||DSML||, may be mixed at this
boundary. No other spellings are introduced.
Values are never repaired; schemas and the entire batch are validated before calls
are returned. Repeats elsewhere, missing tags and unfinished replies are rejected.
The run is not limited to a single duplicate: its length cannot change argument
values. Offline verification: 135 tests and both exact saved replies. Response #4
was recovered without generation: Harness successfully executed bash and read
once each. Response #6 was then recovered without generation: one bash call
completed once; all 478 previous links and usage counters were preserved.
Recovery passed 8 offline checks; its new link retained three accepted replies.
The single subsequent request returned #8 with mixed repeated closing tags,
</||DSML|| parameter> and </|DSML| parameter>. The former identical-marker
requirement rejected the whole batch before execution. This inconsistency is fixed:
the tolerance uses the same two markers as ordinary parameter boundaries.
Offline verification: 137 tests, the exact complete stream for #8, unchanged
read and get_goal arguments, schema validation and replay protection.
Response #8 was recovered without generation: read and get_goal completed
once, preserving all 479 prior links. The next two web replies parsed successfully:
bash completed, then Harness's local “file has not been read” guard stopped
an edit batch after an application restart. Neither file was written; the second
edit was aborted before dispatch, with no generation retry. The goal is inactive.
Reliable long-running autonomous operation remains unverified.
With the updated shell plugin (harness-contract-v6), Harness appends runtime
context with a fresh identifier after restart. It automatically reminds the model
to perform a native read before edit or replacing an existing file, and wait
for the successful result in a separate step. Historical reads and bash/cat
do not qualify. This is a reminder, not a fabricated read observation: ignoring
it still triggers the native refusal. File version checks, schemas and replay
protection remain intact. Harness appends a normal context event without rewriting
old messages, the system prompt, or counters. Offline checks on Harness 0.2.1-rc.3
cover restart → read → edit, refusal without a fresh read, and refusal when the
file changes after reading. Install with
python -m integrations.harness_shell_safety --profile desktop, then restart Harness;
/bridge-shell-status reports runtime read reminder active.
Live web-bridge verification completed three sequential steps: two successful
native reads, then separate successful edits to REPORT.ru.md and PROGRESS.md.
There were no errors or repeated earlier commands/tests. The bounded probe
stopped the goal normally after three steps; this does not establish completion
of the whole project. No paid API was used. The offline integration, history
binding and protection suite passed 48 tests.
The next continuation, without the probe step cap, reached a normal final reply:
four web steps, bash, get_goal, and update_goal, with no errors or retries.
Harness recorded turn/end: completed and goal completion; all 485 prior history
links and 62 prior usage events were preserved. The verified result covers only
OWNER-NOTIFY-01: the final reply explicitly excludes a full release and all
product plans. A goal marked complete does not by itself establish fulfillment
of its broader wording or long-running autonomous reliability.
Details and limits: Harness guide.
The harness-contract-v7 shell plugin rejects model calls to
update_goal(action="complete"). A successful tool, completed plan item or partial
report does not establish completion of the entire objective. Rejection occurs
before the native goal changes; the existing safety policy stops the turn
without another request. The model should continue remaining authorized work
without repeating completed actions.
Final acceptance is separate: stop the turn, run /bridge-goal-review, compare
the entire objective and every criterion with actual evidence, then use the
shown /bridge-goal-complete <token>. The single-use review expires after five
minutes and is bound to this session; new work, goal changes or restart invalidate
it. Review disarms automatic continuation. Completion makes no provider request
and does not reset history or counters.
This is a conservative acceptance boundary, not automated semantic verification.
The model cannot yet autonomously certify completion of the whole project.
50 offline checks passed, including installed Harness 0.2.1-rc.3 with a mocked
provider: completing one component could not close the broad goal, and no next
request was issued. Install with
python -m integrations.harness_shell_safety --profile desktop, restart Harness,
and check /bridge-shell-status for whole-goal completion review required.
DEEPSEEK_CONTEXT_ROLLOVER=1 enables a new DeepSeek conversation within the
same Harness goal after restarting the bridge. Only an explicit
context_length_exceeded refusal with an empty, cleared answer permits one
migration. Full history is stored privately in session/context-checkpoints/;
the model receives exact instructions, a completed-call ledger and result
excerpts. Missing details can be read from the archive. This is not a semantic
summary. Harness history, usage and accepted-reply counts are preserved.
Any new failure stops without retry; existing pauses are not cleared automatically.
Off by default. Verified offline without account requests.
Conditions and limits.
MIT · unofficial project. Based on Tsuev/opencode-deepseek and sums001/Deepseek-API. Use accounts under each service's terms. License.
Browser adapters (userscripts, tab protocol, ChatGPT / Claude / Gemini / Qwen / Kimi / GLM / Mistral / Grok providers and related checks) are reworked in the SimolSem/opencode-deepseek fork and contributed here via pull requests.
Old upstream branches do not imply missing features: feat/browser-providers
was reconciled with main, with no missing functional patches found.
Commit mapping and documentation differences.
The offline budget installer test checks the real model-catalog import in an
isolated copy and reports the original child-process failure.
With DEEPSEEK_CONTEXT_ROLLOVER=1, rollover also handles the local
CONVERSATION_MAX_REPLIES cap (200 replies in the working configuration). Before
sending the next step, the bridge archives the full history privately and hands
off instructions, completed-action results and recent notes to a new conversation.
It does not send another request to the capped conversation. Only the new
conversation starts at one accepted reply; global budgets, old history and the
Harness goal binding are preserved. Completed actions are observations, not
commands to execute again. Incomplete/oversized context, archive failure or a new
refusal stops migration without automatic regeneration. Disabled mode retains the
manual stop. Verified offline without account requests; migration happens on the
next Harness request, not on a timer.
Large histories no longer hit a separate 32 KiB instruction cap. The handoff
budget includes tool schemas within the unchanged 131072-byte prompt ceiling.
Every instruction retains its role, order and exact text; only identical
instruction objects share dictionary storage with an ordered sequence of indexes.
When result excerpts do not fit, the packet indexes every completed call/result
and the latest reports. Full history and arguments remain in a private, pretty
JSON archive (0600), without truncation. This mode requires an available read
tool: the agent must read archived context before choosing further work, instead
of rerunning commands to recover their results. Inaccessible archives or exact
instructions plus indexes that still exceed the budget stop migration. This does
not promise unlimited model context. Offline verification used a saved history of
488 messages, 67 instructions and 218 results; the full API path made one mocked
request to a new conversation. No account request was sent for this verification.
The bridge distinguishes invalid tokens (40002/40003), temporary sending
restrictions (account_muted), bans (account_banned), IP restrictions
(ip_restricted), gateway verification (gateway_challenge) and rejected PoW
(pow_rejected). Code 5 denotes muted in the completion context; other
operations have separate numeric namespaces. None of these errors triggers replay.
Minimal private metadata is saved automatically under session/refusal-diagnostics/:
time, phase, HTTP status, original numeric API/biz codes, gateway category and
local attempt ID when available. Tokens, cookies, headers, response text and
prompts are excluded. The directory is private, files use 0600, and the limit
is 64 files; existing evidence is not deleted. Full or unwritable storage emits
a warning while the safety stop remains in effect. Do not publish this directory.
Classification and pause retention were verified offline with synthetic responses. This does not verify account availability or guarantee freedom from restrictions.

