subharness 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -9
- package/dist/adapters/approval-context.d.ts +2 -0
- package/dist/adapters/approval-context.js +9 -0
- package/dist/adapters/approval-context.js.map +1 -0
- package/dist/adapters/approval-lifetime.d.ts +8 -0
- package/dist/adapters/approval-lifetime.js +50 -0
- package/dist/adapters/approval-lifetime.js.map +1 -0
- package/dist/adapters/claude-approvals.d.ts +4 -0
- package/dist/adapters/claude-approvals.js +32 -0
- package/dist/adapters/claude-approvals.js.map +1 -0
- package/dist/adapters/claude-permissions.js +1 -1
- package/dist/adapters/claude-permissions.js.map +1 -1
- package/dist/adapters/claude-process.d.ts +3 -0
- package/dist/adapters/claude-process.js +11 -1
- package/dist/adapters/claude-process.js.map +1 -1
- package/dist/adapters/claude-result.d.ts +2 -0
- package/dist/adapters/claude-result.js +22 -0
- package/dist/adapters/claude-result.js.map +1 -0
- package/dist/adapters/claude.js +60 -12
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/codex-approvals.d.ts +6 -0
- package/dist/adapters/codex-approvals.js +111 -0
- package/dist/adapters/codex-approvals.js.map +1 -0
- package/dist/adapters/codex.js +36 -3
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/copilot-permissions.d.ts +26 -0
- package/dist/adapters/copilot-permissions.js +121 -0
- package/dist/adapters/copilot-permissions.js.map +1 -0
- package/dist/adapters/copilot-tools.d.ts +12 -0
- package/dist/adapters/copilot-tools.js +61 -0
- package/dist/adapters/copilot-tools.js.map +1 -0
- package/dist/adapters/copilot.d.ts +108 -0
- package/dist/adapters/copilot.js +819 -0
- package/dist/adapters/copilot.js.map +1 -0
- package/dist/adapters/cursor-cli-approvals.d.ts +2 -0
- package/dist/adapters/cursor-cli-approvals.js +83 -0
- package/dist/adapters/cursor-cli-approvals.js.map +1 -0
- package/dist/adapters/cursor-cli-model.d.ts +4 -0
- package/dist/adapters/cursor-cli-model.js +64 -0
- package/dist/adapters/cursor-cli-model.js.map +1 -0
- package/dist/adapters/cursor-cli-session.d.ts +32 -0
- package/dist/adapters/cursor-cli-session.js +316 -0
- package/dist/adapters/cursor-cli-session.js.map +1 -0
- package/dist/adapters/cursor-cli-tools.d.ts +20 -0
- package/dist/adapters/cursor-cli-tools.js +181 -0
- package/dist/adapters/cursor-cli-tools.js.map +1 -0
- package/dist/adapters/cursor-cli.d.ts +2 -0
- package/dist/adapters/cursor-cli.js +306 -0
- package/dist/adapters/cursor-cli.js.map +1 -0
- package/dist/adapters/cursor-model.d.ts +7 -0
- package/dist/adapters/cursor-model.js +67 -0
- package/dist/adapters/cursor-model.js.map +1 -0
- package/dist/adapters/cursor-rpc.d.ts +42 -0
- package/dist/adapters/cursor-rpc.js +271 -0
- package/dist/adapters/cursor-rpc.js.map +1 -0
- package/dist/adapters/cursor-session.d.ts +4 -0
- package/dist/adapters/cursor-session.js +327 -0
- package/dist/adapters/cursor-session.js.map +1 -0
- package/dist/adapters/cursor-startup.d.ts +16 -0
- package/dist/adapters/cursor-startup.js +74 -0
- package/dist/adapters/cursor-startup.js.map +1 -0
- package/dist/adapters/cursor-tools.d.ts +11 -0
- package/dist/adapters/cursor-tools.js +49 -0
- package/dist/adapters/cursor-tools.js.map +1 -0
- package/dist/adapters/cursor.d.ts +6 -0
- package/dist/adapters/cursor.js +17 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/fx-approvals.d.ts +2 -0
- package/dist/adapters/fx-approvals.js +14 -0
- package/dist/adapters/fx-approvals.js.map +1 -0
- package/dist/adapters/fx-auth.d.ts +12 -2
- package/dist/adapters/fx-auth.js +51 -61
- package/dist/adapters/fx-auth.js.map +1 -1
- package/dist/adapters/fx-profile.d.ts +8 -0
- package/dist/adapters/fx-profile.js +101 -0
- package/dist/adapters/fx-profile.js.map +1 -0
- package/dist/adapters/fx-rpc.d.ts +2 -0
- package/dist/adapters/fx-rpc.js +30 -4
- package/dist/adapters/fx-rpc.js.map +1 -1
- package/dist/adapters/fx-status.d.ts +2 -0
- package/dist/adapters/fx-status.js +96 -0
- package/dist/adapters/fx-status.js.map +1 -0
- package/dist/adapters/fx.js +77 -30
- package/dist/adapters/fx.js.map +1 -1
- package/dist/adapters/opencode-access.d.ts +13 -0
- package/dist/adapters/opencode-access.js +76 -0
- package/dist/adapters/opencode-access.js.map +1 -0
- package/dist/adapters/opencode-config.d.ts +11 -0
- package/dist/adapters/opencode-config.js +238 -0
- package/dist/adapters/opencode-config.js.map +1 -0
- package/dist/adapters/opencode-http.d.ts +28 -0
- package/dist/adapters/opencode-http.js +297 -0
- package/dist/adapters/opencode-http.js.map +1 -0
- package/dist/adapters/opencode-tools.d.ts +19 -0
- package/dist/adapters/opencode-tools.js +127 -0
- package/dist/adapters/opencode-tools.js.map +1 -0
- package/dist/adapters/opencode.d.ts +2 -0
- package/dist/adapters/opencode.js +569 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/adapters/rpc.d.ts +3 -0
- package/dist/adapters/rpc.js +45 -4
- package/dist/adapters/rpc.js.map +1 -1
- package/dist/adapters/types.d.ts +7 -0
- package/dist/adapters/types.js.map +1 -1
- package/dist/approvals/schema.d.ts +4 -0
- package/dist/approvals/schema.js +70 -0
- package/dist/approvals/schema.js.map +1 -0
- package/dist/approvals/types.d.ts +47 -0
- package/dist/approvals/types.js +2 -0
- package/dist/approvals/types.js.map +1 -0
- package/dist/cli/args.d.ts +3 -2
- package/dist/cli/args.js +24 -3
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/dashboard-client.d.ts +5 -1
- package/dist/cli/dashboard-client.js +162 -15
- package/dist/cli/dashboard-client.js.map +1 -1
- package/dist/cli/dashboard-controller.d.ts +7 -0
- package/dist/cli/dashboard-controller.js +27 -0
- package/dist/cli/dashboard-controller.js.map +1 -0
- package/dist/cli/dashboard-detail-view.d.ts +16 -0
- package/dist/cli/dashboard-detail-view.js +162 -0
- package/dist/cli/dashboard-detail-view.js.map +1 -0
- package/dist/cli/dashboard-history-view.d.ts +29 -0
- package/dist/cli/dashboard-history-view.js +211 -0
- package/dist/cli/dashboard-history-view.js.map +1 -0
- package/dist/cli/dashboard-input.d.ts +14 -0
- package/dist/cli/dashboard-input.js +128 -0
- package/dist/cli/dashboard-input.js.map +1 -0
- package/dist/cli/dashboard-layout.d.ts +12 -1
- package/dist/cli/dashboard-layout.js +32 -30
- package/dist/cli/dashboard-layout.js.map +1 -1
- package/dist/cli/dashboard-renderer.d.ts +30 -2
- package/dist/cli/dashboard-renderer.js +125 -7
- package/dist/cli/dashboard-renderer.js.map +1 -1
- package/dist/cli/dashboard-style.d.ts +9 -0
- package/dist/cli/dashboard-style.js +36 -0
- package/dist/cli/dashboard-style.js.map +1 -0
- package/dist/cli/dashboard.d.ts +5 -3
- package/dist/cli/dashboard.js +102 -12
- package/dist/cli/dashboard.js.map +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +37 -12
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/input.d.ts +1 -0
- package/dist/cli/input.js +23 -17
- package/dist/cli/input.js.map +1 -1
- package/dist/cli/main.js +6 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/output.js +17 -1
- package/dist/cli/output.js.map +1 -1
- package/dist/config/access-provenance.d.ts +10 -0
- package/dist/config/access-provenance.js +31 -0
- package/dist/config/access-provenance.js.map +1 -0
- package/dist/config/access.d.ts +12 -2
- package/dist/config/access.js +125 -27
- package/dist/config/access.js.map +1 -1
- package/dist/config/loader.js +1 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/oidc.js +4 -2
- package/dist/config/oidc.js.map +1 -1
- package/dist/config/project.js +2 -1
- package/dist/config/project.js.map +1 -1
- package/dist/config/resolve-access.js +24 -10
- package/dist/config/resolve-access.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/process-diagnostics.d.ts +2 -0
- package/dist/process-diagnostics.js +11 -0
- package/dist/process-diagnostics.js.map +1 -0
- package/dist/runtime/access-errors.d.ts +4 -0
- package/dist/runtime/access-errors.js +25 -0
- package/dist/runtime/access-errors.js.map +1 -0
- package/dist/runtime/approval-registry.d.ts +27 -0
- package/dist/runtime/approval-registry.js +126 -0
- package/dist/runtime/approval-registry.js.map +1 -0
- package/dist/runtime/client.js +54 -14
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/coordinator.d.ts +17 -1
- package/dist/runtime/coordinator.js +124 -6
- package/dist/runtime/coordinator.js.map +1 -1
- package/dist/runtime/dashboard-sanitize.d.ts +2 -0
- package/dist/runtime/dashboard-sanitize.js +7 -0
- package/dist/runtime/dashboard-sanitize.js.map +1 -0
- package/dist/runtime/dashboard.d.ts +43 -1
- package/dist/runtime/dashboard.js +2 -3
- package/dist/runtime/dashboard.js.map +1 -1
- package/dist/runtime/definition.js +11 -1
- package/dist/runtime/definition.js.map +1 -1
- package/dist/runtime/select-native.d.ts +2 -1
- package/dist/runtime/select-native.js +28 -10
- package/dist/runtime/select-native.js.map +1 -1
- package/dist/runtime/service.js +24 -1
- package/dist/runtime/service.js.map +1 -1
- package/dist/runtime/types.d.ts +9 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/runtime/worker-approvals.d.ts +21 -0
- package/dist/runtime/worker-approvals.js +51 -0
- package/dist/runtime/worker-approvals.js.map +1 -0
- package/dist/runtime/worker-client.js +73 -20
- package/dist/runtime/worker-client.js.map +1 -1
- package/dist/runtime/worker-server.d.ts +2 -0
- package/dist/runtime/worker-server.js +39 -10
- package/dist/runtime/worker-server.js.map +1 -1
- package/dist/sdk/definitions.d.ts +4 -1
- package/dist/sdk/definitions.js +16 -2
- package/dist/sdk/definitions.js.map +1 -1
- package/dist/sdk/permission-validation.js +10 -1
- package/dist/sdk/permission-validation.js.map +1 -1
- package/dist/sdk/types.d.ts +29 -1
- package/dist/sdk/types.js.map +1 -1
- package/package.json +4 -1
- package/sdk/access-config.md +86 -4
- package/sdk/adapter-contract.md +7 -13
- package/sdk/additional-harnesses.md +43 -0
- package/sdk/agent-skill.md +7 -4
- package/sdk/agent.md +5 -3
- package/sdk/approvals.md +120 -0
- package/sdk/authentication.md +1 -1
- package/sdk/cli/dashboard-design.md +44 -4
- package/sdk/cli/dashboard-inspection.md +25 -0
- package/sdk/cli/index.md +20 -11
- package/sdk/cli/output.md +10 -4
- package/sdk/completion-notifications.md +2 -0
- package/sdk/config.md +1 -1
- package/sdk/copilot.md +53 -0
- package/sdk/cursor.md +68 -0
- package/sdk/diagnostics.md +29 -0
- package/sdk/distribution.md +53 -1
- package/sdk/evals.md +10 -4
- package/sdk/fx.md +51 -6
- package/sdk/harnesses.md +13 -13
- package/sdk/index.md +4 -2
- package/sdk/message-delivery.md +1 -1
- package/sdk/opencode.md +57 -0
- package/sdk/permissions.md +7 -5
- package/sdk/pr-integration.md +1 -1
- package/sdk/project-team.md +2 -0
- package/sdk/sessions.md +4 -1
- package/sdk/tools.md +1 -1
- package/sdk/v1-runtime.md +6 -6
package/sdk/copilot.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# GitHub Copilot adapter
|
|
2
|
+
|
|
3
|
+
The Copilot adapter uses the native GitHub Copilot SDK over a dedicated stdio runtime process. The pinned SDK is `@github/copilot-sdk` 1.0.14, with native protocol version 3 as exposed by Copilot runtime 1.0.85. It does not implement a model loop. The public model-only configuration and access routes are defined in [additional harnesses](additional-harnesses.md).
|
|
4
|
+
|
|
5
|
+
## Native GitHub access
|
|
6
|
+
|
|
7
|
+
An explicit `subscription` connection uses the native Copilot or GitHub CLI login. An `api-key` connection with `provider: "github"` instead reads the selected GitHub token, defaulting to `COPILOT_GITHUB_TOKEN`. The token must be eligible for Copilot; its presence alone does not prove entitlement. Neither route uses BYOK or Vercel Gateway.
|
|
8
|
+
|
|
9
|
+
The adapter removes competing ambient tokens and provider overrides. Saved-login access enables native logged-in-user discovery without passing a token; explicit GitHub access supplies the selected `gitHubToken` to the SDK client and session and disables logged-in-user fallback. Startup checks native authentication status and exact native model-catalog membership without generation. Saved-user or GitHub CLI authentication must not be confused with an environment token. Unknown authentication metadata and route changes fail explicitly. Auto-routing models are not accepted. Native login and refresh remain the harness's responsibility. Saved-login access preserves the native Copilot home so last-user metadata, keychain credentials, file-backed credentials, and GitHub CLI discovery remain native. Each session still uses a private `configDirectory` with configuration discovery disabled. Explicit GitHub-token, BYOK, and Gateway routes use a private runtime base directory as well. Subharness never reads or copies native credentials or imports personal provider/model configuration. Cleanup removes only adapter-owned private state. This preserves the existing manual approval and managed-policy behavior; it does not import personal permission configuration.
|
|
10
|
+
|
|
11
|
+
## Direct provider API keys
|
|
12
|
+
|
|
13
|
+
Copilot BYOK connections use `type: "api-key"` with `provider: "openai"`, `"anthropic"`, or `"azure"`. The selected variable defaults to `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or `AZURE_OPENAI_API_KEY`, respectively; `env` and `envFile` can select another source. These routes disable GitHub login and configure one singular native provider, so requests use the chosen provider's billing.
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"access": {
|
|
18
|
+
"copilot": [{ "type": "api-key", "provider": "openai", "wireApi": "responses" }]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
OpenAI defaults to `https://api.openai.com/v1`, and Anthropic defaults to `https://api.anthropic.com`. `baseUrl` can select another compatible endpoint; Azure requires it and expects its native resource/project URL. OpenAI-compatible services use `provider: "openai"` and their full API prefix. OpenAI/Azure accept `wireApi: "completions" | "responses"`, defaulting to `completions`; Anthropic uses Messages and rejects `wireApi`. Azure alone accepts `apiVersion`, passed to the SDK's native Azure option. HTTP transport is used.
|
|
24
|
+
|
|
25
|
+
The agent's `model` is the native behavior model. Optional `wireModel` supplies a different remote model or Azure deployment name; omission uses the agent model on the wire too. The adapter passes these explicit values without inventing aliases. Readiness verifies the native model and configured provider/endpoint/credential representation. A BYOK assistant response can label its model with the remote name or deployment; native model RPC readback remains the authority for the pinned behavior model. BYOK has no universal remote catalog check: readiness does not prove a remote model exists or that a key has quota. Neither configured readback nor a user-supplied endpoint proves the remotely executed model.
|
|
26
|
+
|
|
27
|
+
Custom headers, bearer-token callbacks, experimental mixed-provider sessions, and provider options outside the documented access shape are not exposed. Gateway connections remain a separate supported route with their existing fixed endpoint and `creator/model` names.
|
|
28
|
+
|
|
29
|
+
## Gateway access
|
|
30
|
+
|
|
31
|
+
The adapter accepts explicit Gateway API-key or project OIDC connections. It configures a single native OpenAI-compatible provider with the fixed base URL `https://ai-gateway.vercel.sh/coding-agent/v1`, HTTP Chat Completions, and the exact requested `creator/model` identifier. API-key access supplies the selected API key; OIDC uses bearer-token authentication. GitHub login and unrelated provider credentials are disabled for this inference route. Ambient `COPILOT_PROVIDER_*` settings, model preferences, alternate endpoints, and native logged-in-user discovery must not override it.
|
|
32
|
+
|
|
33
|
+
A dedicated private native configuration/state directory prevents stored provider settings or plugins from changing billing. Native repository instructions and the caller's working directory remain available. Credentials are passed through the private SDK/protocol configuration, never process arguments or user-visible diagnostics. Startup uses the native protocol handshake, session creation, current-model and provider-endpoint introspection to verify the selection without submitting a prompt. The native allowed model list is restricted to the selected identifier. Unverifiable or changed selection is an explicit configuration error, not an access fallback.
|
|
34
|
+
|
|
35
|
+
The SDK's bundled runtime may be used; a separate global Copilot installation is not required when that runtime is available. Missing or incompatible runtime support is `HARNESS_UNAVAILABLE`. `check copilot --model creator/model` checks startup and cleanup only, not remote allowance.
|
|
36
|
+
|
|
37
|
+
## Tools and approvals
|
|
38
|
+
|
|
39
|
+
Declared tools use native SDK tool callbacks and preserve validated text/image results. Copilot's result envelope groups text and images separately: text blocks are joined in order, and images retain their relative order as binary image results. Interleaving between those groups is not representable. Specialist instructions supplement the native system instructions; they never replace the harness's tool loop. Tool names retain their definition keys; a collision with a native built-in tool is an explicit configuration error, never an override. Callbacks are admitted only while a task is active, and interruption/close drain already-admitted callbacks.
|
|
40
|
+
|
|
41
|
+
The adapter uses the native manual permission mode and forwards supported active permission requests through the shared approval flow. Only the specific offered once/deny decisions are exposed initially; it does not invent persistent permission grants. The action context is bounded and excludes credential-bearing configuration. Unsupported input or startup requests fail with `INPUT_REQUIRED` and stop affected work. No callback automatically approves native commands or expands native permissions for declared children.
|
|
42
|
+
|
|
43
|
+
Enterprise managed-policy self-fetch is enabled for explicit GitHub-token access, which supplies the session identity required by the SDK. Saved-login, BYOK, and Gateway routes omit that opt-in because they do not supply a session GitHub token; the adapter does not extract one or promise enterprise policy self-fetch for those routes. Native manual approval requirements remain enabled on every route. A permission request that specifically requires human-only provenance or an unrepresentable sandbox bypass is unsupported and fails with `INPUT_REQUIRED`; a Subharness caller response must not be relabeled as a verified human decision. Requests already resolved by native hooks are not presented as new approval requests. Native withdrawal retires the matching request and late answers are ignored.
|
|
44
|
+
|
|
45
|
+
## Lifecycle
|
|
46
|
+
|
|
47
|
+
Follow-ups reuse the native session. Events are correlated to the active turn; a final assistant message alone is not successful completion. A session-idle event is accepted in any native session mode after evidence that the submitted turn started, and only when no native work remains. A stale idle event cannot complete a newly submitted task. The adapter reports native shutdown or transport loss as an execution failure and limits final response text to 1 MiB. Model or endpoint substitutions fail without replay. Turn completion withdraws remaining approval requests.
|
|
48
|
+
|
|
49
|
+
Interruption stops tool admission, withdraws pending approvals, requests native abort, confirms no native work remains, and waits for admitted callbacks. An abort acknowledgement alone does not prove the native process stopped. Native cancellation requests and status checks are bounded; a cancellation that cannot establish idle/termination returns `CANCELLATION_FAILED`. Already-admitted host callbacks are drained without imposing that native cancellation deadline on their execution. Steering uses the coordinator's interrupt behavior, and prompt-free recovery returns `RECOVERY_UNSUPPORTED`.
|
|
50
|
+
|
|
51
|
+
An unconfirmed interruption fails the active turn and prevents further tasks in that session. Tool admission remains closed, and close must still attempt native cleanup. The turn cannot later report success after continuing with disabled tool callbacks. This rule also applies while native prompt admission is pending.
|
|
52
|
+
|
|
53
|
+
Close retires the session, shuts down the dedicated native runtime, confirms termination, drains tools, and removes private state. Cleanup RPCs are bounded so an unresponsive session cannot prevent process shutdown attempts. Escalated termination without confirmed exit produces `CANCELLATION_FAILED`, never successful cleanup. Startup failure also cleans up. A successful readiness record requires completed runtime cleanup. Raw SDK errors and process output are replaced with fixed diagnostics so credentials do not enter CLI records.
|
package/sdk/cursor.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Cursor adapter
|
|
2
|
+
|
|
3
|
+
The Cursor adapter runs the native Cursor CLI for subscription access and the native Cursor TypeScript SDK for explicit API-key access against the caller's local working directory. Cursor owns inference, tools, conversation state, and context management. Cloud agents are not part of this adapter.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { cursor } from "subharness";
|
|
9
|
+
|
|
10
|
+
cursor({ model: "CURSOR_MODEL_ID", sandboxMode: "enabled" });
|
|
11
|
+
|
|
12
|
+
interface CursorOptions {
|
|
13
|
+
readonly model: string;
|
|
14
|
+
readonly sandboxMode?: "enabled" | "disabled";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface CursorConfig extends CursorOptions {
|
|
18
|
+
readonly kind: "cursor";
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The constructor returns an immutable configuration. Model is required and must be nonempty. Unknown options, including `fast` and `effort`, are rejected. For API-key access, `sandboxMode` maps to the native SDK's local sandbox option. Omission preserves the SDK default, which runs local tools without interactive approval and without a sandbox. The native sandbox, when enabled, restricts shell writes and network access according to Cursor's native policy; it is not a sandbox for host-side custom tool callbacks. The adapter never enables broader permissions to make declared-child launches succeed.
|
|
23
|
+
|
|
24
|
+
The CLI target is `cursor`. `run cursor` and `check cursor` require `--model`. The following catalog behavior applies to API-key access; subscription model selection is defined below. The native routing IDs `auto` and `auto-smart`, including catalog aliases that resolve to either ID, are rejected because they do not retain a concrete model. The catalog has no generic router flag; the adapter does not infer routing from display names or invent additional reserved IDs. `--effort` is unsupported for this adapter. Native model aliases are accepted only when the account catalog supplies their mapping. Startup verifies the account and selected model through the native SDK without a generation, creates an empty local agent, and pins the resolved selection for follow-ups. When terminal results report a model, a detected replacement fails explicitly without replay. The agent handle's configured model is not evidence of the model that executed a run.
|
|
25
|
+
|
|
26
|
+
## Access
|
|
27
|
+
|
|
28
|
+
Personal access uses the `cursor` key and requires an explicit connection. A `subscription` connection uses the current native Cursor CLI login; the user completes `cursor-agent login` outside Subharness. An `api-key` connection uses the SDK and defaults to `CURSOR_API_KEY`. Both environment and explicit dotenv references follow the shared [access contract](access-config.md). Omission does not discover or enable a connection, and an empty list disables Cursor. Native login and API-key access are separate billing routes; neither is an implicit fallback for the other. Vercel API-key and OIDC connections are unsupported and fail with `UNSUPPORTED_OPTION` before startup. A Cursor API key is a Cursor billing route, not a Gateway credential.
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"access": {
|
|
33
|
+
"cursor": [{ "type": "subscription" }]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For API-key access, the selected key is passed explicitly to the SDK. Subscription access delegates credential discovery to the native CLI; Subharness never reads, copies, or transfers its saved tokens. A missing or expired native login fails with `ACCESS_UNAVAILABLE` and login guidance before submission. Subharness does not open a browser or perform an interactive login. Explicit API-key/auth-token environment variables must not silently override a subscription connection. Backend or website endpoint overrides through `CURSOR_BACKEND_URL` or `CURSOR_WEBSITE_URL` are rejected before the key is used. This also applies to the SDK host environment, because the SDK reads process-level settings. The adapter does not transfer subscription tokens, change account-synced provider settings, or treat the editor's custom-provider configuration as verified Gateway routing. Credentials and raw native errors must not appear in diagnostics.
|
|
39
|
+
|
|
40
|
+
## API-key session behavior
|
|
41
|
+
|
|
42
|
+
Successive tasks use the same native agent. Specialist instructions supplement the native harness instructions in the first user turn; they do not replace Cursor's system prompt. Declared tools use native SDK custom tools, validate inputs, preserve text and image results, and stop admitting calls when the task stops. Cancellation and close wait for admitted callbacks to settle.
|
|
43
|
+
|
|
44
|
+
Active steering returns `UNSUPPORTED_DELIVERY`; the coordinator applies its documented interrupt-and-replace behavior. Interruption cancels the active native run and waits for terminal completion. Failure to confirm native stop returns `CANCELLATION_FAILED`, fails the active turn, and prevents further tasks in that session. Tool admission remains closed; close must still dispose the native agent and drain admitted callbacks. Prompt-free recovery returns `RECOVERY_UNSUPPORTED`. A turn succeeds only with a successful terminal result and returns its final text. Responses are limited to 1 MiB.
|
|
45
|
+
|
|
46
|
+
The SDK's headless permission decisions remain native. This SDK version exposes no supported interactive approval or input channel to the adapter. Its `request` message records a backend request identifier and is not an approval request. Native run failures produce a sanitized execution error; the adapter does not invent an `INPUT_REQUIRED` mapping from undocumented error codes. Close disposes the native agent and drains custom tools. `check cursor` verifies credential/model discovery, local agent creation, and disposal without submitting a prompt; it does not prove quota or that a later shell command will be permitted.
|
|
47
|
+
|
|
48
|
+
## Subscription protocol and lifetime
|
|
49
|
+
|
|
50
|
+
Subscription access uses the installed `cursor-agent` executable with its native ACP protocol over standard input and output. The supported CLI release is `2026.09.23-86fc751`, using ACP version 1. Other releases fail before input with `HARNESS_UNAVAILABLE` and compatibility guidance. The executable name is explicit because `agent` is also a Subharness compatibility alias. A session owns one native process and one native conversation in the requested working directory. Subscription access currently supports macOS and Linux, where the adapter can own and terminate a native process group. Windows subscription startup fails with `UNSUPPORTED_OPTION`; the SDK API-key route is unchanged.
|
|
51
|
+
|
|
52
|
+
The transport accepts newline-delimited JSON-RPC 2.0, correlates responses by request ID, and rejects malformed envelopes or oversized protocol frames with `PROTOCOL_ERROR`. Individual frames are limited to 16 MiB; returned assistant text remains limited to 1 MiB. Native standard error and raw error messages are not forwarded to callers. Startup and configuration requests are bounded; model execution itself has no arbitrary completion deadline. Transport loss fails pending operations and retires the native process rather than leaving a task pending indefinitely.
|
|
53
|
+
|
|
54
|
+
Native cancellation sends `session/cancel` and requires the active `session/prompt` to finish before confirming interruption. A cancellation timeout retires and poisons the session with `CANCELLATION_FAILED`; it does not replay input or silently open a replacement conversation. Closing stops admitting tool calls, retires the native process, and drains admitted custom-tool callbacks. Process retirement uses bounded termination and confirms process exit; failure to establish termination is an error. Tool callbacks have their own drain lifetime and are not falsely marked cancelled by a native timeout. Queued follow-ups reuse the same conversation after ordinary completion or confirmed native cancellation. Active steering and prompt-free recovery remain unsupported.
|
|
55
|
+
|
|
56
|
+
## Subscription startup and capabilities
|
|
57
|
+
|
|
58
|
+
Subscription access preserves native credential ownership. It removes `CURSOR_API_KEY` and `CURSOR_AUTH_TOKEN` from the child environment and does not call ACP `authenticate`, `login`, or `logout`. Native credential discovery, refresh, and account eligibility remain Cursor's responsibility. The adapter checks bounded `status --format json` output for authenticated access/refresh availability, then requires authenticated ACP session creation and model discovery. Status alone is not proof of readiness, quota, subscription-plan eligibility, or credential history. The `subscription` connection selects Cursor's saved native CLI account route; it does not attest how those native credentials were originally created or impose an account spending cap.
|
|
59
|
+
|
|
60
|
+
A private owner-only configuration and data directory isolates model selection and session state. Native non-credential permission and network settings are preserved in the private configuration; project permission rules continue to apply. Shared user/project configuration is never rewritten. Alternate Cursor endpoints, authless/local-provider settings, and Bedrock activation are unsupported and rejected before native startup. Ordinary AWS credentials are not rejected merely because unrelated tools may use them. Native API keys, custom endpoints, or API-key helpers cannot silently select a different inference route. Subharness-created configuration files use mode `0600`; native-generated files remain inside the owner-only directory. Private state is removed after confirmed process shutdown. The native credential store is neither copied nor exposed through diagnostics.
|
|
61
|
+
|
|
62
|
+
Startup negotiates ACP version 1 with the native parameterized model picker. The required model must match a concrete base ID in the returned native catalog; CLI display aliases and bracketed variant strings are not inferred. Native model parameters retain their defaults for that model. Auto routing IDs `auto`, `auto-smart`, `default`, and `default[]` are rejected. The selected model is applied with `session/set_config_option` and verified from its returned configuration before each prompt. A reported change fails explicitly. The adapter does not claim configured model readback proves the remotely executed model; this native protocol does not expose executed-model metadata in its terminal result. For example, `cursor({ model: "gpt-5-mini" })` selects that base model when present in the account catalog. A CLI display variant such as a model name with an effort suffix is not automatically treated as the same base ID.
|
|
63
|
+
|
|
64
|
+
For subscription access, either explicit `sandboxMode` value fails with `UNSUPPORTED_OPTION` before submission because ACP does not expose a verified sandbox control. Omitting it uses Cursor's native ACP execution and permission behavior; it does not enable a Subharness sandbox. The API-key route retains its SDK sandbox option.
|
|
65
|
+
|
|
66
|
+
Declared tools use a uniquely named private MCP server supplied in `session/new`. The adapter verifies that the native client initializes the server before admitting prompts; a skipped or failed native MCP connection is a startup error. MCP request bodies are limited to 16 MiB and headers to 16 KiB. Text and image results retain their MCP content types and shared tool-result limits. Native approval rules still apply. Active `session/request_permission` requests expose only the offered allow-once and reject-once choices through the shared approval flow with `harness: "cursor"`; persistent allow-always/reject-always choices are not exposed. Original native option IDs are retained. Unsupported or malformed interactive requests, including questions and plan approval, fail with `INPUT_REQUIRED` and cancel the native turn. Startup-time interactive requests are unsupported. Withdrawal, terminal completion, cancellation, and close retire pending requests; late answers never grant permission.
|
|
67
|
+
|
|
68
|
+
A terminal ACP `end_turn` returns the accumulated assistant text. `cancelled` is interruption; refusal or exhausted-limit terminal reasons and RPC/transport failures reject with a sanitized execution error. This CLI release can render some backend failures as ordinary assistant text followed by `end_turn`, without a separate failure signal. Such text is returned as native output; a completed Subharness task on this route means the native turn ended, not that every backend operation succeeded. The adapter does not guess error status from prose. Raw transport diagnostics remain private. This capability limit is specific to the subscription ACP route; the API-key SDK route retains its typed native results.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Error Diagnostics
|
|
2
|
+
|
|
3
|
+
Error records retain their existing `{ code, message }` shape. Messages describe the operation that failed and include safe, observed context that helps callers identify the cause. The diagnostics described here never include raw native stderr, SDK exception text, HTTP response bodies, RPC error messages or data, prompts, credential values, or configuration contents. Unknown native values receive a fixed generic explanation rather than being echoed. Paths included in these diagnostic cases are quoted with C0, C1, DEL, and Unicode line separators escaped.
|
|
4
|
+
|
|
5
|
+
## Claude startup and turn failures
|
|
6
|
+
|
|
7
|
+
Claude startup distinguishes native account inspection, model catalog retrieval, and model settings validation. An unexpected exception from account inspection or model catalog retrieval is `HARNESS_FAILED`, with the failed phase identified. Unexpected exceptions after successful access verification are also harness failures identified by the current startup phase. They do not establish unavailable access and do not permit connection or harness fallback. A completed account inspection that demonstrates unavailable or incompatible access retains `ACCESS_UNAVAILABLE` and its existing eligible fallback behavior. Existing explicit configuration, settings capability, model, and effort errors retain their codes and guidance, including `--model` guidance for an unverifiable native default. Cleanup and no-prompt-before-verification rules remain unchanged.
|
|
8
|
+
|
|
9
|
+
Claude terminal result failures retain `HARNESS_ERROR`. Recognized result subtypes distinguish a native turn limit, a native budget limit, exhausted structured-output retries, and an error during execution. Messages describe the reported category without claiming an unobserved provider cause, quoting native error text, or promising that Subharness exposes configuration for every native limit. Unknown subtypes and unsuccessful results without a recognized category receive a fixed generic turn-failure message. A reported execution failure is never automatically retried or replayed.
|
|
10
|
+
|
|
11
|
+
## Native protocol and process failures
|
|
12
|
+
|
|
13
|
+
Codex and fx RPC rejection messages identify the requested native method and include the native error code only when it is a safe integer. Known JSON-RPC codes use fixed categories: `-32700` is a parse error, `-32600` an invalid request, `-32601` an unavailable method, `-32602` invalid parameters, and `-32603` an internal error. Other safe integer codes remain numeric without an invented interpretation. Missing or malformed codes are not printed. A generic rejection does not claim that authentication or native configuration caused the error. Existing steering and session-option capability classifications retain their Subharness error codes and useful recovery guidance. Freeform native messages and error data are never forwarded.
|
|
14
|
+
|
|
15
|
+
When an observed process termination causes an error, diagnostics identify the process and include its observed exit code or recognized termination signal when available. This applies to Codex and fx transports, the Claude subprocess owned by the adapter, session workers, and coordinator startup. A signal does not establish why it was sent; for example, `SIGKILL` does not by itself prove an out-of-memory condition. Missing termination metadata is omitted. Cleanup-induced termination must not replace or be described as the cause of an earlier failure. Adding diagnostics does not wait indefinitely for process exit or weaken bounded shutdown.
|
|
16
|
+
|
|
17
|
+
Worker failures identify the pending operation when available, such as startup, turn submission, steering, interruption, recovery, or closing. Coordinator failures distinguish failure to spawn, early exit, and startup timeout, retaining existing retirement and endpoint-publication safeguards. Only fixed operation labels and recognized system error categories may be added; raw operating-system exception text is not forwarded.
|
|
18
|
+
|
|
19
|
+
## Coordinator communication
|
|
20
|
+
|
|
21
|
+
Coordinator communication failures retain `COORDINATOR_UNAVAILABLE`. A non-success HTTP response identifies its numeric status and a recognized requested operation, when available. A successful response with no body is distinguished from HTTP rejection. Invalid JSON records, an incomplete final record, and an interrupted response stream have distinct fixed explanations. Transport failures retain the statement that the task was not automatically retried. These diagnostics do not expose endpoint tokens, raw request or response contents, or arbitrary operation names. They do not infer whether a task completed from an HTTP or transport failure and do not automatically replay it.
|
|
22
|
+
|
|
23
|
+
## Access configuration and OIDC
|
|
24
|
+
|
|
25
|
+
Personal-access schema errors retain `INVALID_CONFIG` and identify their structural location: the root object, `access`, a recognized harness entry such as `access.claudeCode`, or a zero-based connection location such as `access.claudeCode[0]`. A known invalid connection property may extend that location, for example `access.claudeCode[0].env`. Unknown property names or harness names are not echoed because arbitrary keys can contain sensitive values. An unknown connection property identifies its containing connection. Errors raised while loading a file also identify its absolute, safely quoted main-checkout path, or execution-project path outside Git. Invalid JSON and file-read errors identify the same safely quoted path. Validation happens before any connection is attempted; it does not fabricate attempted-access context.
|
|
26
|
+
|
|
27
|
+
An inaccessible execution directory is identified by its safely quoted caller-supplied path in both the CLI and project resolver. A missing credential file identifies its safely quoted configured `envFile` reference; it retains `ACCESS_UNAVAILABLE` and eligible fallback behavior. These messages preserve the existing path resolution rules and do not read credential contents to enrich an error.
|
|
28
|
+
|
|
29
|
+
OIDC expiration and not-yet-valid claims have separate diagnostics, both retaining `INVALID_CONFIG`. Expiration directs the caller to refresh the configured credential source. A future not-before claim explains that the token is not yet valid and suggests checking the system clock or waiting until validity begins; it does not assert that the clock is wrong. If both conditions apply, expiration is reported first. No token values or raw claims are printed. Claim-validation rules, project matching, and credential-source selection remain unchanged.
|
package/sdk/distribution.md
CHANGED
|
@@ -4,7 +4,7 @@ The npm package name is `subharness`. One package provides the TypeScript SDK an
|
|
|
4
4
|
|
|
5
5
|
The source repository is [vercel-labs/subharness](https://github.com/vercel-labs/subharness). Its GitHub visibility is internal, so cloning requires repository access. The product name is subharness. The primary CLI command is `subharness`; `agent` remains an identical compatibility alias. Agent discovery and personal configuration live under `.subharness/`.
|
|
6
6
|
|
|
7
|
-
Node.js 22.18 or newer is required. The SDK is ESM and includes TypeScript declarations.
|
|
7
|
+
Node.js 22.18 or newer is required. The SDK is ESM and includes TypeScript declarations. Installing this package supplies the pinned Copilot and Cursor SDK dependencies. It does not configure native harness credentials or install the Codex, Claude Code, fx, OpenCode, or Cursor executables. Cursor subscription access requires the separately installed `cursor-agent` CLI; Cursor API-key access uses the bundled SDK dependency. The Copilot SDK may supply its compatible runtime as defined in the [Copilot contract](copilot.md).
|
|
8
8
|
|
|
9
9
|
## Installation and resolution
|
|
10
10
|
|
|
@@ -40,6 +40,58 @@ Authentication uses npm trusted publishing through GitHub Actions OIDC, without
|
|
|
40
40
|
|
|
41
41
|
To release, choose a new stable tag on `main` that includes this workflow, enter the release notes, leave the GitHub pre-release option unchecked, and publish the GitHub release. The corresponding Actions run reports whether npm publication succeeded. No separate version-bump commit or release-candidate channel is required.
|
|
42
42
|
|
|
43
|
+
### Maintainer commands
|
|
44
|
+
|
|
45
|
+
An agent asked to publish to npm uses this GitHub release path. It needs authenticated `gh` access with permission to create releases in `vercel-labs/subharness`; local npm login and npm two-factor prompts are not part of normal automated publishing. Intended changes must already be merged into `main` with their checks passing. Uncommitted work and commits on other branches are not included.
|
|
46
|
+
|
|
47
|
+
Inspect live state before selecting the version and source commit:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
git fetch origin main --tags
|
|
51
|
+
gh release list --repo vercel-labs/subharness
|
|
52
|
+
gh run list --repo vercel-labs/subharness --workflow release.yml --limit 10
|
|
53
|
+
npm view subharness versions dist-tags --json --registry=https://registry.npmjs.org/
|
|
54
|
+
git log -5 --oneline origin/main
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Use the requested stable version, or settle the version with the maintainer if the request does not specify one. Do not infer it from `package.json`. Verify that it is greater than the latest published version and has no existing npm version, GitHub release, or remote tag. Wait for any in-progress release before creating another. Inspect remote tags with `git ls-remote --tags origin`. Resolve a tag conflict before continuing: `--target` does not move an existing tag.
|
|
58
|
+
|
|
59
|
+
After selecting the version and reviewing release notes, create the release at the exact verified `main` commit. In this example, replace `X.Y.Z` with the selected version and prepare `.context/release-notes.md` with the release notes:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
release_version='X.Y.Z'
|
|
63
|
+
release_commit=$(git rev-parse origin/main)
|
|
64
|
+
gh release create "v$release_version" \
|
|
65
|
+
--repo vercel-labs/subharness \
|
|
66
|
+
--target "$release_commit" \
|
|
67
|
+
--title "v$release_version" \
|
|
68
|
+
--notes-file .context/release-notes.md \
|
|
69
|
+
--latest
|
|
70
|
+
gh run list --repo vercel-labs/subharness --workflow release.yml \
|
|
71
|
+
--event release --commit "$release_commit" \
|
|
72
|
+
--json databaseId,headBranch,headSha,status,conclusion,url
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Select the run for the new release tag and commit, then run `gh run watch RUN_ID --repo vercel-labs/subharness --exit-status`, replacing `RUN_ID` with its database ID. Inspect the run with `gh run view RUN_ID --repo vercel-labs/subharness --log` if necessary. The job must succeed through **Publish package**, including all behavioral tests and clean-consumer checks. The workflow limits tests to two concurrent files to avoid exhausting subprocess test deadlines on hosted runners; preserve that limit when updating release checks.
|
|
76
|
+
|
|
77
|
+
`gh run watch` does not support fine-grained personal access tokens. If it is unavailable for the current authentication, poll `gh run view RUN_ID --repo vercel-labs/subharness --json status,conclusion,url` until `status` is `completed` and require `conclusion` to be `success`. Authentication must also allow reading Actions runs.
|
|
78
|
+
|
|
79
|
+
Confirm public availability after the job succeeds:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
npm view "subharness@$release_version" version dist.shasum dist.integrity \
|
|
83
|
+
--json --prefer-online --registry=https://registry.npmjs.org/
|
|
84
|
+
npm view subharness dist-tags --json --prefer-online --registry=https://registry.npmjs.org/
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The requested version must exist, `latest` must point to it, and its `dist.shasum` must match the tarball checksum in the publish log. npm can accept a publish while still processing the package; metadata and tarball downloads can become available at different times. Wait for propagation and verify that the public tarball downloads successfully before reporting completion. Report the GitHub release, successful Actions run, and npm version links.
|
|
88
|
+
|
|
89
|
+
### Failed releases and retries
|
|
90
|
+
|
|
91
|
+
Inspect the failed step and registry state before retrying. A failure before **Publish package** does not publish anything. For a transient failure on an unchanged commit, rerun the failed job only after confirming that publication did not succeed or remain in processing. A rerun uses the original release commit; it does not pick up a later fix on `main`.
|
|
92
|
+
|
|
93
|
+
When a correction requires a new commit, merge and verify it before publishing another release. Never silently delete or move an existing release/tag. Replacing a failed release/tag requires explicit maintainer authorization and confirmation that the npm version was never accepted for publication. Otherwise, use a new stable version. An accepted npm version is immutable, including while it is processing: do not republish it or recreate its tag to attempt another upload. A delayed registry response alone is not evidence that publication failed.
|
|
94
|
+
|
|
43
95
|
## License
|
|
44
96
|
|
|
45
97
|
The current source checkout and packages built from it are distributed under the Apache License, Version 2.0. The already-published `subharness@0.0.1` registry release remains under the MIT license. The root [LICENSE](../LICENSE) contains the complete Apache License text and is included in packages built from this source.
|
package/sdk/evals.md
CHANGED
|
@@ -41,7 +41,7 @@ resolveEvalAccess(options: {
|
|
|
41
41
|
}): Promise<EvalAccess | EvalAccessError>
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
The evaluation harness discriminator remains limited to the original `codex`, `claudeCode`, and `fx` identifiers. The harness list is nonempty and has no duplicates. IDs are nonempty strings. `minimumValidityMs` is a positive finite safe integer. The default environment is `process.env`, and the default clock is `Date.now` in milliseconds. Input errors are returned before native startup; this function never starts a harness or makes a network request.
|
|
45
45
|
|
|
46
46
|
`EvalAccessError` is a typed returned error, not an exception-based domain result. Its `reason` is one of `invalid-input`, `policy`, `credentials-unavailable`, `validation`, `identity`, or `insufficient-lifetime`. Its message is exactly `Evaluation access is unavailable.` for every reason; it never retains raw caught exceptions, credentials, token claims, or native output. A policy error includes absent keys, a disabled participating harness, non-OIDC routes, or fallback lists. Credential reading failures are credentials-unavailable; malformed/expired/not-yet-valid credentials or linkage validation failures are validation. A valid linked token whose project or organization differs from the independent expectation is identity. Insufficient remaining validity uses insufficient-lifetime. Unclassified access-layer failures are validation, not guessed native authentication causes.
|
|
47
47
|
|
|
@@ -80,7 +80,7 @@ interface EvalManifest {
|
|
|
80
80
|
effort?: string;
|
|
81
81
|
// Optional permission fields from the matching native harness constructor.
|
|
82
82
|
}>;
|
|
83
|
-
cases?: Array<"auth-smoke" | "concurrent-progress" | "cli-skill" | "cli-background">;
|
|
83
|
+
cases?: Array<"auth-smoke" | "concurrent-progress" | "cli-skill" | "cli-background" | "cli-approval">;
|
|
84
84
|
limits?: {
|
|
85
85
|
startupMs?: number;
|
|
86
86
|
caseMs?: number;
|
|
@@ -94,11 +94,11 @@ There are one to three targets with unique IDs matching `[a-z][a-z0-9-]{0,47}`.
|
|
|
94
94
|
|
|
95
95
|
`access.cwd` is an absolute existing project directory used only to resolve access, never as the model's execution directory. Expected IDs are nonempty strings. Planning may validate directory/file metadata but never reads a credential source, starts a native executable (including `--version`), evaluates repository agent modules, or makes a network request.
|
|
96
96
|
|
|
97
|
-
Cases default to `["auth-smoke"]`. An explicitly supplied list is nonempty, unique, contains only the
|
|
97
|
+
Cases default to `["auth-smoke"]`. An explicitly supplied list is nonempty, unique, contains only the five supported case IDs, and includes `auth-smoke`. Cells are the full target-by-case product, executed in target order and then the fixed order auth-smoke, concurrent-progress, cli-skill, cli-background, cli-approval. Cell IDs are `<target-id>/<case-id>`. Every cell has a fresh caller session and one attempt. Cases after an unsuccessful auth-smoke for that target are `not_run` with reason `auth-prerequisite`; no extra smoke is silently submitted.
|
|
98
98
|
|
|
99
99
|
Limits are positive safe integers. Defaults and accepted ranges in milliseconds are: startupMs 30000 (1000–60000), caseMs 120000 (10000–300000), cleanupMs 10000 (5000–30000), and runMs 900000 (30000–1800000). The run budget must fit at least one startup + case + cleanup allowance. Reserve that full allowance before admitting each cell. The case deadline starts after native startup, separately from the startup deadline. Remaining cells become `not_run/run-budget` when insufficient budget remains.
|
|
100
100
|
|
|
101
|
-
Fixed limits are one caller at a time, one requested caller turn per cell, one deterministic fake child at most, no retries/resume/replay, one invocation of each fixed helper action, helper lifetime at most 30000 ms, fixture output at most 4096 bytes, safe event evidence at most 65536 bytes per cell, and eight evaluated CLI invocations at most. A bounded final result is reserved separately from the event allowance. Overflow stops the affected cell and cannot erase its final classification. Up to
|
|
101
|
+
Fixed limits are one caller at a time, one requested caller turn per cell, one deterministic fake child at most, no retries/resume/replay, one invocation of each fixed helper action, helper lifetime at most 30000 ms, fixture output at most 4096 bytes, safe event evidence at most 65536 bytes per cell, and eight evaluated CLI invocations at most. A bounded final result is reserved separately from the event allowance. Overflow stops the affected cell and cannot erase its final classification. Up to fifteen requested caller turns is not a guarantee of fifteen provider requests or a hard spending cap: native tool loops and internal auxiliary requests are not fully exposed.
|
|
102
102
|
|
|
103
103
|
Planning exits 0. Invalid input, missing build inputs, and a mismatched live plan digest exit 2 without native startup. A run exits 0 only when every required case assertion passes, otherwise 1. Operator cancellation exits 130 after bounded cleanup. Optional unobservable native dimensions are visible coverage gaps rather than part of a concurrent-progress pass denominator.
|
|
104
104
|
|
|
@@ -169,3 +169,9 @@ Native background acknowledgement, notification and idle reactivation are explic
|
|
|
169
169
|
The event writer accepts only known event kinds, bounded identifiers, booleans, numeric timestamps, exit statuses, validated session/task/response IDs, and known fixture values or verifier digests. It records monotonic local ordering and wall time. It excludes tokens and token hashes, full JWT claims, environments, private endpoint secrets, raw CLI response/error text, raw protocol/native settings/stderr, free-form model transcripts, reasoning, and exception messages/stacks before persistence or console output. CLI responses may be passed transiently to the caller but only validated fields enter reports.
|
|
170
170
|
|
|
171
171
|
Each cell's cost is `{ amountUsd: null, source: "unobserved" }` in live mode and `{ amountUsd: null, source: "synthetic-no-billing" }` in fake mode. No actual total is invented from unknown cells. One smoke is a compatibility observation for that exact configuration, not a statistically supported model ranking.
|
|
172
|
+
|
|
173
|
+
## Structured approval case
|
|
174
|
+
|
|
175
|
+
`cli-approval` measures whether the selected caller harness/model follows the forced Subharness skill to inspect and answer a child's native permission requests. It uses the built CLI, a real local coordinator, and one deterministic child with two sequential requests in the same original turn. Request identifiers and offered choice values are generated per fixture; the caller must read the actual schemas. One harmless fixture operation is authorized once and a second is explicitly outside the caller's assignment and must be denied. Session or persistent grants are not authorized for either operation.
|
|
176
|
+
|
|
177
|
+
Mechanical assertions cover both structured answers, correct request/task correlation, schema-valid choices, exactly one authorized effect and zero denied effects, one child session/turn, no retry/resume/replay or policy changes, terminal observation, bounded CLI use, and complete cleanup. The case may use up to eight built CLI invocations. Its single live caller submission uses the manifest's explicit permissions and strict project OIDC route. Child requests and effects are synthetic: passing establishes caller behavior and CLI/coordinator integration, not that a real native child generated those requests. Each `cli-approval` cell records `childExecution: "synthetic"` independently of the caller's `synthetic` flag. Native adapter emission and continuation are verified separately with deterministic external-protocol tests for Codex, Claude and fx. Reports retain only bounded structured evidence, never raw command output or model transcripts.
|
package/sdk/fx.md
CHANGED
|
@@ -27,14 +27,13 @@ function fx(options: FxOptions): FxConfig;
|
|
|
27
27
|
|
|
28
28
|
`FxConfig` is a readonly harness configuration with `kind: "fx"`, the required `model`, optional `effort`, and optional `permissionMode`. It is a member of `HarnessConfig` and can appear alone or in an ordered `harness` array. The constructor only validates and declares configuration; it does not start a process or access credentials. Unknown fields, including `fast`, are rejected. Native fx does not expose a compatible fast-mode selector in the supported ACP interface; its native fast-mode preference remains in effect.
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Gateway models use the exact AI Gateway `provider/model` identifier. Other routes use their native model identifier without inferring the authentication provider from the model name. An explicit effort must be supported by the native session's advertised configuration for that model. Unsupported effort fails with `UNSUPPORTED_OPTION` before a task is submitted. Omitting effort preserves the native model's default. The adapter verifies the selected model and explicit effort; it does not silently substitute a model. A model appearing in the catalog does not guarantee access through a particular Gateway team or key.
|
|
31
31
|
|
|
32
|
-
Direct CLI invocation does not require a definition: `subharness run fx "Review the current diff."` uses the native
|
|
32
|
+
Direct CLI invocation does not require a definition: `subharness run fx "Review the current diff."` uses the selected native provider's default model when it can be verified before submission. `--model <provider/model>` selects it explicitly. Both forms retain the access and permission requirements below; neither discovers a subscription or enables ambient paid credentials. The TypeScript constructor still requires `model`.
|
|
33
33
|
|
|
34
34
|
## Access
|
|
35
35
|
|
|
36
|
-
fx
|
|
37
|
-
|
|
36
|
+
fx accepts explicitly selected Gateway API-key/OIDC access, native saved logins, and API keys for native custom connections. Omitting `access.fx` leaves fx without an enabled connection; an empty array explicitly disables it. Ambient credentials do not enable a route. Gateway connections retain their existing shape and behavior; the following example selects one explicitly.
|
|
38
37
|
```json
|
|
39
38
|
{
|
|
40
39
|
"access": {
|
|
@@ -45,7 +44,53 @@ fx supports explicit `vercel-api-key` and `vercel-oidc` connections through the
|
|
|
45
44
|
}
|
|
46
45
|
```
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
## Native saved logins and custom API keys
|
|
48
|
+
|
|
49
|
+
Expanded access is verified against fx 0.0.11 and requires the described native status and ACP provider capabilities. Existing Gateway compatibility with fx 0.0.9 is retained.
|
|
50
|
+
|
|
51
|
+
A `subscription` connection requires `provider: "gateway"`, `"codex"`, or `"grok"`. The corresponding native setup is `fx login`, `fx login codex`, or `fx login grok`, performed outside Subharness. These are fx-owned logins; the adapter never imports another harness's tokens. `gateway` selects the saved Vercel login and still bills AI Gateway. The connection type identifies native saved-login access, not proof of a subscription plan or free usage.
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{ "access": { "fx": [{ "type": "subscription", "provider": "codex" }] } }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
For saved Gateway login, native settings must explicitly select `credential_source: "fx_login"`. An automatic preference or a stored-key preference is insufficient because refresh must not fall through to another billing source. The adapter does not change that preference. Native status must report the exact selected login source; startup then initializes native ACP with the same environment. Native discovery, refresh, credential migration, and persistence remain fx's responsibility and may update its own credential stores. Subharness does not read or copy those stores. Local status and successful initialization do not prove remote entitlement or quota.
|
|
58
|
+
|
|
59
|
+
For direct `api-key`, `provider` names a preexisting connection in `~/.fx/settings.json`, and `env` explicitly selects the input credential variable. The native connection must use `protocol: "openai-chat-completions"` and `auth: { "type": "bearer", "env": "NATIVE_KEY_SLOT" }`. Built-in provider names cannot be used for this route. The adapter reads bounded connection metadata, resolves the selected input key, and supplies it only to the native connection's declared environment slot in the child. Input and output variable names can differ. The adapter never writes the key or endpoint into native settings.
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"access": {
|
|
64
|
+
"fx": [{ "type": "api-key", "provider": "openrouter", "env": "MY_ROUTER_KEY", "envFile": ".env.local" }]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
A corresponding native connection, configured through fx, can be:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"providers": {
|
|
74
|
+
"openrouter": {
|
|
75
|
+
"protocol": "openai-chat-completions",
|
|
76
|
+
"base_url": "https://openrouter.ai/api/v1",
|
|
77
|
+
"auth": { "type": "bearer", "env": "OPENROUTER_API_KEY" }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Connection names follow native fx syntax: `[A-Za-z][A-Za-z0-9_-]*`, at most 64 characters, with built-in names reserved case-insensitively. Native endpoints require HTTPS, or HTTP on `localhost`, `127.0.0.1`, or `[::1]`, without user information, query, or fragment. The native connection retains its API path prefix.
|
|
84
|
+
|
|
85
|
+
Model metadata and defaults for custom connections remain native fx settings. The adapter does not invent context limits, protocols, or provider-specific capabilities. Anonymous custom connections are not represented by `api-key`; Anthropic Messages and generic Responses endpoints cannot be treated as Chat Completions. Native custom-connection vision limitations still apply even when metadata advertises image support.
|
|
86
|
+
|
|
87
|
+
Credential output slots cannot name process-control or routing variables such as `HOME`, `PATH`, `FX_PROVIDER`, `FX_MODEL`, `FX_AUTH_MODE`, loader/proxy controls, or native diagnostic/endpoint controls. Inapplicable access fields, malformed profiles, unknown connections, unsupported protocols, and unsafe slots fail before native startup. Both status and ACP use the same selected provider, model, and environment. Custom readiness checks the native provider name, endpoint, bearer slot metadata, and ACP provider/model selection; the generic status label `configured provider` alone is insufficient.
|
|
88
|
+
|
|
89
|
+
For all routes, inherited host-managed authentication, test endpoint overrides, competing Gateway credentials, and recording controls cannot replace the explicitly selected route. Native settings are fingerprinted around startup and before each task; a detected change requires a new session. This is not atomic protection against changes during a native request. Native permissions, approvals, same-session follow-ups, cancellation, and callback drain keep their existing behavior.
|
|
90
|
+
|
|
91
|
+
## Gateway credential selection
|
|
92
|
+
|
|
93
|
+
The connection reads only the named variable from the selected file. Existing main-checkout and worktree path rules apply. For Gateway connections, the adapter supplies the selected credential through the fx subprocess environment, fixes the native provider to AI Gateway, and removes competing credential variables and endpoint overrides. It does not save the key in native settings or place it in process arguments. Native shell processes can inherit that environment, so the credential is available within the native process tree. ACP does not provide an isolated credential channel for this release. Credential isolation from native tools belongs to the external harness or execution environment; this adapter does not provide it.
|
|
49
94
|
|
|
50
95
|
Native authentication preferences can override environment credentials. Startup runs native credential-source introspection with the same environment and requires the expected environment source. A known conflicting native login fails with `ACCESS_UNAVAILABLE` and guidance to select the environment source in fx. An unrecognized or malformed introspection result fails with `PROTOCOL_ERROR`, without trying another route. The adapter never changes native authentication preferences itself. It checks native settings for changes during startup and before each new task; detected changes fail with `INVALID_CONFIG` and require a new session. ACP does not provide atomic, in-process credential-source attestation, so native settings must remain stable while the session is active. OIDC project validation and token-lifetime rules are the same as for other Gateway connections.
|
|
51
96
|
|
|
@@ -59,7 +104,7 @@ Declared custom tools are exposed through an authenticated MCP HTTP endpoint bou
|
|
|
59
104
|
|
|
60
105
|
Native fx 0.0.9 has a verified crash when receiving image-bearing MCP tool results through this ACP/HTTP route. The adapter forwards valid image blocks, but this native release's live image-tool execution is not supported reliably and can fail with `HARNESS_FAILED`. Adding a text label to the image result does not avoid the crash. Text and JSON tools remain supported. Native fx image attachments use a separate path; subharness's CLI currently accepts text prompts only.
|
|
61
106
|
|
|
62
|
-
Native permissions remain authoritative. fx ACP does not expose scoped allow rules equivalent to the Claude adapter's delegation rules. The optional `permissionMode` selects and verifies the native process mode as defined in [Native Permissions](permissions.md). Omission preserves native settings. The library does not infer approval from a permission request.
|
|
107
|
+
Native permissions remain authoritative. fx ACP does not expose scoped allow rules equivalent to the Claude adapter's delegation rules. The optional `permissionMode` selects and verifies the native process mode as defined in [Native Permissions](permissions.md). Omission preserves native settings. The library does not infer approval from a permission request. Supported ACP permission requests expose the native choices through the [structured approval flow](approvals.md); an explicit answer resumes the same operation. Unsupported interactive input and startup-time requests fail with `INPUT_REQUIRED`. Declared subagents use the session launcher and require native permission to execute it and reach the local coordinator. Declaring tools or children does not override a native approval requirement.
|
|
63
108
|
|
|
64
109
|
## Session lifecycle
|
|
65
110
|
|
package/sdk/harnesses.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Harnesses
|
|
2
2
|
|
|
3
|
-
V1 integrates Codex, Claude Code,
|
|
3
|
+
V1 integrates Codex, Claude Code, fx, OpenCode, GitHub Copilot, and Cursor. Compatibility means connecting the external harness, not merely calling its model through an API.
|
|
4
4
|
|
|
5
5
|
The library provides coordination and definitions. Harnesses own inference, native tools, conversation state, and context management. The execution environment provides any sandboxing or worktree isolation.
|
|
6
6
|
|
|
@@ -16,23 +16,23 @@ Automatic fallback ends at task submission. Execution failure, allowance exhaust
|
|
|
16
16
|
|
|
17
17
|
## Capabilities
|
|
18
18
|
|
|
19
|
-
| Operation | Codex | Claude Code | fx |
|
|
20
|
-
| --- | --- | --- | --- |
|
|
21
|
-
| Native session follow-ups | Supported | Supported | Supported |
|
|
22
|
-
| Queue between library tasks | Supported | Supported | Supported |
|
|
23
|
-
| Active steering | Native steering | Uses interrupt semantics | Uses interrupt semantics |
|
|
24
|
-
| Interruption | Native
|
|
25
|
-
| Custom tools | Native dynamic tools | Native SDK MCP tools | Private MCP HTTP tools |
|
|
26
|
-
| Declared-child launcher permissions |
|
|
27
|
-
| Prompt-free recovery of failed work | Explicit unsupported result | Explicit unsupported result | Explicit unsupported result |
|
|
28
|
-
| Startup readiness check without a turn | Supported | Supported | Supported |
|
|
19
|
+
| Operation | Codex | Claude Code | fx | OpenCode | Copilot | Cursor |
|
|
20
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
21
|
+
| Native session follow-ups | Supported | Supported | Supported | Supported | Supported | Supported |
|
|
22
|
+
| Queue between library tasks | Supported | Supported | Supported | Supported | Supported | Supported |
|
|
23
|
+
| Active steering | Native steering | Uses interrupt semantics | Uses interrupt semantics | Uses interrupt semantics | Uses interrupt semantics | Uses interrupt semantics |
|
|
24
|
+
| Interruption | Native stop confirmation | Native stop confirmation | Native ACP stop confirmation | Native abort stop confirmation | Native abort stop confirmation | Native cancellation stop confirmation |
|
|
25
|
+
| Custom tools | Native dynamic tools | Native SDK MCP tools | Private MCP HTTP tools | Private MCP tools | Native SDK callbacks | Native SDK custom tools or subscription MCP tools |
|
|
26
|
+
| Declared-child launcher permissions | Effective native policy | Exact session-only launcher rules | Effective native policy | Effective native policy | Effective native policy | Effective native policy |
|
|
27
|
+
| Prompt-free recovery of failed work | Explicit unsupported result | Explicit unsupported result | Explicit unsupported result | Explicit unsupported result | Explicit unsupported result | Explicit unsupported result |
|
|
28
|
+
| Startup readiness check without a turn | Supported | Supported | Supported | Supported | Supported | Supported |
|
|
29
29
|
|
|
30
30
|
`resume` remains a stable command; these adapters return `RECOVERY_UNSUPPORTED` when they cannot resume failed work without replay. The queue remains paused. Unsupported native steering follows the [interrupt contract](message-delivery.md), including cancellation propagation and a new replacement task; output reports the effective mode.
|
|
31
31
|
|
|
32
|
-
All TypeScript constructors require a model.
|
|
32
|
+
All TypeScript constructors require a model. The Codex, Claude Code, and fx direct [CLI harness targets](cli/index.md) may omit it to select and retain a verifiable native default within the authorized access route. OpenCode, Copilot, and Cursor require an explicit CLI model and reject effort. Codex and Claude Code default fast mode to false. fx exposes model and optional native effort. Cursor additionally exposes its optional SDK sandbox mode for API-key access; explicit sandbox settings are unsupported for subscription ACP access. Explicit options are validated where the native interface exposes compatibility. A provider can reject a request after submission; that is an execution failure, not permission to choose another model. Model/provider substitutions detected by an adapter are rejected.
|
|
33
33
|
|
|
34
34
|
Claude fast mode with subscription access is unavailable in v1 because shared agent configuration does not authorize additional subscription spending. Explicit paid API or Gateway access can request it where supported. Native subscription eligibility and provider distribution terms still apply; subscription login is not an account-wide spending cap.
|
|
35
35
|
|
|
36
|
-
See [native adapter behavior](adapter-contract.md) for authentication isolation, native permissions, tool transport, and lifecycle details.
|
|
36
|
+
See [native adapter behavior](adapter-contract.md), [Additional native harnesses](additional-harnesses.md), [OpenCode](opencode.md), [Copilot](copilot.md), and [Cursor](cursor.md) for authentication isolation, native permissions, tool transport, and lifecycle details.
|
|
37
37
|
|
|
38
38
|
Codex permission behavior is version- and environment-dependent. The adapter does not assume that an installed Codex version can accept session-scoped launcher rules. Explicit [permission options](permissions.md) select native sandbox and network settings; the adapter does not broaden them automatically. A native approval request therefore remains possible for declared-child delegation even though the child is authorized by the Subharness definition.
|
package/sdk/index.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SDK and CLI
|
|
2
2
|
|
|
3
|
-
The CLI runs generic Codex, Claude Code, and
|
|
3
|
+
The CLI runs generic Codex, Claude Code, fx, OpenCode, GitHub Copilot, and Cursor agents without definition files. The optional TypeScript SDK defines reusable specialists on those same external harnesses. The CLI coordinates sessions, queues, follow-ups, and nested delegation in caller-supplied working directories.
|
|
4
4
|
|
|
5
5
|
- [Distribution](distribution.md): package identity, local installation, and release boundaries.
|
|
6
6
|
- [Agent skill](agent-skill.md): the installable skill that teaches a coding agent to delegate with the CLI.
|
|
@@ -9,9 +9,11 @@ The CLI runs generic Codex, Claude Code, and fx agents without definition files.
|
|
|
9
9
|
- [Discovery](config.md): repository/global definitions and personal worktree settings.
|
|
10
10
|
- [Personal access](access-config.md): subscription discovery, explicit API keys, and project OIDC.
|
|
11
11
|
- [Native permissions](permissions.md): session permission options, native limits, and background delegation.
|
|
12
|
+
- [Permission requests](approvals.md): request-specific schemas, structured answers, and approval lifecycle.
|
|
12
13
|
- [Harnesses](harnesses.md): selection, capabilities, and fallback boundaries.
|
|
13
14
|
- [CLI](cli/index.md): commands and response waiting.
|
|
14
15
|
- [Output](cli/output.md): compact text and typed JSONL records.
|
|
16
|
+
- [Error diagnostics](diagnostics.md): failure categories, safe context, and recovery guidance.
|
|
15
17
|
- [Sessions](sessions.md): task identity, state, and recovery.
|
|
16
18
|
- [Message delivery](message-delivery.md): queue, steer, interrupt, and cancellation.
|
|
17
19
|
- [Subagents](plugins/sub-agents.md): nested delegation and context boundaries.
|
|
@@ -21,4 +23,4 @@ The CLI runs generic Codex, Claude Code, and fx agents without definition files.
|
|
|
21
23
|
|
|
22
24
|
These documents describe the v1 contract. They do not authorize automatic conversation migration, a custom model harness, sandbox provisioning, or a separate pipeline-definition API.
|
|
23
25
|
|
|
24
|
-
The [fx
|
|
26
|
+
The adapter contracts cover [fx](fx.md), [OpenCode](opencode.md), [GitHub Copilot](copilot.md), and [Cursor](cursor.md). The shared configuration for the three additional harnesses is defined in [Additional native harnesses](additional-harnesses.md). The [repository team](project-team.md) defines the roles used to develop this project.
|
package/sdk/message-delivery.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
Queue dispatch follows task completion, not the return of a CLI command or every native turn. If A returns a response while a reviewer is running, A remains active and B waits. Once descendants and result processing finish, the parent's complete response releases B. The library uses native completion and task relationships, not text classification.
|
|
14
14
|
|
|
15
|
-
A complete response asking a question finishes a task when no descendant work remains. B can then start. The coordinator decides whether its answer should be queued or applied to the current task with another delivery mode. A native approval/input request is different: it has not completed a turn.
|
|
15
|
+
A complete response asking a question finishes a task when no descendant work remains. B can then start. The coordinator decides whether its answer should be queued or applied to the current task with another delivery mode. A native approval/input request is different: it has not completed a turn. Supported native permission requests keep that original turn pending through the [structured approval flow](approvals.md); queued tasks retain their order until it finishes. Unsupported input fails with `INPUT_REQUIRED`. No request is automatically approved.
|
|
16
16
|
|
|
17
17
|
Execution failure pauses pending work. New queued sends remain admissible while dispatch is paused. Their immediate `started` records include `paused: true` and `blockedByTaskId` naming the failed active task, so the caller can inspect it with `subharness status <failed-task-id> --full` and release the queue with `subharness cancel <failed-task-id>`. These fields capture queue state at admission and are omitted when the queue is not paused. A successful explicit native recovery must finish the failed task before pending tasks proceed. Unsupported recovery leaves work paused. Queued command observers may remain waiting until their task starts or is explicitly cancelled. Cancellation must be confirmed before it releases the queue; an unconfirmed stop keeps dispatch paused.
|
|
18
18
|
|
package/sdk/opencode.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# OpenCode adapter
|
|
2
|
+
|
|
3
|
+
The OpenCode adapter runs the installed `opencode` executable in native HTTP server mode on a private loopback endpoint. This adapter supports OpenCode 1.18.32. It checks the executable version before starting the server and rejects other or unverifiable versions with `INVALID_CONFIG`, because configuration isolation depends on that version's native switches. OpenCode owns inference, native tools, conversation state, and compaction. The public configuration and access routes are defined in [additional harnesses](additional-harnesses.md).
|
|
4
|
+
|
|
5
|
+
## Direct API keys
|
|
6
|
+
|
|
7
|
+
An explicit `api-key` connection requires `provider` and `env`; `envFile` and `baseUrl` are optional. Supported single-key providers are `anthropic`, `openai`, `google`, `groq`, `openrouter`, `xai`, `mistral`, `cohere`, `opencode` (Zen), and `opencode-go` (Go). Provider IDs that require additional cloud identity, resource, or region settings are not represented by this single-key connection. Unknown providers fail explicitly instead of trying ambient credentials.
|
|
8
|
+
|
|
9
|
+
The agent model uses the exact native `provider/model` ID and its prefix must equal the connection's provider. OpenRouter model names may contain additional slashes. The installed native catalog must contain the exact model and compatible provider metadata; the adapter does not invent custom model metadata. An optional `baseUrl` overrides the selected catalog provider's endpoint. Without it, the pinned native SDK supplies that provider's endpoint. Startup verifies the explicit provider, token configuration, model, and any endpoint override without generation. Native default endpoints and SDK transformations are not remote executed-model attestation.
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"access": {
|
|
14
|
+
"opencode": [{ "type": "api-key", "provider": "anthropic", "env": "ANTHROPIC_API_KEY" }]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Direct keys retain private configuration, data, state, and cache directories. Only the chosen provider is enabled; its environment-key discovery is disabled and the selected key is supplied explicitly. Saved native auth and default auth plugins are excluded. Both the main and auxiliary model are pinned to the exact native model. Existing Gateway behavior remains available independently.
|
|
20
|
+
|
|
21
|
+
## Native saved logins
|
|
22
|
+
|
|
23
|
+
A `subscription` connection requires `provider: "openai"`, `"github-copilot"`, or `"xai"`, selecting the corresponding built-in native OAuth integration. The user completes OpenCode's native login first. The model uses the same exact `provider/model` syntax as direct access, with a matching provider prefix. Saved API-key and well-known credentials are not treated as OAuth subscriptions. Plan names alone do not establish credential type or eligibility.
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{ "access": { "opencode": [{ "type": "subscription", "provider": "openai" }] } }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This route intentionally uses OpenCode's native persistent XDG data directory because OpenCode stores and refreshes its login there. The native process can access its complete authentication store and persist normal native data, including session history and token refreshes. Subharness does not read, copy, synchronize, or rewrite the credential file. It does not perform login or logout. Config, state, and cache remain private; project/provider configuration discovery remains disabled. Only built-in authentication plugins are enabled, with the explicitly selected inference provider and pinned main/auxiliary model. The subscription route does not offer an endpoint override.
|
|
30
|
+
|
|
31
|
+
Readiness requires the selected connected provider's OAuth plugin configuration and the exact native model. A missing login is unavailable; ambiguous, malformed, or conflicting auth metadata is an error. Detection relies on this pinned release's native provider source and OAuth markers, without extracting tokens or pretending that every native fetch transformation is exposed as an endpoint. A known saved API-key route cannot silently replace the requested OAuth route. Closing removes only adapter-owned private state; it never removes or restores the native persistent data directory.
|
|
32
|
+
|
|
33
|
+
## Gateway isolation
|
|
34
|
+
|
|
35
|
+
For Gateway connections, the public model is a Gateway `creator/model` identifier. The native provider is `vercel`; the adapter supplies the corresponding `vercel/creator/model` native selection. Startup uses a private configuration and private native state directories, enables only the Gateway provider, and pins the main and auxiliary model to the requested identifier. It disables native model fallback, project/provider configuration discovery, default plugins, automatic updates, and sharing so inherited provider configuration cannot change the selected billing route.
|
|
36
|
+
|
|
37
|
+
Repository instruction discovery is restored through the native `instructions` setting without importing repository provider configuration. Starting at the execution directory and stopping at the nearest Git worktree root, the adapter finds `AGENTS.md`, otherwise `CLAUDE.md`, otherwise `CONTEXT.md`, using the first filename category with matches. Matching ancestor files are passed as absolute native instruction paths. Outside Git, only the execution directory is searched. Native per-file instruction handling remains native. Subharness specialist instructions supplement the first task. Native configuration, plugins, and custom native agent definitions excluded by this isolation profile are not copied into the temporary state.
|
|
38
|
+
|
|
39
|
+
Residual home or machine-managed configuration that would still load outside the private directories must be checked before native startup. If it cannot be established compatible with the isolated profile, startup fails with `INVALID_CONFIG`; organizational policy is never bypassed using test-only environment overrides. Missing model metadata is also an error; the adapter does not invent context limits or image capabilities for unknown models.
|
|
40
|
+
|
|
41
|
+
Only the selected Gateway credential is supplied for inference. An API key and an OIDC token retain their distinct Gateway authentication modes; the adapter must not pass an OIDC token as an API key. Configuration files containing credentials have mode `0600` in an owner-only temporary directory. Credentials never appear in command arguments, native diagnostic output, or shared project files. Startup checks the effective provider, endpoint, model, and native configuration before admitting prompts, and rejects incompatible or unverifiable settings. Private files are removed on startup failure and normal close.
|
|
42
|
+
|
|
43
|
+
The server is bound to `127.0.0.1` with a random per-process password, and requests are scoped to the caller's directory. Startup requires health, provider/configuration discovery, and creation of an empty native session. No generation is used to verify readiness or quota. A missing executable is `HARNESS_UNAVAILABLE`; malformed native protocol data is `PROTOCOL_ERROR`.
|
|
44
|
+
|
|
45
|
+
## Tools and permissions
|
|
46
|
+
|
|
47
|
+
Declared tools are exposed through a private authenticated loopback MCP endpoint. Arguments are validated before callbacks run. Text and image content are preserved through MCP. The adapter verifies that the native MCP connection succeeds before submitting work. Tools stop admitting calls during interruption and close, and admitted callbacks must settle before stop is reported.
|
|
48
|
+
|
|
49
|
+
Native permissions are configured to ask. Active native permission requests use the shared approval flow, exposing bounded action context and the native `once`/`reject` decisions. Reusable grants are not exposed because native instance scope can include native child sessions. Startup-time requests and other interactive questions fail with `INPUT_REQUIRED`; no permission is automatically granted. The adapter does not add launcher allow rules. Native denial is passed back to OpenCode without authorizing a replacement operation. OpenCode can retire other pending requests in the same session after a rejection; the adapter reconciles native pending requests and drops late answers to retired operations.
|
|
50
|
+
|
|
51
|
+
## Lifecycle
|
|
52
|
+
|
|
53
|
+
Each Subharness session owns one native conversation and one native server. Follow-up tasks retain that conversation. Every prompt pins the requested provider/model; detectable changes are errors without replay. A task returns the final assistant text only after successful native completion. Native errors, unfinished responses, and malformed terminal data cannot count as success. The final text limit is 1 MiB.
|
|
54
|
+
|
|
55
|
+
Interruption requests native abort and confirms the active work stopped; an accepted HTTP abort request alone is insufficient. It also drains admitted tools and retires outstanding approvals, including when native work ended before interruption began. Native stop confirmation is bounded; that deadline does not limit already-admitted host callbacks, which must settle before interruption completes. Failure to confirm native termination produces `CANCELLATION_FAILED`, and no replacement prompt is admitted to still-running work. Steering uses the shared interrupt behavior; prompt-free recovery is unsupported.
|
|
56
|
+
|
|
57
|
+
Close aborts active work, closes event streams and the native session, terminates the owned server with bounded escalation when needed, and removes private configuration/state and tool endpoints. Startup failures use the same cleanup discipline. Normal cleanup never deletes project files or user-owned OpenCode settings.
|