subharness 0.0.1
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/LICENSE +21 -0
- package/README.md +158 -0
- package/dist/adapters/claude-auth.d.ts +7 -0
- package/dist/adapters/claude-auth.js +87 -0
- package/dist/adapters/claude-auth.js.map +1 -0
- package/dist/adapters/claude-config.d.ts +6 -0
- package/dist/adapters/claude-config.js +37 -0
- package/dist/adapters/claude-config.js.map +1 -0
- package/dist/adapters/claude-input.d.ts +9 -0
- package/dist/adapters/claude-input.js +32 -0
- package/dist/adapters/claude-input.js.map +1 -0
- package/dist/adapters/claude-permissions.d.ts +3 -0
- package/dist/adapters/claude-permissions.js +24 -0
- package/dist/adapters/claude-permissions.js.map +1 -0
- package/dist/adapters/claude-settings.d.ts +14 -0
- package/dist/adapters/claude-settings.js +43 -0
- package/dist/adapters/claude-settings.js.map +1 -0
- package/dist/adapters/claude-tools.d.ts +9 -0
- package/dist/adapters/claude-tools.js +47 -0
- package/dist/adapters/claude-tools.js.map +1 -0
- package/dist/adapters/claude.d.ts +22 -0
- package/dist/adapters/claude.js +295 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex-config.d.ts +25 -0
- package/dist/adapters/codex-config.js +66 -0
- package/dist/adapters/codex-config.js.map +1 -0
- package/dist/adapters/codex.d.ts +2 -0
- package/dist/adapters/codex.js +238 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/fx-auth.d.ts +4 -0
- package/dist/adapters/fx-auth.js +116 -0
- package/dist/adapters/fx-auth.js.map +1 -0
- package/dist/adapters/fx-rpc.d.ts +29 -0
- package/dist/adapters/fx-rpc.js +124 -0
- package/dist/adapters/fx-rpc.js.map +1 -0
- package/dist/adapters/fx-tools.d.ts +19 -0
- package/dist/adapters/fx-tools.js +119 -0
- package/dist/adapters/fx-tools.js.map +1 -0
- package/dist/adapters/fx.d.ts +3 -0
- package/dist/adapters/fx.js +302 -0
- package/dist/adapters/fx.js.map +1 -0
- package/dist/adapters/mcp-tool-content.d.ts +12 -0
- package/dist/adapters/mcp-tool-content.js +8 -0
- package/dist/adapters/mcp-tool-content.js.map +1 -0
- package/dist/adapters/rpc.d.ts +28 -0
- package/dist/adapters/rpc.js +101 -0
- package/dist/adapters/rpc.js.map +1 -0
- package/dist/adapters/types.d.ts +28 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli/args.d.ts +18 -0
- package/dist/cli/args.js +118 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/catalog-worker.d.ts +1 -0
- package/dist/cli/catalog-worker.js +22 -0
- package/dist/cli/catalog-worker.js.map +1 -0
- package/dist/cli/catalog.d.ts +26 -0
- package/dist/cli/catalog.js +41 -0
- package/dist/cli/catalog.js.map +1 -0
- package/dist/cli/help.d.ts +3 -0
- package/dist/cli/help.js +68 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/input.d.ts +5 -0
- package/dist/cli/input.js +69 -0
- package/dist/cli/input.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +105 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.d.ts +7 -0
- package/dist/cli/output.js +43 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/config/access.d.ts +21 -0
- package/dist/config/access.js +97 -0
- package/dist/config/access.js.map +1 -0
- package/dist/config/loader.d.ts +13 -0
- package/dist/config/loader.js +80 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/oidc.d.ts +1 -0
- package/dist/config/oidc.js +53 -0
- package/dist/config/oidc.js.map +1 -0
- package/dist/config/project.d.ts +5 -0
- package/dist/config/project.js +56 -0
- package/dist/config/project.js.map +1 -0
- package/dist/config/resolve-access.d.ts +4 -0
- package/dist/config/resolve-access.js +42 -0
- package/dist/config/resolve-access.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +14 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/client.d.ts +9 -0
- package/dist/runtime/client.js +91 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/coordinator.d.ts +61 -0
- package/dist/runtime/coordinator.js +296 -0
- package/dist/runtime/coordinator.js.map +1 -0
- package/dist/runtime/daemon.d.ts +1 -0
- package/dist/runtime/daemon.js +28 -0
- package/dist/runtime/daemon.js.map +1 -0
- package/dist/runtime/definition.d.ts +6 -0
- package/dist/runtime/definition.js +19 -0
- package/dist/runtime/definition.js.map +1 -0
- package/dist/runtime/native-owner.d.ts +8 -0
- package/dist/runtime/native-owner.js +25 -0
- package/dist/runtime/native-owner.js.map +1 -0
- package/dist/runtime/observation.d.ts +1 -0
- package/dist/runtime/observation.js +15 -0
- package/dist/runtime/observation.js.map +1 -0
- package/dist/runtime/select-native.d.ts +3 -0
- package/dist/runtime/select-native.js +34 -0
- package/dist/runtime/select-native.js.map +1 -0
- package/dist/runtime/service.d.ts +5 -0
- package/dist/runtime/service.js +90 -0
- package/dist/runtime/service.js.map +1 -0
- package/dist/runtime/session-launcher.d.ts +6 -0
- package/dist/runtime/session-launcher.js +29 -0
- package/dist/runtime/session-launcher.js.map +1 -0
- package/dist/runtime/state.d.ts +14 -0
- package/dist/runtime/state.js +69 -0
- package/dist/runtime/state.js.map +1 -0
- package/dist/runtime/types.d.ts +81 -0
- package/dist/runtime/types.js +2 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/runtime/worker-client.d.ts +3 -0
- package/dist/runtime/worker-client.js +81 -0
- package/dist/runtime/worker-client.js.map +1 -0
- package/dist/runtime/worker.d.ts +1 -0
- package/dist/runtime/worker.js +67 -0
- package/dist/runtime/worker.js.map +1 -0
- package/dist/sdk/definitions.d.ts +6 -0
- package/dist/sdk/definitions.js +72 -0
- package/dist/sdk/definitions.js.map +1 -0
- package/dist/sdk/tool-result.d.ts +23 -0
- package/dist/sdk/tool-result.js +95 -0
- package/dist/sdk/tool-result.js.map +1 -0
- package/dist/sdk/tools.d.ts +9 -0
- package/dist/sdk/tools.js +73 -0
- package/dist/sdk/tools.js.map +1 -0
- package/dist/sdk/types.d.ts +49 -0
- package/dist/sdk/types.js +2 -0
- package/dist/sdk/types.js.map +1 -0
- package/dist/sdk/validation.d.ts +5 -0
- package/dist/sdk/validation.js +20 -0
- package/dist/sdk/validation.js.map +1 -0
- package/dist/shell.d.ts +2 -0
- package/dist/shell.js +5 -0
- package/dist/shell.js.map +1 -0
- package/package.json +61 -0
- package/sdk/access-config.md +52 -0
- package/sdk/adapter-contract.md +33 -0
- package/sdk/agent.md +52 -0
- package/sdk/authentication.md +11 -0
- package/sdk/cli/index.md +67 -0
- package/sdk/cli/output.md +28 -0
- package/sdk/completion-notifications.md +29 -0
- package/sdk/config.md +41 -0
- package/sdk/distribution.md +31 -0
- package/sdk/fx.md +71 -0
- package/sdk/harnesses.md +32 -0
- package/sdk/index.md +22 -0
- package/sdk/message-delivery.md +39 -0
- package/sdk/plugins/sub-agents.md +57 -0
- package/sdk/project-team.md +57 -0
- package/sdk/sessions.md +31 -0
- package/sdk/tools.md +73 -0
- package/sdk/v1-runtime.md +63 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/sdk/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,CAAC,MAAM,WAAW,GAAG,+BAA+B,CAAC;AAE3D,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,MAAM,IAAI,UAAU,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,KAAc,EAAE,MAA0B;IAC/D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,kCAAkC,CAAC,CAAC;IAC7G,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;AACjH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc,EAAE,KAAa;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QAAE,OAAO,CAAC,GAAG,KAAK,6BAA6B,CAAC,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,iGAAiG,CAAC,CAAC;AACxK,CAAC","sourcesContent":["import { AgentError } from \"../errors.js\";\n\nexport const namePattern = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;\n\nexport function invalid(message: string): never {\n throw new AgentError(\"INVALID_DEFINITION\", message);\n}\n\nexport function object(value: unknown, fields?: readonly string[]): asserts value is Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) invalid(\"Expected a configuration object.\");\n if (fields && Object.keys(value).some((key) => !fields.includes(key))) invalid(\"Unknown configuration field.\");\n}\n\nexport function nonempty(value: unknown, label: string): asserts value is string {\n if (typeof value !== \"string\" || !value.trim()) invalid(`${label} must be a nonempty string.`);\n}\n\nexport function validName(value: unknown): asserts value is string {\n if (typeof value !== \"string\" || !namePattern.test(value)) invalid(\"Names must start with a letter and contain at most 64 letters, digits, underscores, or hyphens.\");\n}\n"]}
|
package/dist/shell.d.ts
ADDED
package/dist/shell.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC;AACpD,CAAC","sourcesContent":["/** Quote one literal argument for the native POSIX shell. */\nexport function shellQuote(value: string): string {\n return \"'\" + value.replaceAll(\"'\", \"'\\\\''\") + \"'\";\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "subharness",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"workspaces": [
|
|
5
|
+
"apps/docs"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.18.0"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"bin": {
|
|
18
|
+
"subharness": "./dist/cli/main.js",
|
|
19
|
+
"agent": "./dist/cli/main.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"sdk",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -p tsconfig.build.json && node --input-type=module -e \"import { chmodSync } from 'node:fs'; chmodSync('dist/cli/main.js', 0o755)\"",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"test": "node --import tsx --test test/*.test.ts",
|
|
30
|
+
"check": "npm run build && npm run typecheck && npm test",
|
|
31
|
+
"dev:site": "npm run dev --workspace @subharness/docs --",
|
|
32
|
+
"build:site": "npm run build --workspace @subharness/docs",
|
|
33
|
+
"check:site": "npm run check --workspace @subharness/docs",
|
|
34
|
+
"prepack": "npm run build",
|
|
35
|
+
"check:package": "node --test test/package-consumer/package.test.mjs"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.278",
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
40
|
+
"tsx": "4.23.15",
|
|
41
|
+
"zod": "4.6.5"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^22.0.0",
|
|
45
|
+
"typescript": "7.0.2"
|
|
46
|
+
},
|
|
47
|
+
"description": "Run native coding agents and coordinate optional specialists through a local CLI.",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/vercel-labs/subharness.git"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/vercel-labs/subharness#readme",
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/vercel-labs/subharness/issues"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public",
|
|
59
|
+
"registry": "https://registry.npmjs.org/"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Personal Access Configuration
|
|
2
|
+
|
|
3
|
+
The optional `.agents/agents.local.json` file selects access independently of versioned agent definitions. In linked Git worktrees it is read from the main checkout. Without Git, it is read from the execution project root. Global definitions still use the selected project's access preferences.
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"access": {
|
|
8
|
+
"codex": [
|
|
9
|
+
{ "type": "subscription" },
|
|
10
|
+
{ "type": "vercel-api-key", "env": "AI_GATEWAY_API_KEY" }
|
|
11
|
+
],
|
|
12
|
+
"claudeCode": [
|
|
13
|
+
{
|
|
14
|
+
"type": "vercel-oidc",
|
|
15
|
+
"project": ".",
|
|
16
|
+
"envFile": ".env.local"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This example explicitly enables subscription-to-Gateway fallback for Codex before submission. It enables only project OIDC access for Claude Code. Selecting an access method is authorization to use that billing source, subject to native account controls. It does not log in or create credentials.
|
|
24
|
+
|
|
25
|
+
## Connections
|
|
26
|
+
|
|
27
|
+
| `type` | Credential source | Default `env` |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `subscription` | The harness's current native subscription login | Not applicable |
|
|
30
|
+
| `api-key` | Direct native provider API key | `OPENAI_API_KEY` for Codex, `ANTHROPIC_API_KEY` for Claude Code |
|
|
31
|
+
| `vercel-api-key` | AI Gateway API key | `AI_GATEWAY_API_KEY` |
|
|
32
|
+
| `vercel-oidc` | Project OIDC token | `VERCEL_OIDC_TOKEN` |
|
|
33
|
+
|
|
34
|
+
`subscription` accepts only `type`. Other connections optionally accept `env` and `envFile`. `vercel-oidc` also accepts `project`, defaulting to `.`. Unknown fields are errors. Variable names match `[A-Za-z_][A-Za-z0-9_]*`. Tokens and API keys must never appear directly in this file.
|
|
35
|
+
|
|
36
|
+
Without `envFile`, the named variable is read from the invoking environment. With `envFile`, only that variable is read from that explicit UTF-8 dotenv file; its other values are not imported, and the process environment does not override the selected file. File and project paths resolve relative to the main checkout, or project root outside Git. Shell expansion and command execution in dotenv values are not supported. Sources are read at native session startup; changing a file does not refresh a token already supplied to a running harness.
|
|
37
|
+
|
|
38
|
+
Omitting `codex` or `claudeCode` retains native subscription discovery. A nonempty array replaces discovery with the listed connections in order. An empty array disables that harness. Supported keys are `codex`, `claudeCode`, and `fx`. Omitting `fx` does not enable any connection because its adapter requires explicit Gateway access; see [fx access](fx.md). Missing credentials make a connection unavailable before submission; malformed settings, malformed tokens, expired tokens, and project mismatches are errors. Only known unavailability permits advancing to another explicitly enabled connection or harness. Failures after submission never automatically migrate or replay the task.
|
|
39
|
+
|
|
40
|
+
## OIDC project selection
|
|
41
|
+
|
|
42
|
+
The selected project directory must contain `.vercel/project.json` with `projectId` and `orgId`. In a deployment without local linkage, `VERCEL_PROJECT_ID` and `VERCEL_ORG_ID` identify the expected project and organization when `project` is omitted. A supplied `project` always requires its explicit linkage. Token claims must match the expected project and organization and be within their validity interval. Local claim checks prevent accidental selection errors; the Gateway authenticates the token.
|
|
43
|
+
|
|
44
|
+
The library does not infer the expected project from the token itself, search other applications in a monorepo, or fall back to an unrelated API key. Refreshing OIDC credentials remains the provider/environment's responsibility. An expired running credential produces an execution failure; native recovery is available only where the adapter can actually continue that failed task. This version does not promise transparent token rotation in an existing native process.
|
|
45
|
+
|
|
46
|
+
Gateway API keys and OIDC are distinct routes. An API key does not acquire project attribution merely because the execution directory contains a Vercel project. Project OIDC carries the selected project identity; billing and cost reporting remain with the provider.
|
|
47
|
+
|
|
48
|
+
## Local file management
|
|
49
|
+
|
|
50
|
+
The file can be edited directly; no library login wizard is required. Before using an existing personal settings file in Git, the CLI ensures `/.agents/agents.local.json` is excluded through the repository's local Git exclude file. A tracked personal settings file is rejected with instructions to remove it from the index. The library never copies secrets or personal settings into worktrees.
|
|
51
|
+
|
|
52
|
+
Native login remains external: the CLI reports which harness needs login when no eligible access exists. It never extracts a subscription token for use by another harness. Explicit API/Gateway startup prevents ambient credentials from silently selecting a competing billing route. Native subscription access and accelerated modes remain subject to provider eligibility and spending controls; the library is not a spending cap.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Native Adapter Contract
|
|
2
|
+
|
|
3
|
+
Adapters create native Codex, Claude Code, or fx conversations in the caller-supplied directory. Their implementation interface is internal and is not an SDK extension API. A session supplies `turn(prompt)`, `steer(prompt)`, `interrupt()`, `resume()`, and `close()`. A turn resolves with its complete text; execution failures reject with a stable error code. Interrupt resolves only when active execution has stopped. Unsupported operations return explicit capability errors. The [fx contract](fx.md) specifies its native ACP transport, Gateway access, and capability limits.
|
|
4
|
+
|
|
5
|
+
Adapter startup receives the selected harness configuration, loaded agent definition, execution directory, environment, and resolved personal access. Startup verifies compatibility and access before submitting any task. A direct CLI harness target supplies no specialist instructions, custom tools, or declared children. Its internal execution configuration may request a native default model; public SDK constructors still require explicit models. Default resolution happens before the first prompt, within the authorized access route, and the resolved model is retained for follow-ups. An unavailable or unverifiable default fails with guidance to supply `--model`, without probing it through a paid generation. Explicit model and effort validation remains effective. `HARNESS_UNAVAILABLE` and `ACCESS_UNAVAILABLE` permit trying another declared alternative before submission. Invalid configuration and unsupported explicit options do not. No adapter performs automatic fallback after turn submission.
|
|
6
|
+
|
|
7
|
+
Agent instructions supplement native instructions. The adapter exposes declared tools without implementing its own model loop. Tool input is validated before the function runs, and returned text or JSON data is encoded as a native text result. Explicit `toolResult` values preserve their text and image blocks through the native protocol, as defined in [custom tools](tools.md). Declared subagents authorize delegation. The Claude adapter translates that authorization into session-only native allow rules for the exact session launcher, declared child invocations, and documented coordination commands. These rules supplement custom-tool permissions without changing the permission mode or stored native settings; native deny rules, explicit approval requirements, managed policy, and sandbox restrictions remain authoritative. Native approval or interactive-input requests that still require a response from this library fail with `INPUT_REQUIRED`; the permission callback never grants them automatically.
|
|
8
|
+
|
|
9
|
+
Codex uses its native App Server protocol. Claude Code uses its native Agent SDK with the installed Claude executable. A Claude session accepts queued native turns and interruption. Its native steering method reports `UNSUPPORTED_DELIVERY` before any mutation; the coordinator converts that request to the documented interrupt operation and reports the effective delivery mode. Codex steering targets the active native turn. Neither adapter synthesizes native recovery by replaying the original prompt; unavailable recovery returns `RECOVERY_UNSUPPORTED`.
|
|
10
|
+
|
|
11
|
+
Personal subscription selection must verify native subscription access. Ambient API keys, alternate endpoints, provider overrides, and native API-key helpers must not silently change the selected billing method. Explicit API or Gateway selections supply only the selected credential route. Credentials are never included in CLI records or diagnostic output.
|
|
12
|
+
|
|
13
|
+
When Claude Code requires a tool approval, `INPUT_REQUIRED` identifies the native tool if its name is a bounded, valid identifier. Tool arguments, command text, settings, and credentials are not included. Other interactive requests retain a generic actionable message. This diagnostic identifies what requires native configuration without granting approval or changing permission rules.
|
|
14
|
+
|
|
15
|
+
Codex custom tools use the native `subharness` namespace while retaining the declared tool map keys. Explicit API/Gateway model identifiers need not appear in a subscription model catalog. Adapters disable provider model fallback and reject a detectable replacement of the requested model; an upstream rejection after submission is an execution failure. Codex explicitly selects standard service when `fast` is false, rather than inheriting a native accelerated preference.
|
|
16
|
+
|
|
17
|
+
Claude accelerated mode with subscription access is rejected in v1 because it requires additional spending that shared agent definitions do not authorize. It can be requested through an explicitly selected paid API or Gateway route where supported. This does not turn the library into an account-wide spending cap.
|
|
18
|
+
|
|
19
|
+
## Native default selection
|
|
20
|
+
|
|
21
|
+
Direct CLI targets resolve omitted models through the native session's effective configuration before submitting user input. Codex uses the model returned by App Server `thread/start`, verifies the selected provider and subscription catalog when applicable, and retains that model in subsequent turns. fx uses the Gateway provider and model reported by ACP `session/new`, then verifies any explicit effort through the existing configuration control.
|
|
22
|
+
|
|
23
|
+
Claude Code resolves its effective model through a capability-checked native settings control. Only applied model and effort fields are retained; merged settings and credential-bearing source details are never logged or included in output. The adapter pins the resolved model for the session through the native model control and verifies the applied result before input is admitted. If the installed SDK or executable cannot expose a verifiable applied model before submission, omitted-model startup fails with `INVALID_CONFIG` and guidance to supply `--model`. Explicit models retain compatibility with native versions that lack that introspection, along with the existing catalog and initialization checks.
|
|
24
|
+
|
|
25
|
+
Native model aliases and their concrete identifiers are equivalent only when the native catalog supplies that mapping. This applies to explicit and default selections, including initialization and follow-up checks. A missing settings control or a recognized unsupported-control response permits the older explicit-model path; an unrelated control failure does not silently disable verification. Omitted effort inherits native configuration, including non-credential effort environment preferences, unless an explicit CLI or SDK option overrides it.
|
|
26
|
+
|
|
27
|
+
Unavailable or unverifiable default selection uses `INVALID_CONFIG`, with guidance to select an explicit model. It is not classified as an eligible access fallback. Authentication, provider, and malformed protocol failures retain their specific errors. An explicit effort that a native interface demonstrably changes or rejects fails with `UNSUPPORTED_OPTION` or the adapter's existing configuration error before submission. A selected model does not prove remote allowance or provider availability; an upstream rejection remains an execution failure without replay.
|
|
28
|
+
|
|
29
|
+
When every eligible connection or declared harness is unavailable, selection reports `ACCESS_UNAVAILABLE` with per-harness explanations, including missing executable guidance. An explicitly empty access list identifies the disabled harness and personal access configuration in its explanation. This aggregate outcome does not imply that each underlying failure was an authentication failure.
|
|
30
|
+
|
|
31
|
+
An adapter may create a private temporary native configuration file outside the repository to pass explicit API/Gateway credentials without putting secrets in process arguments. The directory uses owner-only access and the file uses mode `0600`. Startup failures and normal session close remove the file. Project settings and coordinator endpoint records never contain those credentials, and subscription tokens are never extracted. Abrupt environment termination does not guarantee cleanup of native temporary files.
|
|
32
|
+
|
|
33
|
+
Cancellation waits for custom tool callbacks already admitted to settle before reporting execution stopped. `execute(input)` has no cancellation signal. The library cannot undo their completed side effects or truthfully report a still-running callback as cancelled.
|
package/sdk/agent.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Defining an Agent
|
|
2
|
+
|
|
3
|
+
Import definitions from `subharness`. The helpers are synchronous declarations: they do not start harnesses, authenticate, call models, or execute tools. Execution uses the CLI.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { agent, codex, claudeCode } from "subharness";
|
|
7
|
+
|
|
8
|
+
export default agent({
|
|
9
|
+
name: "developer",
|
|
10
|
+
description: "Implements repository features and fixes.",
|
|
11
|
+
instructions: "Follow repository conventions and verify the changes.",
|
|
12
|
+
harness: [
|
|
13
|
+
codex({ model: "CODEX_MODEL_ID", effort: "high" }),
|
|
14
|
+
claudeCode({ model: "CLAUDE_MODEL_ID", effort: "high" }),
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Model identifiers in examples are placeholders for models available through the selected harness and access method.
|
|
20
|
+
|
|
21
|
+
| Field | Required | Meaning |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| `name: string` | Yes | Stable discovery name, independent of filename |
|
|
24
|
+
| `description: string` | Yes | Description used by callers to choose an agent |
|
|
25
|
+
| `instructions: string` | Yes | Additional instructions supplied to the native harness |
|
|
26
|
+
| `harness` | Yes | One harness configuration or a nonempty ordered array |
|
|
27
|
+
| `tools` | No | Map of names to [custom tool definitions](tools.md) |
|
|
28
|
+
| `subagents` | No | Map of local names to [agent definitions](plugins/sub-agents.md) |
|
|
29
|
+
|
|
30
|
+
Descriptions, instructions, and model identifiers must contain text. Names and map keys match `[a-zA-Z][a-zA-Z0-9_-]{0,63}`. Unknown fields, empty harness arrays, and recursive subagent references are errors. Omitting tools or subagents adds none through the library; it does not disable native capabilities.
|
|
31
|
+
|
|
32
|
+
`AgentDefinition` exposes readonly configuration fields without execution methods. Functions and schemas make definitions unsuitable as a JSON serialization format. Shared definitions do not contain credentials, provider clients, storage, or lifecycle plugins.
|
|
33
|
+
|
|
34
|
+
Instructions supplement native harness guidance and repository instructions. Authors can compose the instruction string with ordinary TypeScript. Each discovered `*.agent.ts` file default-exports one definition; helper files are not independently registered.
|
|
35
|
+
|
|
36
|
+
## Reusing skills
|
|
37
|
+
|
|
38
|
+
A skill is a task-specific instruction file, conventionally named `SKILL.md`, that an agent reads when needed. Keep a role's stable responsibility in `instructions` and reference relevant skill paths in the task or repository guidance. This avoids creating another agent definition for every library or technique.
|
|
39
|
+
|
|
40
|
+
Subharness does not expose a `skills` field, install skills, or normalize native skill discovery. The selected harness owns its discovery rules and file-reading tools. A task can explicitly ask the agent to read an accessible skill file using ordinary file reading. A filesystem path is not a portable argument to a harness's native skill command, which may expect a registered skill name instead. Reading a skill does not grant tools, permissions, or subagent access.
|
|
41
|
+
|
|
42
|
+
Children do not inherit loaded skill contents or the parent's transcript. Include the relevant paths and governing contracts in each child's task. The [repository team](project-team.md) demonstrates this convention with a small set of roles and on-demand repository skills.
|
|
43
|
+
|
|
44
|
+
## Harness constructors
|
|
45
|
+
|
|
46
|
+
`codex(options)` and `claudeCode(options)` return configurations. Both require `model`, accept native `effort`, and accept `fast?: boolean`, defaulting to `false`. Options are typed independently to preserve each harness's capabilities. Codex effort values depend on the native model catalog; Claude effort accepts `low`, `medium`, `high`, `xhigh`, and `max`, subject to native model restrictions.
|
|
47
|
+
|
|
48
|
+
`fx(options)` requires `model` and accepts optional native `effort`. It has no `fast` option. Its readonly configuration has `kind: "fx"`; see the [fx contract](fx.md) for Gateway access, native capabilities, and permission limits. `HarnessConfig` is the union of the native harness configurations, so narrowing by `kind` exposes the options supported by that harness.
|
|
49
|
+
|
|
50
|
+
The package exports `CodexOptions`, `ClaudeCodeOptions`, and `FxOptions` together with their readonly return types `CodexConfig`, `ClaudeCodeConfig`, and `FxConfig`. Runtime validation also enforces Claude's listed effort values when TypeScript checking is absent; unsupported values fail with `INVALID_DEFINITION` before native execution.
|
|
51
|
+
|
|
52
|
+
The `harness` property always uses that name, even for an array. Alternatives are considered in declaration order; an array does not mean parallel execution. [Personal access settings](access-config.md) determine which connections are eligible without editing a shared agent.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Authentication and Cost Attribution
|
|
2
|
+
|
|
3
|
+
Login belongs to the selected native harness or access provider. The library selects compatible access at startup and does not create a separate subscription account or transfer subscription tokens between harnesses.
|
|
4
|
+
|
|
5
|
+
Without personal settings, the library discovers eligible native subscription access for the agent's declared harness alternatives. An API credential in the environment does not enable API billing. If the integration cannot establish that a native login uses subscription access, it reports that access is unavailable instead of assuming OAuth implies a subscription.
|
|
6
|
+
|
|
7
|
+
Users can mix subscription, direct API, Gateway API-key, and Gateway OIDC access in one project. Preferences belong to the user, separately from versioned agent definitions. Two contributors can run the same definition through different declared harness alternatives and compatible connections.
|
|
8
|
+
|
|
9
|
+
The final configuration shape, credential references, worktree resolution, fallback rules, and OIDC project selection are defined in [Personal Access Configuration](access-config.md). No new library login is required when an eligible native subscription is already configured.
|
|
10
|
+
|
|
11
|
+
Selecting an explicit API/Gateway connection authorizes that billing source. API-key access and project OIDC are distinct: linkage of a directory alone does not attribute API-key spending to a project. The selected provider handles accounting and token renewal. The library does not promise transparent rotation in an already-running native process or automatic migration after a billing failure.
|
package/sdk/cli/index.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# CLI
|
|
2
|
+
|
|
3
|
+
The CLI is the primary execution interface for an everyday coding assistant. The assistant can invoke a native harness directly or a reusable specialist defined with the SDK, run commands through its host's background-task controls, and use identifiers for later interactions. `subharness` is the primary executable; `agent` is a compatibility alias with identical behavior.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
subharness list [--cwd <directory>]
|
|
7
|
+
subharness run <target> [--cwd <directory>] <prompt>
|
|
8
|
+
subharness run <target> [--cwd <directory>] --prompt <text>
|
|
9
|
+
subharness run <target> [--cwd <directory>] --prompt-file <path|->
|
|
10
|
+
subharness run <harness> [--model <model>] [--effort <effort>] <prompt>
|
|
11
|
+
subharness run <harness> --help
|
|
12
|
+
subharness send <session-id> [--delivery queue|steer|interrupt] <prompt>
|
|
13
|
+
subharness send <session-id> [--delivery queue|steer|interrupt] --prompt <text>
|
|
14
|
+
subharness send <session-id> [--delivery queue|steer|interrupt] --prompt-file <path|->
|
|
15
|
+
subharness wait <task-id> --after <response-id>
|
|
16
|
+
subharness status <task-id> [--full]
|
|
17
|
+
subharness queue <session-id>
|
|
18
|
+
subharness cancel <task-id>
|
|
19
|
+
subharness resume <session-id>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Execution commands accept `--format text|jsonl`; text is the default. `--help` and `--version` are plain-text information modes without a `--format` option. `subharness --help` and `<command> --help` show usage without requiring execution arguments or starting execution. Direct harness help includes that harness’s options; other command help shows the general usage. [Output records and exit codes](output.md) define the integration format.
|
|
23
|
+
|
|
24
|
+
`list` includes the built-in harness targets and discovered definitions without starting harnesses or calling models. Inclusion is not a claim that an executable, eligible account, or particular model is available. Definition loading retains its existing validation and trust requirements. `run` starts a new session and its first task, immediately prints their identities, and waits for one complete response or terminal outcome. It returns control even if that task still has delegated work. The result contains a response identifier and task state.
|
|
25
|
+
|
|
26
|
+
## Direct harnesses and specialists
|
|
27
|
+
|
|
28
|
+
`codex`, `claude`, and `fx` are reserved, case-sensitive target names for direct harness execution. `claude` selects the Claude Code adapter, whose SDK and access configuration key remains `claudeCode`. Direct execution needs an installed native harness and eligible access, but no TypeScript definition, project-local SDK import, or agent catalog evaluation. An invalid specialist file does not block a direct harness run.
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
subharness run claude "Review the current diff."
|
|
32
|
+
subharness run codex --cwd ../feature-worktree "Implement the documented validation."
|
|
33
|
+
subharness run fx --model "provider/model" "Compare the proposed implementations."
|
|
34
|
+
subharness run repo:reviewer "Review the current diff."
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Replace `provider/model` with an available Gateway model identifier. Nonreserved bare names retain the existing unambiguous specialist lookup. Qualified `repo:`, `global:`, and `subagent:` names retain their existing meanings. A specialist named `claude`, `codex`, or `fx` requires its scope qualifier; it never shadows the built-in target. Unknown targets fail rather than being executed as arbitrary commands. Omitting the target is an argument error; the CLI never chooses the first installed harness.
|
|
38
|
+
|
|
39
|
+
Direct harness sessions use native instructions and project context without adding a specialist role, custom tools, or declared children. They preserve the existing authentication, native permission, queue, cancellation, follow-up, and response contracts. They do not broaden the caller's native permissions or automatically authorize additional delegation.
|
|
40
|
+
|
|
41
|
+
`--model` and `--effort` configure direct harness targets only, and can be combined with any supported prompt source and `--cwd`. Empty values are errors. Specialist targets reject these overrides and retain their declared harness configurations. `send` retains the session configuration and does not accept model or effort overrides.
|
|
42
|
+
|
|
43
|
+
Omitting `--model` requests the native default for the authorized access route at session creation. The adapter retains the selected model for that conversation. It does not inherit the calling agent's model, rank models, choose a substitute, or change billing routes. An unavailable or unverifiable native default fails with actionable guidance to supply `--model`; it does not submit a prompt merely to discover a default. Explicit model identifiers retain native validation and substitution checks. TypeScript harness constructors continue to require a model.
|
|
44
|
+
|
|
45
|
+
Effort is harness-specific: Codex and fx accept native effort identifiers, while Claude Code accepts `low`, `medium`, `high`, `xhigh`, or `max`. Explicit effort must be compatible with the selected model where native capabilities expose that validation. An omitted effort retains the native default at session creation. Detected changes to an explicit request are errors. Codex and Claude Code retain standard-speed execution; this interface adds no fast-mode flag. fx retains its documented native preference and Gateway access contract.
|
|
46
|
+
|
|
47
|
+
`subharness run codex --help`, `subharness run claude --help`, and `subharness run fx --help` describe the relevant options and access requirements without loading definitions, starting a coordinator, or invoking a harness. Native CLI flags are not forwarded. Unsupported flags are errors.
|
|
48
|
+
|
|
49
|
+
`wait` observes the next response after a returned response identifier. It returns an already-available response immediately or waits for another response or terminal outcome. It creates no work, consumes no responses, and does not restart the harness. Multiple readers can use independent cursors. Unknown or cross-task response identifiers are errors.
|
|
50
|
+
|
|
51
|
+
`status` returns a nonblocking snapshot with a bounded latest-response preview; `--full` retrieves the complete latest response. `queue` shows active and pending work in order. `cancel` waits for cancellation of the targeted task and its delegated descendants, without removing independently queued tasks. `resume` requests native recovery of a failed task without new input; unsupported recovery reports an error and leaves the queue paused.
|
|
52
|
+
|
|
53
|
+
## Input and delivery
|
|
54
|
+
|
|
55
|
+
`run` and `send` require exactly one prompt source: one positional text argument, `--prompt <text>`, or `--prompt-file <path|->`. Quote a positional prompt containing spaces; extra positional arguments are errors rather than implicitly joined text. `--` ends option parsing and allows a positional prompt beginning with a dash. It never introduces native harness arguments.
|
|
56
|
+
|
|
57
|
+
`--prompt-file -` reads standard input to EOF. It is explicit: piped stdin is not read or appended when another prompt source is supplied. Interactive terminal stdin is rejected instead of waiting for a conversation. Missing, empty, invalid UTF-8, and oversized input fail before session admission. File and stdin diagnostics distinguish read failures from invalid UTF-8 content. Files and stdin are bounded while reading to 1 MiB. File paths resolve relative to the calling command's directory, independently of `--cwd`. Omitted `--cwd` uses the calling directory. Follow-ups retain their session's execution directory.
|
|
58
|
+
|
|
59
|
+
`send` defaults to `queue`, creating a new task that waits for the active task's full completion boundary. `steer` targets whichever task is active when handled. If that task is between native turns, steering starts a continuation of the same task. If an actively generating harness lacks steering, the operation follows `interrupt` semantics and reports the effective mode. `interrupt` cancels affected work, waits for stop confirmation, and starts a replacement before independently queued tasks. See [message delivery](../message-delivery.md).
|
|
60
|
+
|
|
61
|
+
Native steering returns an acceptance acknowledgement. Queued and interrupting sends return their task's first complete response or terminal outcome; they do not wait for the whole queue. There is no expected-task guard. If B starts before a correction is handled, steering targets B.
|
|
62
|
+
|
|
63
|
+
## Nested execution
|
|
64
|
+
|
|
65
|
+
A managed agent invokes its own declared children with `subharness run subagent:<name>`. Parent context is supplied internally, not through public flags. Names resolve only against that parent's direct declarations. This invocation loads the parent's definition source without discovering unrelated repository or global entries, so an unrelated broken definition does not block a declared child. A generic parent has no declared children and fails this lookup without evaluating a specialist catalog. Missing context or an undeclared child is an error; there is no fallback to another scope. Child execution uses the same commands and lifecycle.
|
|
66
|
+
|
|
67
|
+
The caller supplies an existing working directory. The library does not create worktrees or sandboxes. A local coordinator owns pending execution after an individual response command exits. It does not guarantee survival after the coordinator or environment exits, nor does CLI output itself guarantee that an external assistant's host starts a new model turn.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# CLI Output
|
|
2
|
+
|
|
3
|
+
Execution commands accept `--format text|jsonl`; `text` is the default. Both formats report the same operations. JSONL contains complete records separated by newlines, never native token streams or tool transcripts. All records include `version: 1` and a `type` discriminator. Identifiers are opaque strings prefixed with `ses_`, `tsk_`, or `rsp_` and are not paths or process identifiers.
|
|
4
|
+
|
|
5
|
+
Task-creating commands print a `started` record immediately after admission, including when queued. They then return one complete response or terminal outcome for that task. A successful `steer` prints an `accepted` record. If native steering is unavailable, the operation uses `interrupt` semantics and prints `started` with `requestedDelivery: "steer"` and `delivery: "interrupt"`, then returns the replacement task's response or outcome.
|
|
6
|
+
|
|
7
|
+
Quiet tasks keep their local observation connection alive without printing progress messages or extra JSONL records. Transport keepalives are private to the CLI/coordinator connection. A lost observer connection does not cancel or replay its task; callers can retrieve its state and response with `status`.
|
|
8
|
+
|
|
9
|
+
```jsonl
|
|
10
|
+
{"version":1,"type":"started","sessionId":"ses_123","taskId":"tsk_456","state":"running"}
|
|
11
|
+
{"version":1,"type":"response","sessionId":"ses_123","taskId":"tsk_456","responseId":"rsp_789","state":"waiting","text":"Does the limit apply per user?"}
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The identifiers above are illustrative. A `response` contains `sessionId`, `taskId`, `responseId`, `state`, and `text`. Its state describes the task at response creation. Current state is available through `status`. A `terminal` record contains `sessionId`, `taskId`, `state`, and an optional `error` object. It is returned when an observation ends without another response.
|
|
15
|
+
|
|
16
|
+
`status` emits a `status` record with `sessionId`, `taskId`, `state`, and optional `response` and `error`. The response preview contains `responseId`, `text`, and `truncated`; at most 4,000 text characters are included. `subharness status <task-id> --full` returns the complete latest response instead. This provides retrieval without inventing a response cursor that precedes the first response.
|
|
17
|
+
|
|
18
|
+
`list` emits an `agents` record with an `agents` array of `{ id, name, description, scope }`, where scope is `harness`, `repo`, `global`, or `subagent`. The built-in entries have IDs and names `codex`, `claude`, and `fx`, and scope `harness`; they appear in that order before discovered specialists. They describe supported targets, not verified executable, authentication, or model availability. A managed parent's catalog includes its declared `subagent:` entries; generic parents have no declared children. `queue` emits a `queue` record with `sessionId`, `paused`, optional `active`, and a `tasks` array in pending order. Task summaries contain `taskId`, `state`, and a prompt `description` limited to 120 characters.
|
|
19
|
+
|
|
20
|
+
An `accepted` record contains `sessionId`, `taskId`, `delivery: "steer"`. A successful `cancel` emits a `cancelled` record with the targeted task identity and final state, after affected native execution has stopped. Already-terminal tasks retain their existing state. `resume` emits `started` for the existing task with `resumed: true`, then its next response or terminal outcome.
|
|
21
|
+
|
|
22
|
+
Errors use `{ "version": 1, "type": "error", "error": { "code": "INVALID_ARGUMENT", "message": "..." } }` with task/session identifiers when known. JSONL errors appear on stdout; text errors appear on stderr. Native stderr and credentials are not forwarded. Text responses show identifiers and state above the complete response text, with a `wait --after` hint only while the task is pending. Text layout is intended for reading; integrations should use JSONL.
|
|
23
|
+
|
|
24
|
+
Exit code `0` means the command succeeded or a response was returned; it does not mean the agent fulfilled the objective. Code `1` means execution, access, or capability failure; `2` means invalid input, configuration, or unknown identifiers; and `130` means an observation ended in cancellation or interruption. Successful `cancel` itself exits `0`.
|
|
25
|
+
|
|
26
|
+
`status` snapshots retain the observed task’s outcome in their exit code: queued, running, waiting, and completed states exit `0`; cancelled or interrupted states exit `130`; failed states use `1` or `2` according to the stored error. A retrieval error uses its own error code.
|
|
27
|
+
|
|
28
|
+
Standard CLI `--help` and `--version` produce plain text, do not accept `--format`, and do not start the coordinator or harnesses. Command-level `--help` is also available without execution arguments. Unsupported options and extra positional arguments are errors.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Responses and Task Completion
|
|
2
|
+
|
|
3
|
+
Returning a response, completing a task, and starting another caller model turn are separate operations. The CLI returns control after each complete native response from the requested agent. It includes task/session identity, response identity, and the task state. It does not continuously forward token streams, native tool events, or descendants' transcripts.
|
|
4
|
+
|
|
5
|
+
The adapter captures complete responses automatically. Agents do not need a reporting tool, special JSON format, or a classifier that recognizes questions. A question is delivered through the same response mechanism as any other text.
|
|
6
|
+
|
|
7
|
+
## Waiting for another response
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
subharness wait <task-id> --after <response-id>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The command returns the next retained response after the supplied identifier. If none exists, it waits for another complete response or a terminal outcome. If the task has ended without another response, it returns that outcome rather than waiting indefinitely.
|
|
14
|
+
|
|
15
|
+
The explicit cursor prevents losing a response that arrives between commands. Reads do not consume responses. Every reader uses its own cursor; unknown and cross-task identifiers are errors. `status --full` retrieves the complete latest response without needing an earlier cursor.
|
|
16
|
+
|
|
17
|
+
## Completion with delegated work
|
|
18
|
+
|
|
19
|
+
A task remains pending while descendants are unfinished or their results still require processing. Ending a native turn does not cancel children or release independently queued tasks. Child results continue the parent in the same native conversation and library task. Normal completion requires the descendants to finish and the parent to produce a response after processing their results.
|
|
20
|
+
|
|
21
|
+
For example, a developer starts a reviewer and asks whether a limit applies per user or organization. The command returns that question with a pending state; the reviewer continues. The caller can steer an answer into the pending developer task and use `wait` for another response. If no descendants remain, an ordinary complete response can finish the task and release its queue.
|
|
22
|
+
|
|
23
|
+
A child result returned by a child CLI command to an actively running parent is already delivered through that parent's native tool interaction. It is not duplicated in another continuation. Results arriving after an earlier pending response was returned are retained and delivered through a continuation. Results arriving while a parent is busy are held until they can be delivered without interrupting it.
|
|
24
|
+
|
|
25
|
+
## External callers
|
|
26
|
+
|
|
27
|
+
Managed parents can continue through their harness adapters. An everyday assistant outside the library depends on its host's background-task and notification behavior. Completing a shell command makes its result available; it does not guarantee that every host will start another model turn.
|
|
28
|
+
|
|
29
|
+
A caller can run `run`, task-creating `send`, or `wait` through its own background-task controls and handle the resulting response. The library does not claim to reactivate arbitrary external conversations after their caller has ended its turn. The coordinator still owns pending work after an individual response-reading command exits.
|
package/sdk/config.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Agent Discovery
|
|
2
|
+
|
|
3
|
+
Repository agents live in `.agents/agents/` at the Git worktree root. Global agents live in `~/.agents/agents/`. The dedicated subdirectory keeps definitions separate from `.agents/skills/` and other configuration.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
project/
|
|
7
|
+
.agents/
|
|
8
|
+
agents/
|
|
9
|
+
developer.agent.ts
|
|
10
|
+
reviewer.agent.ts
|
|
11
|
+
tools/
|
|
12
|
+
lookup-ticket.ts
|
|
13
|
+
agents.local.json
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Discovery recursively loads `*.agent.ts`, with one default-exported `AgentDefinition` per entry. Ordinary TypeScript helper files are not catalog entries. There is no registry file or registration command. Symlink directories are not traversed. An invalid entry fails discovery with its source filename; it is not silently skipped.
|
|
17
|
+
|
|
18
|
+
Discovery is required for listing and repository/global specialist lookup. Direct harness execution skips it. Declared-child execution loads only the parent's source and direct child map; unrelated catalog entries are not dependencies of a `subagent:` invocation.
|
|
19
|
+
|
|
20
|
+
The declared name determines identity. Duplicate names in the same scope are errors. `repo:reviewer` and `global:reviewer` remain distinct; bare `reviewer` is accepted only when unambiguous. The reserved CLI targets `codex`, `claude`, and `fx` always select native harnesses. Specialists with those names require a `repo:` or `global:` qualifier. Direct harness runs bypass definition discovery entirely, including invalid definition files. Global definitions can execute in any caller-supplied directory. Global and repository files resolve their own imports through normal Node package resolution.
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
subharness list --cwd /repo/worktree
|
|
24
|
+
subharness run repo:developer --cwd /repo/worktree --prompt "Implement the documented feature."
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
An omitted `--cwd` uses the calling command's directory. Inside nested repositories, the nearest Git worktree root is used. Outside Git, the supplied directory is the project root. Bare repositories are not execution directories. Definitions are trusted executable TypeScript: catalog loading evaluates modules.
|
|
28
|
+
|
|
29
|
+
## Personal access and worktrees
|
|
30
|
+
|
|
31
|
+
The optional [access file](access-config.md) is `.agents/agents.local.json` in the repository's main checkout, shared by its linked worktrees. This means the local checkout associated with those worktrees, not a branch named `main` or another clone of the same remote. Worktree agent definitions and execution directories still come from the selected worktree.
|
|
32
|
+
|
|
33
|
+
| Resource | Source |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| Repository definitions | Execution worktree |
|
|
36
|
+
| Personal access preferences | Main checkout |
|
|
37
|
+
| Native credentials | Execution environment and native credential store |
|
|
38
|
+
|
|
39
|
+
The library does not copy configuration or credentials into worktrees. An inaccessible main checkout is an error. A remote or isolated environment must have its own accessible preferences and credentials.
|
|
40
|
+
|
|
41
|
+
A session retains its loaded instructions, tools, and harness configuration. New sessions, including newly delegated child sessions, load definitions from the current files. In a managed parent context, `subharness list` also includes the direct `subagent:` names from that parent's definition source. These names do not register the children in the global or repository catalog.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Package Distribution
|
|
2
|
+
|
|
3
|
+
The npm package name is `subharness`. One package provides the TypeScript SDK and the `subharness` executable and its `agent` compatibility alias. Agent definitions import public helpers from `subharness`. The website workspace remains private and is not part of the npm package.
|
|
4
|
+
|
|
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 remain under `.agents/`.
|
|
6
|
+
|
|
7
|
+
Node.js 22.18 or newer is required. The SDK is ESM and includes TypeScript declarations. The native Codex, Claude Code, and fx executables remain external prerequisites; installing this package does not install harnesses or configure their credentials.
|
|
8
|
+
|
|
9
|
+
## Installation and resolution
|
|
10
|
+
|
|
11
|
+
The public npm package is `subharness`, with an initial release version of `0.0.1`. Install the CLI globally with `npm install --global subharness`, or run it without a global installation using `npx subharness`. Registry installation does not require access to the internal source repository. The root package is publishable; the documentation workspace remains private.
|
|
12
|
+
|
|
13
|
+
For project-local use, install `subharness` with `npm install --save-dev subharness` and invoke its CLI with `npx subharness`. This also makes SDK imports resolve from repository agent definitions. A global CLI installation alone does not make SDK imports resolve from project-local definitions. Global definitions need an SDK dependency reachable from their own directory under normal Node package resolution.
|
|
14
|
+
|
|
15
|
+
For a source checkout, run `npm install --workspaces=false`, `npm run build`, and `npm link` in the repository, then run `npm link subharness` in the consumer project. Disabling workspace installation avoids downloading and preparing the documentation website when only the SDK and CLI are needed. The first link exposes `subharness` and `agent` on PATH. Direct harness targets do not need a consumer SDK dependency; the consumer link makes SDK imports resolve from that project's agent definitions. Global definitions likewise need an SDK dependency reachable from their own directory under normal Node package resolution.
|
|
16
|
+
|
|
17
|
+
A local tarball is installed with `npm install --save-dev /absolute/path/to/subharness-0.0.1.tgz`. Run that project's local executable with `npx subharness`.
|
|
18
|
+
|
|
19
|
+
## Package contents and release checks
|
|
20
|
+
|
|
21
|
+
The package contains the built JavaScript, declaration files and source maps under `dist/`, SDK Markdown documentation under `sdk/`, the README, the MIT license, and package metadata. Source maps embed the original TypeScript so debuggers can display it without a separate source checkout. Website code, development agents, skills, source assets, tests, `.context`, personal settings, and environment files are excluded.
|
|
22
|
+
|
|
23
|
+
Packing builds the SDK from the current source before assembling its files. The CLI's `--version` output matches the package version. A clean consumer must be able to import the SDK and discover a TypeScript agent definition using the installed executable without access to the source checkout or a paid model call. `npm run check:package` verifies this clean-consumer behavior and the packaged file boundary without running a coding model. It requires network access to install dependencies from the public npm registry and disables install lifecycle scripts in the temporary consumer.
|
|
24
|
+
|
|
25
|
+
Dependency URLs in the committed lockfile use the public npm registry so authorized repository users can install without a company registry mirror. Installation and package verification do not change the developer's global npm registry configuration. Registry authentication remains with npm.
|
|
26
|
+
|
|
27
|
+
Releases use the public npm registry, public access, and the `latest` distribution tag. A release publishes the verified tarball for its package version. The matching source commit is tagged `v<version>` in the source repository. Publishing the package does not change the repository's internal visibility.
|
|
28
|
+
|
|
29
|
+
## License
|
|
30
|
+
|
|
31
|
+
Subharness is distributed under the MIT license and retains the existing contributor copyright notice. The root [LICENSE](../LICENSE) contains the complete license text and is included in the package.
|
package/sdk/fx.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# fx Harness
|
|
2
|
+
|
|
3
|
+
The `fx` adapter connects the installed `fx` executable through its native Agent Client Protocol (ACP) over standard input and output. It requires the native ACP capabilities supplied by fx 0.0.9 or a compatible version. The external harness owns inference, native tools, permissions, conversation history, and context management.
|
|
4
|
+
|
|
5
|
+
## Definition
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { agent, fx } from "subharness";
|
|
9
|
+
|
|
10
|
+
export default agent({
|
|
11
|
+
name: "gateway-developer",
|
|
12
|
+
description: "Implements documented tasks through AI Gateway.",
|
|
13
|
+
instructions: "Follow AGENTS.md and the contracts named in the task.",
|
|
14
|
+
harness: fx({ model: "openai/gpt-5.6-sol", effort: "high" }),
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
interface FxOptions {
|
|
20
|
+
readonly model: string;
|
|
21
|
+
readonly effort?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function fx(options: FxOptions): FxConfig;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`FxConfig` is a readonly harness configuration with `kind: "fx"`, the required `model`, and optional `effort`. 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.
|
|
28
|
+
|
|
29
|
+
Models use the exact AI Gateway `provider/model` identifier. 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.
|
|
30
|
+
|
|
31
|
+
Direct CLI invocation does not require a definition: `subharness run fx "Review the current diff."` uses the native Gateway 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`.
|
|
32
|
+
|
|
33
|
+
## Access
|
|
34
|
+
|
|
35
|
+
fx supports explicit `vercel-api-key` and `vercel-oidc` connections through the existing personal access file. It does not discover subscriptions, reuse another harness's subscription, or infer paid access from ambient credentials. Omitting `access.fx` leaves fx without an enabled connection. An empty array explicitly disables it. A `subscription` connection is unavailable; direct `api-key` access is unsupported by this adapter and fails with `UNSUPPORTED_OPTION`.
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"access": {
|
|
40
|
+
"fx": [
|
|
41
|
+
{ "type": "vercel-api-key", "env": "API_KEY", "envFile": ".env" }
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The connection reads only the named variable from the selected file. Existing main-checkout and worktree path rules apply. 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.
|
|
48
|
+
|
|
49
|
+
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.
|
|
50
|
+
|
|
51
|
+
Provider restrictions, exhausted budgets, expired credentials, and model rejections after submission are execution failures. They do not trigger automatic replay or a switch to another provider or model. The library does not relax Gateway team policies.
|
|
52
|
+
|
|
53
|
+
## Instructions, tools, and permissions
|
|
54
|
+
|
|
55
|
+
ACP does not expose a separate system-instructions field for this native release. For a specialist, the adapter supplies the agent's instructions as a clearly delimited context block in the first native prompt, followed by the task, without a preliminary inference turn. Direct generic execution sends the task without an empty specialist context block. Follow-ups reuse that native conversation. Specialist instructions have prompt-context priority and remain subject to native compaction; they are not a replacement system prompt or a guarantee of permanent retention.
|
|
56
|
+
|
|
57
|
+
Declared custom tools are exposed through an authenticated MCP HTTP endpoint bound to loopback for that native session. Tool schemas, argument validation, results, and errors follow the existing custom-tool contract. The endpoint and authentication context are passed privately through ACP. They are not included in agent instructions or CLI output. The endpoint stops admitting calls during cancellation or shutdown, waits for admitted callbacks to finish, and closes when its native session closes or fails to start.
|
|
58
|
+
|
|
59
|
+
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.
|
|
60
|
+
|
|
61
|
+
Native permissions remain authoritative. fx ACP does not expose scoped allow rules equivalent to the Claude adapter's delegation rules. The library does not change its permission mode or infer approval from a permission request. Any unresolved native approval or interactive-input request fails 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.
|
|
62
|
+
|
|
63
|
+
## Session lifecycle
|
|
64
|
+
|
|
65
|
+
Each library session owns one native ACP conversation. Successful follow-ups reuse its session identifier. The adapter returns text from the last native message identifier in the completed prompt and excludes thinking, earlier progress messages, tool traffic, and protocol diagnostics. Native ACP uses the same message type for assistant responses and operational notices, so the last message can be a native notice. Responses retain the library's size limit. Only `end_turn` is successful; native errors and other stop reasons reject the task instead of returning a successful empty response.
|
|
66
|
+
|
|
67
|
+
Native auxiliary calls, such as title generation or permission classification, can use models selected by fx and consume Gateway usage. They do not change the requested task model and are not library fallback. The adapter preserves native preferences for those operations.
|
|
68
|
+
|
|
69
|
+
The coordinator owns FIFO task queuing. This adapter reports native steering as unsupported before changing execution; the coordinator applies the existing `steer`-to-`interrupt` behavior. Interruption sends native cancellation and waits for the active prompt and admitted tool callbacks to stop. Unconfirmed cancellation fails explicitly and keeps queued dispatch paused. Prompt-free recovery returns `RECOVERY_UNSUPPORTED`; no task is silently replayed.
|
|
70
|
+
|
|
71
|
+
Closing a session stops the native process and its local tool endpoint. Native diagnostic output is not forwarded because it can contain credentials. Errors identify the affected operation without exposing credentials or raw provider responses.
|
package/sdk/harnesses.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Harnesses
|
|
2
|
+
|
|
3
|
+
V1 integrates Codex, Claude Code, and fx. Grok Build, Cursor, and OpenCode remain product compatibility requirements without implemented adapters. Compatibility means connecting the external harness, not merely calling its model through an API.
|
|
4
|
+
|
|
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
|
+
|
|
7
|
+
## Selection
|
|
8
|
+
|
|
9
|
+
An agent declares one harness configuration or a nonempty ordered array through `harness`. Each alternative has independent model and execution options. Personal access settings determine eligibility without changing the shared definition.
|
|
10
|
+
|
|
11
|
+
Before submitting work, known unavailability, such as a missing executable or missing eligible login, permits trying the next enabled connection or alternative. Invalid configuration, unsupported explicit options, and uncertain failures are errors. API keys or Gateway tokens present in the environment are not implicitly enabled.
|
|
12
|
+
|
|
13
|
+
Automatic fallback ends at task submission. Execution failure, allowance exhaustion, or an uncertain submission outcome never automatically replays work or migrates a conversation. Partial file and tool effects remain in the execution environment. The caller can inspect progress and explicitly create other work.
|
|
14
|
+
|
|
15
|
+
## Capabilities
|
|
16
|
+
|
|
17
|
+
| Operation | Codex | Claude Code | fx |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| Native session follow-ups | Supported | Supported | Supported |
|
|
20
|
+
| Queue between library tasks | Supported | Supported | Supported |
|
|
21
|
+
| Active steering | Native steering | Uses interrupt semantics | Uses interrupt semantics |
|
|
22
|
+
| Interruption | Native interruption with stop confirmation | Native interruption with stop confirmation | Native ACP cancellation with stop confirmation |
|
|
23
|
+
| Custom tools | Native dynamic tools | Native SDK MCP tools | Private MCP HTTP tools |
|
|
24
|
+
| Prompt-free recovery of failed work | Explicit unsupported result | Explicit unsupported result | Explicit unsupported result |
|
|
25
|
+
|
|
26
|
+
`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.
|
|
27
|
+
|
|
28
|
+
All TypeScript constructors require a model. Direct [CLI harness targets](cli/index.md) may omit it to select and retain a verifiable native default within the authorized access route. Codex and Claude Code default fast mode to false. fx exposes model and optional native effort, with the capabilities and limits described in its [adapter contract](fx.md). 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.
|
|
29
|
+
|
|
30
|
+
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.
|
|
31
|
+
|
|
32
|
+
See [native adapter behavior](adapter-contract.md) for authentication isolation, native permissions, tool transport, and lifecycle details.
|
package/sdk/index.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# SDK and CLI
|
|
2
|
+
|
|
3
|
+
The CLI runs generic Codex, Claude Code, and fx 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
|
+
|
|
5
|
+
- [Distribution](distribution.md): package identity, local installation, and release boundaries.
|
|
6
|
+
- [Agent definitions](agent.md): package exports, fields, harness options, and examples.
|
|
7
|
+
- [Custom tools](tools.md): validated tool functions exposed through native harness integrations.
|
|
8
|
+
- [Discovery](config.md): repository/global definitions and personal worktree settings.
|
|
9
|
+
- [Personal access](access-config.md): subscription discovery, explicit API keys, and project OIDC.
|
|
10
|
+
- [Harnesses](harnesses.md): selection, capabilities, and fallback boundaries.
|
|
11
|
+
- [CLI](cli/index.md): commands and response waiting.
|
|
12
|
+
- [Output](cli/output.md): compact text and typed JSONL records.
|
|
13
|
+
- [Sessions](sessions.md): task identity, state, and recovery.
|
|
14
|
+
- [Message delivery](message-delivery.md): queue, steer, interrupt, and cancellation.
|
|
15
|
+
- [Subagents](plugins/sub-agents.md): nested delegation and context boundaries.
|
|
16
|
+
- [Response delivery](completion-notifications.md): return points and parent continuation.
|
|
17
|
+
- [Runtime](v1-runtime.md): execution ownership, loading, limits, and failure handling.
|
|
18
|
+
- [Native adapters](adapter-contract.md): integration behavior and capability limits.
|
|
19
|
+
|
|
20
|
+
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.
|
|
21
|
+
|
|
22
|
+
The [fx adapter](fx.md) connects native fx ACP sessions through explicit AI Gateway access. The [repository team](project-team.md) defines the roles used to develop this project.
|