@usine/agents 0.4.0 → 0.5.0
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 +661 -21
- package/README.md +16 -8
- package/dist/child-host/child-host.d.ts +31 -1
- package/dist/child-host/child-host.d.ts.map +1 -1
- package/dist/child-host/child-host.js +52 -17
- package/dist/child-host/child-host.js.map +1 -1
- package/dist/child-host/command.d.ts +12 -1
- package/dist/child-host/command.d.ts.map +1 -1
- package/dist/child-host/command.js +18 -1
- package/dist/child-host/command.js.map +1 -1
- package/dist/openai-prices.d.ts +8 -0
- package/dist/openai-prices.d.ts.map +1 -0
- package/dist/openai-prices.js +28 -0
- package/dist/openai-prices.js.map +1 -0
- package/dist/pi/claude/claude.d.ts +10 -0
- package/dist/pi/claude/claude.d.ts.map +1 -0
- package/dist/pi/{claude.js → claude/claude.js} +244 -252
- package/dist/pi/claude/claude.js.map +1 -0
- package/dist/pi/claude/codec.d.ts +67 -0
- package/dist/pi/claude/codec.d.ts.map +1 -0
- package/dist/pi/claude/codec.js +140 -0
- package/dist/pi/claude/codec.js.map +1 -0
- package/dist/pi/{claude.d.ts → claude/sdk.d.ts} +9 -7
- package/dist/pi/claude/sdk.d.ts.map +1 -0
- package/dist/pi/claude/sdk.js +63 -0
- package/dist/pi/claude/sdk.js.map +1 -0
- package/dist/pi/codex/account.d.ts +8 -2
- package/dist/pi/codex/account.d.ts.map +1 -1
- package/dist/pi/codex/account.js +18 -1
- package/dist/pi/codex/account.js.map +1 -1
- package/dist/pi/codex/app-server.d.ts +1 -1
- package/dist/pi/codex/app-server.d.ts.map +1 -1
- package/dist/pi/codex/app-server.js +1 -1
- package/dist/pi/codex/app-server.js.map +1 -1
- package/dist/pi/codex/codec.d.ts +1 -97
- package/dist/pi/codex/codec.d.ts.map +1 -1
- package/dist/pi/codex/codec.js +2 -91
- package/dist/pi/codex/codec.js.map +1 -1
- package/dist/pi/codex/codex.d.ts.map +1 -1
- package/dist/pi/codex/codex.js +17 -7
- package/dist/pi/codex/codex.js.map +1 -1
- package/dist/pi/conversation.d.ts +4 -0
- package/dist/pi/conversation.d.ts.map +1 -1
- package/dist/pi/conversation.js +4 -0
- package/dist/pi/conversation.js.map +1 -1
- package/dist/pi/pi.d.ts.map +1 -1
- package/dist/pi/pi.js +5 -0
- package/dist/pi/pi.js.map +1 -1
- package/dist/pi/sdk.d.ts +8 -1
- package/dist/pi/sdk.d.ts.map +1 -1
- package/dist/pi/sdk.js +21 -13
- package/dist/pi/sdk.js.map +1 -1
- package/dist/pi/usage.d.ts +13 -13
- package/dist/pi/usage.d.ts.map +1 -1
- package/dist/pi/usage.js +40 -18
- package/dist/pi/usage.js.map +1 -1
- package/dist/preflight/command.d.ts +4 -5
- package/dist/preflight/command.d.ts.map +1 -1
- package/dist/preflight/command.js +11 -22
- package/dist/preflight/command.js.map +1 -1
- package/dist/preflight/preflight.d.ts +11 -4
- package/dist/preflight/preflight.d.ts.map +1 -1
- package/dist/preflight/preflight.js +24 -10
- package/dist/preflight/preflight.js.map +1 -1
- package/dist/protocol/codex.d.ts +122 -4
- package/dist/protocol/codex.d.ts.map +1 -1
- package/dist/protocol/codex.js +108 -0
- package/dist/protocol/codex.js.map +1 -1
- package/dist/protocol/count.d.ts +4 -0
- package/dist/protocol/count.d.ts.map +1 -0
- package/dist/protocol/count.js +4 -0
- package/dist/protocol/count.js.map +1 -0
- package/dist/runtime.d.ts +18 -8
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +12 -6
- package/dist/runtime.js.map +1 -1
- package/package.json +23 -19
- package/dist/pi/claude.d.ts.map +0 -1
- package/dist/pi/claude.js.map +0 -1
package/README.md
CHANGED
|
@@ -26,27 +26,29 @@ const childBackendLayer = childDispatcherLayer.pipe(
|
|
|
26
26
|
);
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
The Claude backend also needs `ClaudeLoginLock` (`/runtime`),
|
|
29
|
+
The Claude backend also needs `ClaudeLoginLock` (`/runtime`), which every Claude command that can refresh a Claude login takes: one lock per worker, or per machine of a provisioned worker, whose machines each have their own login. Give the same instance (`claudeLoginLockLayer` from `@usine/executor`) to the backends and the executor running preflight. Like Codex's app server, it also needs a `ChildProcessSpawner`, to stop a child on its worker before it resumes the child.
|
|
30
30
|
|
|
31
31
|
## Harnesses and credentials
|
|
32
32
|
|
|
33
33
|
- **Parent:** a Pi model with an API key, or an `openai-codex` ChatGPT model through Plus or Pro OAuth. The start log records `apiKeys` or `chatgptPlan`. A child past the run spec's child-session budget stops the parent with `ChildSessionBudgetExceededError`.
|
|
34
34
|
- **Claude Code:** the worker's `claude` through the Agent SDK, with `bypassPermissions`, worker-user settings only, and the CLI login.
|
|
35
|
-
- **Codex:** app-server JSON-RPC over stdio, with `approvalPolicy: never` and the CLI login. It checks the login with `account/read` before creating a thread, and records plan rate windows as `PoolRateLimitsUpdated`. An API-key login, no login, or an unsupported account fails with `PoolUnavailableError`, which needs the operator. A login change ends the session before more rate windows are recorded.
|
|
35
|
+
- **Codex:** app-server JSON-RPC over stdio, with `approvalPolicy: never` and the CLI login. It checks the login with `account/read` before creating a thread, and records plan rate windows as `PoolRateLimitsUpdated`. An API-key login, no login, or an unsupported account fails with `PoolUnavailableError`, which needs the operator. A login change ends the session before more rate windows are recorded. If `ChildSession.gatewayCredential` is set, the gateway holds the OpenAI credential and Codex has no login: the child skips the login check and ignores login changes, and its pool follows the credential kind, `chatgptPlan` for a ChatGPT login and `apiKeys` for an API key.
|
|
36
36
|
- **Pi children:** API keys only.
|
|
37
37
|
|
|
38
38
|
Logs record the verified authentication method and pool. On completion or interrupt, usine closes the input of the child's Codex app server and waits for it to exit before publishing, releasing background commands. Scope cleanup does the same, bounded, and logs before force-stopping a stalled process.
|
|
39
39
|
|
|
40
40
|
`/pi/authentication` exports the parent's rules for other runtimes: `splitPiModel` (parses `provider/model`), `piAuthentication` (a provider's credential check), `requiredPiPool` (a provider's pool), and `resolvePiPool` (its credential's pool).
|
|
41
41
|
|
|
42
|
+
`/openai-prices` exports `openAiPrice`, which prices the token breakdown of a Codex session's `SessionTokensUpdated` at OpenAI's API rates. It returns `None` for a model that the bundled table lacks. The table covers the listed models of each supported Codex CLI version.
|
|
43
|
+
|
|
42
44
|
## Child hosts
|
|
43
45
|
|
|
44
46
|
`ChildRequest.host` (`/child-host`) sets where a child runs, and `ChildRequest.worker` names that machine's worker:
|
|
45
47
|
|
|
46
48
|
- `LocalProcess`: on this machine, with `ChildRequest.environment` as the whole environment.
|
|
47
|
-
- `RemoteShell`: one `ssh <args> <destination> -- <remote command>`, which enters the worktree, exports the request's environment over the machine's own, writes its pid to `<worktree>.child-<n>.pid`, and runs the harness. The SSH client gets only `RemoteShell.sshEnvironment`.
|
|
49
|
+
- `RemoteShell`: one `ssh <args> <destination> -- <remote command>`, which enters the worktree, exports the request's environment over the machine's own, writes its pid to `<worktree>.child-<n>.pid`, and runs the harness. The SSH client gets only `RemoteShell.sshEnvironment`. With `container`, the remote command is `<runtime> exec -i <name> sh -c <command line>`, so the directory, variables, and pid file are inside that container.
|
|
48
50
|
|
|
49
|
-
Executors run `CHILD_KILL_SCRIPT` remotely to stop every child in those pid files before keeping or removing a worktree. Claude Code runs remotely through `spawnClaudeCodeProcess`, Codex over the same SSH command. Pi children run tools in this process and fail on `RemoteShell` with `AgentResultError`.
|
|
51
|
+
Executors run `CHILD_KILL_SCRIPT` remotely to stop every child in those pid files before keeping or removing a worktree. Claude Code runs remotely through `spawnClaudeCodeProcess`, Codex over the same SSH command. Pi children run tools in this process and fail on `RemoteShell` with `AgentResultError`. They get only Pi's default tools, `read`, `bash`, `edit`, and `write`, whatever the global Pi `defaultTools` setting adds, and `bash` runs with exactly `ChildRequest.environment`.
|
|
50
52
|
|
|
51
53
|
The worktree is repo-controlled input: Pi ignores the project's `.pi/` settings, extensions, prompt templates, skills, and themes; Claude omits project setting sources; Codex marks the worktree untrusted. AGENTS instructions still apply. The executor isolates the worker.
|
|
52
54
|
|
|
@@ -58,7 +60,7 @@ Child output becomes typed events: `SessionStarted`, `TextOutput` for completed
|
|
|
58
60
|
|
|
59
61
|
## Claude login lock
|
|
60
62
|
|
|
61
|
-
A Claude child holds the worker
|
|
63
|
+
A Claude child holds the `ClaudeLoginLock` of its worker, or of its machine (`ChildRequest.machine`) on a provisioned worker, from process start until its first messages prove the login (a rate-limit or thinking update, an API retry, a model answer, or the turn result), for one minute at most. `launch` returns then.
|
|
62
64
|
|
|
63
65
|
If the first turn ends with `Failed to refresh OAuth token: another Claude Code process ...` and no tokens spent, another process had the refresh lock. The backend stops the process and relaunches after 15, 30, then 60 seconds, logging each retry at info. Only the running launch is transcribed. If the last launch fails the same way, the parent gets Claude's error as the child's report.
|
|
64
66
|
|
|
@@ -68,6 +70,12 @@ Claude children use the CLI's background-task snapshots and session-idle signal
|
|
|
68
70
|
|
|
69
71
|
The prompts tell parents and children, before returning, to run gates, tests, and CI watches in the foreground, wait for background results and pending checks, finish dependent steps, record real blockers on the issue or pull request, and push commits, since cleanup removes the worktree. This also covers older CLIs without background-task or session-state messages.
|
|
70
72
|
|
|
73
|
+
## Lost connections
|
|
74
|
+
|
|
75
|
+
If the SSH connection to a remote Claude child breaks, for example while the orchestrator's machine sleeps, the SSH client exits with `SSH_FAILURE` (255) and the output stream fails. The backend then resumes the child instead of failing it: it stops the Claude process left on the worker with `CHILD_STOP_SCRIPT` and the child's pid file, and starts Claude with the saved session in the same child session, so the parent spends no child budget. A working child gets a message that asks it to check the worktree and continue, followed by the operator messages that it hadn't read. A paused or idle child waits for the operator. Open tool calls end as errors, and token usage and cost carry over.
|
|
76
|
+
|
|
77
|
+
The backend tries after 5 s, 15 s, 30 s, 1 min, 2 min, and 5 min; a message from Claude resets the count. After the last try, on a local host, or if Claude itself exited, the child fails with `SessionOperationError` as before. A Codex child whose SSH connection breaks fails.
|
|
78
|
+
|
|
71
79
|
## Outcome markers
|
|
72
80
|
|
|
73
81
|
An unresolved blocker starts the final answer with `[usine:blocked]`, then details and incomplete work on that line or the next:
|
|
@@ -96,13 +104,13 @@ Agents share the installation's GitHub App identity. The instructions require co
|
|
|
96
104
|
|
|
97
105
|
## Resume a harness session
|
|
98
106
|
|
|
99
|
-
Claude children save sessions, and Codex threads aren't ephemeral. `SessionStarted.harnessSession` and launch logs (`harnessSessionId`, `sessionId`, `cwd`) give the id and directory. While the worktree and session files exist, run `claude --resume <id>` or `codex resume <id>` there, on the same worker and account.
|
|
107
|
+
Claude children save sessions, and Codex threads aren't ephemeral. `SessionStarted.harnessSession` and launch logs (`harnessSessionId`, `sessionId`, `cwd`) give the id and directory. While the worktree and session files exist, run `claude --resume <id>` or `codex resume <id>` there, on the same worker and account. A Codex child whose environment sets `CODEX_HOME` saves its sessions there, and `HarnessSession.home` and the launch log's `home` record that directory: run `CODEX_HOME=<home> codex resume <id>`. An attempt behind a gateway gets its own `CODEX_HOME`, and its model credential isn't in your shell.
|
|
100
108
|
|
|
101
109
|
## Preflight
|
|
102
110
|
|
|
103
111
|
`preflight(brain, host, environment)` (`/preflight`, Node) checks a worker without a prompt, using a `ChildProcessSpawner` layer. Each check sees `environment` as a child does. On a `RemoteShell` host, checks run over SSH, an SSH failure (exit 255) is an incomplete check rather than a missing harness, and a Pi brain is unavailable.
|
|
104
112
|
|
|
105
|
-
It accepts Codex CLI **0.
|
|
113
|
+
It accepts Codex CLI **0.153.4 or 0.159.0 through 0.159.3** (`supportedCodexVersions`; `unsupportedCodexVersion` returns the error for other `codex --version` output) with a verified ChatGPT account, a Claude subscription in `claude auth status --json`, and Pi's model and API key through the SDK. `HarnessUnavailableError` reports a missing binary, version, or login; `SessionOperationError` an incomplete check, such as a timeout or failed SDK connection. Install and log in as the worker user before starting usine.
|
|
106
114
|
|
|
107
115
|
## Live contract tests
|
|
108
116
|
|
|
@@ -116,4 +124,4 @@ USINE_CODEX_INTEGRATION_MODEL=gpt-5.6-terra pnpm --filter @usine/agents test:int
|
|
|
116
124
|
USINE_PI_INTEGRATION_MODEL=anthropic/claude-sonnet-4-6 pnpm --filter @usine/agents test:integration:pi
|
|
117
125
|
```
|
|
118
126
|
|
|
119
|
-
The protocol conformance baseline is 0.
|
|
127
|
+
The protocol conformance baseline is 0.159.2. Codex 0.159.0, 0.159.1, and 0.159.3 have the same protocol crates. Codex 0.153.4 passed the real steering and interruption contracts, including Astra, and its generated types have the same shape as 0.159.2's for every field the adapter uses. Other CLI builds need verification first.
|
|
@@ -10,6 +10,13 @@ export declare const SshCommand: Schema.Struct<{
|
|
|
10
10
|
readonly args: Schema.$Array<Schema.String>;
|
|
11
11
|
}>;
|
|
12
12
|
export type SshCommand = typeof SshCommand.Type;
|
|
13
|
+
/** A running container on the remote machine, entered with `<runtime> exec`. */
|
|
14
|
+
export declare const ShellContainer: Schema.Struct<{
|
|
15
|
+
/** The container CLI on the machine, such as `docker`. */
|
|
16
|
+
readonly runtime: Schema.NonEmptyString;
|
|
17
|
+
readonly name: Schema.NonEmptyString;
|
|
18
|
+
}>;
|
|
19
|
+
export type ShellContainer = typeof ShellContainer.Type;
|
|
13
20
|
declare const RemoteShell_base: Schema.Class<RemoteShell, Schema.TaggedStruct<"RemoteShell", {
|
|
14
21
|
readonly ssh: Schema.Struct<{
|
|
15
22
|
readonly command: Schema.NonEmptyString;
|
|
@@ -24,6 +31,15 @@ declare const RemoteShell_base: Schema.Class<RemoteShell, Schema.TaggedStruct<"R
|
|
|
24
31
|
readonly sshEnvironment: Schema.$Record<Schema.String, Schema.String>;
|
|
25
32
|
/** The SSH destination, passed as one argument. */
|
|
26
33
|
readonly destination: Schema.NonEmptyString;
|
|
34
|
+
/**
|
|
35
|
+
* The container that runs each command, with the working directory, variables, and PID file
|
|
36
|
+
* inside it. If absent, the SSH user's shell runs the command.
|
|
37
|
+
*/
|
|
38
|
+
readonly container: Schema.optionalKey<Schema.Struct<{
|
|
39
|
+
/** The container CLI on the machine, such as `docker`. */
|
|
40
|
+
readonly runtime: Schema.NonEmptyString;
|
|
41
|
+
readonly name: Schema.NonEmptyString;
|
|
42
|
+
}>>;
|
|
27
43
|
}>, {}>;
|
|
28
44
|
/** A host that runs each child on another machine as one SSH command, through its standard streams. */
|
|
29
45
|
export declare class RemoteShell extends RemoteShell_base {
|
|
@@ -45,10 +61,19 @@ export interface HostInvocation {
|
|
|
45
61
|
readonly command: string;
|
|
46
62
|
readonly args: ReadonlyArray<string>;
|
|
47
63
|
}
|
|
48
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Returns the SSH client arguments that run `invocation`: the options, the destination, and one
|
|
66
|
+
* remote command line. In a container, the line runs `sh -c` with `invocation`'s command line
|
|
67
|
+
* through `<runtime> exec -i`, so its standard streams pass through.
|
|
68
|
+
*/
|
|
49
69
|
export declare const sshArguments: (host: RemoteShell, invocation: HostInvocation) => Array<string>;
|
|
50
70
|
/** Returns the PID file path of a remote child: next to its worktree, named by the child number in its session ID. */
|
|
51
71
|
export declare const childPidFile: (worktree: string, session: AgentSessionId) => string;
|
|
72
|
+
/**
|
|
73
|
+
* The exit code of the OpenSSH client when it can't connect, loses the connection, or can't run the
|
|
74
|
+
* remote command. Any other code is the remote command's.
|
|
75
|
+
*/
|
|
76
|
+
export declare const SSH_FAILURE = 255;
|
|
52
77
|
/**
|
|
53
78
|
* A shell script that stops an Attempt's remaining remote children before its worktree is kept or
|
|
54
79
|
* removed: for each `<worktree>.child-*.pid`, it sends TERM, waits, then sends KILL. Run it with
|
|
@@ -56,5 +81,10 @@ export declare const childPidFile: (worktree: string, session: AgentSessionId) =
|
|
|
56
81
|
* not mean that its remote command stopped.
|
|
57
82
|
*/
|
|
58
83
|
export declare const CHILD_KILL_SCRIPT: string;
|
|
84
|
+
/**
|
|
85
|
+
* A shell script that stops one remote child, whose PID file (`childPidFile`) is `$1`, in the way
|
|
86
|
+
* `CHILD_KILL_SCRIPT` stops each child. Run it with `sh -c`; it prints one outcome.
|
|
87
|
+
*/
|
|
88
|
+
export declare const CHILD_STOP_SCRIPT = "pidfile=$1\n pid=$(cat \"$pidfile\" 2>/dev/null)\n if [ -n \"$pid\" ] && kill -TERM \"$pid\" 2>/dev/null; then\n n=0\n while kill -0 \"$pid\" 2>/dev/null && [ \"$n\" -lt 10 ]; do sleep 0.5; n=$((n+1)); done\n if kill -0 \"$pid\" 2>/dev/null; then kill -KILL \"$pid\" 2>/dev/null; echo \"$pidfile killed\"; else echo \"$pidfile stopped\"; fi\n else\n echo \"$pidfile absent\"\n fi\n rm -f \"$pidfile\"";
|
|
59
89
|
export {};
|
|
60
90
|
//# sourceMappingURL=child-host.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"child-host.d.ts","sourceRoot":"","sources":["../../src/child-host/child-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;;AAEhC,oDAAoD;AACpD,qBAAa,YAAa,SAAQ,iBAGjC;CAAG;AAEJ,uEAAuE;AACvE,eAAO,MAAM,UAAU;;;EAGrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;;;;;;
|
|
1
|
+
{"version":3,"file":"child-host.d.ts","sourceRoot":"","sources":["../../src/child-host/child-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;;AAEhC,oDAAoD;AACpD,qBAAa,YAAa,SAAQ,iBAGjC;CAAG;AAEJ,uEAAuE;AACvE,eAAO,MAAM,UAAU;;;EAGrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AAEhD,gFAAgF;AAChF,eAAO,MAAM,cAAc;IACzB,0DAA0D;;;EAG1D,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAC;;;;;;IAOpD;;;;;OAKG;;IAEH,mDAAmD;;IAEnD;;;OAGG;;QAvBL,0DAA0D;;;;;AAM5D,uGAAuG;AACvG,qBAAa,WAAY,SAAQ,gBAmBhC;CAAG;AAEJ,iGAAiG;AACjG,eAAO,MAAM,SAAS,kEAA4C,CAAC;AACnE,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,IAAI,CAAC;AAE9C,mCAAmC;AACnC,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC;IAChF,oIAAoI;IACpI,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACtC;AA8BD;;;;GAIG;AACH,eAAO,MAAM,YAAY,SAAU,WAAW,cAAc,cAAc,KAAG,KAAK,CAAC,MAAM,CAUxF,CAAC;AAEF,sHAAsH;AACtH,eAAO,MAAM,YAAY,aAAc,MAAM,WAAW,cAAc,KAAG,MACD,CAAC;AAEzE;;;GAGG;AACH,eAAO,MAAM,WAAW,MAAM,CAAC;AAiB/B;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,QAOtB,CAAC;AAET;;;GAGG;AACH,eAAO,MAAM,iBAAiB,saAA+B,CAAC"}
|
|
@@ -7,6 +7,12 @@ export const SshCommand = Schema.Struct({
|
|
|
7
7
|
command: Schema.NonEmptyString,
|
|
8
8
|
args: Schema.Array(Schema.String),
|
|
9
9
|
});
|
|
10
|
+
/** A running container on the remote machine, entered with `<runtime> exec`. */
|
|
11
|
+
export const ShellContainer = Schema.Struct({
|
|
12
|
+
/** The container CLI on the machine, such as `docker`. */
|
|
13
|
+
runtime: Schema.NonEmptyString,
|
|
14
|
+
name: Schema.NonEmptyString,
|
|
15
|
+
});
|
|
10
16
|
/** A host that runs each child on another machine as one SSH command, through its standard streams. */
|
|
11
17
|
export class RemoteShell extends Schema.TaggedClass("@usine/agents/RemoteShell")("RemoteShell", {
|
|
12
18
|
ssh: SshCommand,
|
|
@@ -19,6 +25,11 @@ export class RemoteShell extends Schema.TaggedClass("@usine/agents/RemoteShell")
|
|
|
19
25
|
sshEnvironment: Schema.Record(Schema.String, Schema.String),
|
|
20
26
|
/** The SSH destination, passed as one argument. */
|
|
21
27
|
destination: Schema.NonEmptyString,
|
|
28
|
+
/**
|
|
29
|
+
* The container that runs each command, with the working directory, variables, and PID file
|
|
30
|
+
* inside it. If absent, the SSH user's shell runs the command.
|
|
31
|
+
*/
|
|
32
|
+
container: Schema.optionalKey(ShellContainer),
|
|
22
33
|
}) {
|
|
23
34
|
}
|
|
24
35
|
/** The host that runs an Attempt's children, chosen by the Worker that prepared its checkout. */
|
|
@@ -49,26 +60,34 @@ const remoteShellCommand = (invocation) => {
|
|
|
49
60
|
const run = `sh -c 'printf %s "$$" > "$0" && exec "$@"' ${pidFile} ${quoted(command)}`;
|
|
50
61
|
return `${[...setup, run].join(" && ")}; code=$?; rm -f ${pidFile}; exit "$code"`;
|
|
51
62
|
};
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Returns the SSH client arguments that run `invocation`: the options, the destination, and one
|
|
65
|
+
* remote command line. In a container, the line runs `sh -c` with `invocation`'s command line
|
|
66
|
+
* through `<runtime> exec -i`, so its standard streams pass through.
|
|
67
|
+
*/
|
|
68
|
+
export const sshArguments = (host, invocation) => {
|
|
69
|
+
const line = remoteShellCommand(invocation);
|
|
70
|
+
return [
|
|
71
|
+
...host.ssh.args,
|
|
72
|
+
host.destination,
|
|
73
|
+
"--",
|
|
74
|
+
host.container === undefined
|
|
75
|
+
? line
|
|
76
|
+
: `exec ${quoted([host.container.runtime, "exec", "-i", host.container.name, "sh", "-c", line])}`,
|
|
77
|
+
];
|
|
78
|
+
};
|
|
59
79
|
/** Returns the PID file path of a remote child: next to its worktree, named by the child number in its session ID. */
|
|
60
80
|
export const childPidFile = (worktree, session) => `${worktree}.child-${session.slice(session.lastIndexOf(":") + 1)}.pid`;
|
|
61
81
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* `sh -c` and the worktree as `$1`; it prints one outcome per PID file. An exited SSH client does
|
|
65
|
-
* not mean that its remote command stopped.
|
|
82
|
+
* The exit code of the OpenSSH client when it can't connect, loses the connection, or can't run the
|
|
83
|
+
* remote command. Any other code is the remote command's.
|
|
66
84
|
*/
|
|
67
|
-
export const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
85
|
+
export const SSH_FAILURE = 255;
|
|
86
|
+
/**
|
|
87
|
+
* Stops the process in `$pidfile`: TERM, up to 5 seconds of waiting, then KILL. Prints
|
|
88
|
+
* `<pidfile> stopped`, `killed`, or `absent`, and removes the file.
|
|
89
|
+
*/
|
|
90
|
+
const STOP_PID_FILE = `
|
|
72
91
|
pid=$(cat "$pidfile" 2>/dev/null)
|
|
73
92
|
if [ -n "$pid" ] && kill -TERM "$pid" 2>/dev/null; then
|
|
74
93
|
n=0
|
|
@@ -77,8 +96,24 @@ for pidfile in "$1".child-*.pid; do
|
|
|
77
96
|
else
|
|
78
97
|
echo "$pidfile absent"
|
|
79
98
|
fi
|
|
80
|
-
rm -f "$pidfile"
|
|
99
|
+
rm -f "$pidfile"`;
|
|
100
|
+
/**
|
|
101
|
+
* A shell script that stops an Attempt's remaining remote children before its worktree is kept or
|
|
102
|
+
* removed: for each `<worktree>.child-*.pid`, it sends TERM, waits, then sends KILL. Run it with
|
|
103
|
+
* `sh -c` and the worktree as `$1`; it prints one outcome per PID file. An exited SSH client does
|
|
104
|
+
* not mean that its remote command stopped.
|
|
105
|
+
*/
|
|
106
|
+
export const CHILD_KILL_SCRIPT = `
|
|
107
|
+
found=0
|
|
108
|
+
for pidfile in "$1".child-*.pid; do
|
|
109
|
+
test -f "$pidfile" || continue
|
|
110
|
+
found=1${STOP_PID_FILE}
|
|
81
111
|
done
|
|
82
112
|
test "$found" = 1 || echo no-children
|
|
83
113
|
`.trim();
|
|
114
|
+
/**
|
|
115
|
+
* A shell script that stops one remote child, whose PID file (`childPidFile`) is `$1`, in the way
|
|
116
|
+
* `CHILD_KILL_SCRIPT` stops each child. Run it with `sh -c`; it prints one outcome.
|
|
117
|
+
*/
|
|
118
|
+
export const CHILD_STOP_SCRIPT = `pidfile=$1${STOP_PID_FILE}`;
|
|
84
119
|
//# sourceMappingURL=child-host.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"child-host.js","sourceRoot":"","sources":["../../src/child-host/child-host.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,oDAAoD;AACpD,MAAM,OAAO,YAAa,SAAQ,MAAM,CAAC,WAAW,CAAe,4BAA4B,CAAC,CAC9F,cAAc,EACd,EAAE,CACH;CAAG;AAEJ,uEAAuE;AACvE,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;CAClC,CAAC,CAAC;AAGH,uGAAuG;AACvG,MAAM,OAAO,WAAY,SAAQ,MAAM,CAAC,WAAW,CAAc,2BAA2B,CAAC,CAC3F,aAAa,EACb;IACE,GAAG,EAAE,UAAU;IACf;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;IAC3D,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC,cAAc;
|
|
1
|
+
{"version":3,"file":"child-host.js","sourceRoot":"","sources":["../../src/child-host/child-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
2
3
|
import { type ChildHost, type HostInvocation } from "./child-host.ts";
|
|
3
4
|
/**
|
|
4
5
|
* Returns the local process command that runs `invocation` on `host`: the command itself with
|
|
@@ -8,4 +9,14 @@ import { type ChildHost, type HostInvocation } from "./child-host.ts";
|
|
|
8
9
|
export declare const hostCommand: (host: ChildHost, invocation: HostInvocation & {
|
|
9
10
|
readonly environment: Readonly<Record<string, string>>;
|
|
10
11
|
}, options: ChildProcess.CommandOptions) => ChildProcess.Command;
|
|
12
|
+
/**
|
|
13
|
+
* Runs `invocation` on `host` and returns its trimmed stdout and exit code; stderr is discarded. On
|
|
14
|
+
* a remote host, `SSH_FAILURE` means that SSH failed, not the command.
|
|
15
|
+
*/
|
|
16
|
+
export declare const hostOutput: (host: import("./child-host.ts").LocalProcess | import("./child-host.ts").RemoteShell, invocation: HostInvocation & {
|
|
17
|
+
readonly environment: Readonly<Record<string, string>>;
|
|
18
|
+
}) => Effect.Effect<{
|
|
19
|
+
stdout: string;
|
|
20
|
+
exitCode: ChildProcessSpawner.ExitCode;
|
|
21
|
+
}, import("effect/PlatformError").PlatformError, ChildProcessSpawner.ChildProcessSpawner>;
|
|
11
22
|
//# sourceMappingURL=command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/child-host/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/child-host/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAEnE,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,cAAc,EAAgB,MAAM,iBAAiB,CAAC;AAEpF;;;;GAIG;AACH,eAAO,MAAM,WAAW,SAChB,SAAS,cACH,cAAc,GAAG;IAAE,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,WAC9E,YAAY,CAAC,cAAc,KACnC,YAAY,CAAC,OAgBf,CAAC;AAQF;;;GAGG;AACH,eAAO,MAAM,UAAU;0BAEgC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;;;yFAmBrF,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect, Stream } from "effect";
|
|
2
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
2
3
|
import { homedir } from "node:os";
|
|
3
4
|
import { sshArguments } from "./child-host.js";
|
|
4
5
|
/**
|
|
@@ -23,4 +24,20 @@ export const hostCommand = (host, invocation, options) => {
|
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Runs `invocation` on `host` and returns its trimmed stdout and exit code; stderr is discarded. On
|
|
29
|
+
* a remote host, `SSH_FAILURE` means that SSH failed, not the command.
|
|
30
|
+
*/
|
|
31
|
+
export const hostOutput = Effect.fn("ChildHost.output")(function* (host, invocation) {
|
|
32
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
33
|
+
return yield* Effect.scoped(Effect.gen(function* () {
|
|
34
|
+
const handle = yield* spawner.spawn(hostCommand(host, invocation, { detached: false, forceKillAfter: "2 seconds" }));
|
|
35
|
+
const [stdout, , exitCode] = yield* Effect.all([
|
|
36
|
+
handle.stdout.pipe(Stream.decodeText(), Stream.mkString),
|
|
37
|
+
handle.stderr.pipe(Stream.runDrain),
|
|
38
|
+
handle.exitCode,
|
|
39
|
+
], { concurrency: "unbounded" });
|
|
40
|
+
return { stdout: stdout.trim(), exitCode };
|
|
41
|
+
}));
|
|
42
|
+
});
|
|
26
43
|
//# sourceMappingURL=command.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/child-host/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/child-host/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAuC,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpF;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,IAAe,EACf,UAAuF,EACvF,OAAoC,EACd,EAAE;IACxB,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,cAAc;YACjB,OAAO,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,EAAE;gBAC5D,GAAG,OAAO;gBACV,GAAG,EAAE,UAAU,CAAC,GAAG,IAAI,OAAO,EAAE;gBAChC,GAAG,EAAE,EAAE,GAAG,UAAU,CAAC,WAAW,EAAE;gBAClC,SAAS,EAAE,KAAK;aACjB,CAAC,CAAC;QACL,KAAK,aAAa;YAChB,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE;gBACzE,GAAG,OAAO;gBACV,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE;gBAC/B,SAAS,EAAE,KAAK;aACjB,CAAC,CAAC;IACP,CAAC;AACH,CAAC,CAAC;AAQF;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAAC,EAC/D,IAAe,EACf,UAAuF;IAEvF,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,mBAAmB,CAAC;IAC/D,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CACzB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CACjC,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAChF,CAAC;QACF,MAAM,CAAC,MAAM,EAAE,AAAD,EAAG,QAAQ,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAC5C;YACE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC;YACxD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;YACnC,MAAM,CAAC,QAAQ;SAChB,EACD,EAAE,WAAW,EAAE,WAAW,EAAE,CAC7B,CAAC;QACF,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAuB,CAAC;IAClE,CAAC,CAAC,CACH,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type TokenBreakdown, UsdAmount } from "@usine/core/agent/session";
|
|
2
|
+
import { Option } from "effect";
|
|
3
|
+
/**
|
|
4
|
+
* Returns the price of `tokens` at OpenAI's rates for `model`, a model ID as the Codex app-server
|
|
5
|
+
* reports it, or `None` if the table has no rates for the model.
|
|
6
|
+
*/
|
|
7
|
+
export declare const openAiPrice: (model: string, tokens: TokenBreakdown) => Option.Option<UsdAmount>;
|
|
8
|
+
//# sourceMappingURL=openai-prices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-prices.d.ts","sourceRoot":"","sources":["../src/openai-prices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AA4BhC;;;GAGG;AACH,eAAO,MAAM,WAAW,UAAW,MAAM,UAAU,cAAc,KAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CASxF,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { UsdAmount } from "@usine/core/agent/session";
|
|
2
|
+
import { Option } from "effect";
|
|
3
|
+
// Standard tier, short context (up to 272K input tokens, the Codex context window), read from
|
|
4
|
+
// https://developers.openai.com/api/docs/pricing on 2026-10-26. `cacheWrite` is the 30-minute write
|
|
5
|
+
// rate, which applies when a request sets no cache retention, as Codex does.
|
|
6
|
+
const RATES = new Map([
|
|
7
|
+
["gpt-6-astra", { input: 10, cachedInput: 1, cacheWrite: 12.5, output: 50 }],
|
|
8
|
+
["gpt-6.1-sol", { input: 2, cachedInput: 0.1, cacheWrite: 2.5, output: 10 }],
|
|
9
|
+
["gpt-6-sol", { input: 2, cachedInput: 0.2, cacheWrite: 2.5, output: 10 }],
|
|
10
|
+
["gpt-6-luna", { input: 0.1, cachedInput: 0.01, cacheWrite: 0.125, output: 0.5 }],
|
|
11
|
+
["gpt-5.6-sol", { input: 4, cachedInput: 0.4, cacheWrite: 5, output: 20 }],
|
|
12
|
+
["gpt-5.6-terra", { input: 2, cachedInput: 0.2, cacheWrite: 2.5, output: 12 }],
|
|
13
|
+
["gpt-5.6-luna", { input: 0.2, cachedInput: 0.02, cacheWrite: 0.25, output: 1.2 }],
|
|
14
|
+
["gpt-5.5", { input: 5, cachedInput: 0.5, output: 30 }],
|
|
15
|
+
["gpt-5.4", { input: 2.5, cachedInput: 0.25, output: 15 }],
|
|
16
|
+
["gpt-5.4-mini", { input: 0.75, cachedInput: 0.075, output: 4.5 }],
|
|
17
|
+
["gpt-5.2", { input: 1.75, cachedInput: 0.175, output: 14 }],
|
|
18
|
+
]);
|
|
19
|
+
/**
|
|
20
|
+
* Returns the price of `tokens` at OpenAI's rates for `model`, a model ID as the Codex app-server
|
|
21
|
+
* reports it, or `None` if the table has no rates for the model.
|
|
22
|
+
*/
|
|
23
|
+
export const openAiPrice = (model, tokens) => Option.map(Option.fromNullishOr(RATES.get(model)), (rates) => UsdAmount.make((tokens.input * rates.input +
|
|
24
|
+
tokens.cachedInput * rates.cachedInput +
|
|
25
|
+
tokens.cacheWrite * (rates.cacheWrite ?? rates.input) +
|
|
26
|
+
tokens.output * rates.output) /
|
|
27
|
+
1_000_000));
|
|
28
|
+
//# sourceMappingURL=openai-prices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-prices.js","sourceRoot":"","sources":["../src/openai-prices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAWhC,8FAA8F;AAC9F,oGAAoG;AACpG,6EAA6E;AAC7E,MAAM,KAAK,GAAqC,IAAI,GAAG,CAAC;IACtD,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC5E,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC5E,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC1E,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACjF,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC1E,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC9E,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAClF,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACvD,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC1D,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAClE,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;CAC7D,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,MAAsB,EAA4B,EAAE,CAC7F,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CAC3D,SAAS,CAAC,IAAI,CACZ,CAAC,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK;IACzB,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW;IACtC,MAAM,CAAC,UAAU,GAAG,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,KAAK,CAAC;IACrD,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC7B,SAAS,CACZ,CACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { ChildProcessSpawner } from "effect/process";
|
|
3
|
+
import { ClaudeChildBackend, ClaudeLoginLock, ChildBackend } from "../../runtime.ts";
|
|
4
|
+
import { ClaudeSdk } from "./sdk.ts";
|
|
5
|
+
export { ClaudeSdk, claudeSdkLive, type ClaudeModelInfo, type ClaudeQuery, type ClaudeQueryOptions, type ClaudeUserMessage, } from "./sdk.ts";
|
|
6
|
+
/** Provides `ChildBackend` with Claude Code for every delegated task. */
|
|
7
|
+
export declare const claudeChildLayer: Layer.Layer<ChildBackend, never, ChildProcessSpawner.ChildProcessSpawner | ClaudeLoginLock | ClaudeSdk>;
|
|
8
|
+
/** Provides `ClaudeChildBackend` for direct use or harness-based dispatch. */
|
|
9
|
+
export declare const claudeChildBackendLayer: Layer.Layer<ClaudeChildBackend, never, ChildProcessSpawner.ChildProcessSpawner | ClaudeLoginLock | ClaudeSdk>;
|
|
10
|
+
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../src/pi/claude/claude.ts"],"names":[],"mappings":"AAiBA,OAAO,EAOL,KAAK,EAQN,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAEL,kBAAkB,EAClB,eAAe,EACf,YAAY,EAKb,MAAM,kBAAkB,CAAC;AAyB1B,OAAO,EAEL,SAAS,EAKV,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,SAAS,EACT,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,UAAU,CAAC;AAyyBlB,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,yGAA8C,CAAC;AAC5E,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,+GAAoD,CAAC"}
|