@usine/agents 0.2.0 → 0.4.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.
Files changed (94) hide show
  1. package/README.md +93 -29
  2. package/dist/child-host/child-host.d.ts +60 -0
  3. package/dist/child-host/child-host.d.ts.map +1 -0
  4. package/dist/child-host/child-host.js +84 -0
  5. package/dist/child-host/child-host.js.map +1 -0
  6. package/dist/child-host/command.d.ts +11 -0
  7. package/dist/child-host/command.d.ts.map +1 -0
  8. package/dist/child-host/command.js +26 -0
  9. package/dist/child-host/command.js.map +1 -0
  10. package/dist/pi/authentication.d.ts +11 -2
  11. package/dist/pi/authentication.d.ts.map +1 -1
  12. package/dist/pi/authentication.js +15 -2
  13. package/dist/pi/authentication.js.map +1 -1
  14. package/dist/pi/children.d.ts +1 -4
  15. package/dist/pi/children.d.ts.map +1 -1
  16. package/dist/pi/children.js +1 -4
  17. package/dist/pi/children.js.map +1 -1
  18. package/dist/pi/claude.d.ts +8 -9
  19. package/dist/pi/claude.d.ts.map +1 -1
  20. package/dist/pi/claude.js +413 -39
  21. package/dist/pi/claude.js.map +1 -1
  22. package/dist/pi/codex/account.d.ts +3 -3
  23. package/dist/pi/codex/account.d.ts.map +1 -1
  24. package/dist/pi/codex/account.js +5 -5
  25. package/dist/pi/codex/account.js.map +1 -1
  26. package/dist/pi/codex/app-server.d.ts +19 -7
  27. package/dist/pi/codex/app-server.d.ts.map +1 -1
  28. package/dist/pi/codex/app-server.js +41 -36
  29. package/dist/pi/codex/app-server.js.map +1 -1
  30. package/dist/pi/codex/codec.d.ts +118 -0
  31. package/dist/pi/codex/codec.d.ts.map +1 -0
  32. package/dist/pi/codex/codec.js +137 -0
  33. package/dist/pi/codex/codec.js.map +1 -0
  34. package/dist/pi/codex/codex.d.ts +0 -1
  35. package/dist/pi/codex/codex.d.ts.map +1 -1
  36. package/dist/pi/codex/codex.js +169 -133
  37. package/dist/pi/codex/codex.js.map +1 -1
  38. package/dist/pi/conversation.d.ts +43 -0
  39. package/dist/pi/conversation.d.ts.map +1 -0
  40. package/dist/pi/conversation.js +129 -0
  41. package/dist/pi/conversation.js.map +1 -0
  42. package/dist/pi/pi.d.ts +1 -4
  43. package/dist/pi/pi.d.ts.map +1 -1
  44. package/dist/pi/pi.js +38 -201
  45. package/dist/pi/pi.js.map +1 -1
  46. package/dist/pi/sdk.d.ts +11 -15
  47. package/dist/pi/sdk.d.ts.map +1 -1
  48. package/dist/pi/sdk.js +35 -53
  49. package/dist/pi/sdk.js.map +1 -1
  50. package/dist/pi/session.d.ts +13 -14
  51. package/dist/pi/session.d.ts.map +1 -1
  52. package/dist/pi/session.js +68 -16
  53. package/dist/pi/session.js.map +1 -1
  54. package/dist/pi/turns.d.ts +28 -0
  55. package/dist/pi/turns.d.ts.map +1 -0
  56. package/dist/pi/turns.js +67 -0
  57. package/dist/pi/turns.js.map +1 -0
  58. package/dist/pi/usage.d.ts +39 -0
  59. package/dist/pi/usage.d.ts.map +1 -0
  60. package/dist/pi/usage.js +96 -0
  61. package/dist/pi/usage.js.map +1 -0
  62. package/dist/preflight/command.d.ts +16 -3
  63. package/dist/preflight/command.d.ts.map +1 -1
  64. package/dist/preflight/command.js +30 -17
  65. package/dist/preflight/command.js.map +1 -1
  66. package/dist/preflight/preflight.d.ts +10 -5
  67. package/dist/preflight/preflight.d.ts.map +1 -1
  68. package/dist/preflight/preflight.js +52 -25
  69. package/dist/preflight/preflight.js.map +1 -1
  70. package/dist/prompt.d.ts +12 -9
  71. package/dist/prompt.d.ts.map +1 -1
  72. package/dist/prompt.js +33 -4
  73. package/dist/prompt.js.map +1 -1
  74. package/dist/protocol/codex.d.ts +4 -7
  75. package/dist/protocol/codex.d.ts.map +1 -1
  76. package/dist/protocol/codex.js +1 -1
  77. package/dist/protocol/codex.js.map +1 -1
  78. package/dist/runtime.d.ts +101 -40
  79. package/dist/runtime.d.ts.map +1 -1
  80. package/dist/runtime.js +108 -27
  81. package/dist/runtime.js.map +1 -1
  82. package/package.json +22 -10
  83. package/dist/pi/live.d.ts +0 -4
  84. package/dist/pi/live.d.ts.map +0 -1
  85. package/dist/pi/live.js +0 -11
  86. package/dist/pi/live.js.map +0 -1
  87. package/dist/pi/subagents.d.ts +0 -27
  88. package/dist/pi/subagents.d.ts.map +0 -1
  89. package/dist/pi/subagents.js +0 -113
  90. package/dist/pi/subagents.js.map +0 -1
  91. package/dist/pi/thinking.d.ts +0 -8
  92. package/dist/pi/thinking.d.ts.map +0 -1
  93. package/dist/pi/thinking.js +0 -15
  94. package/dist/pi/thinking.js.map +0 -1
package/README.md CHANGED
@@ -1,55 +1,119 @@
1
1
  # @usine/agents
2
2
 
3
- The agent runtime for [usine](https://github.com/basaltbytes/usine). Each Attempt starts an in-process Pi parent, which can delegate focused work through the usine-owned `subagent` tool. An agent's brain spec selects Claude Code, Codex, or Pi for the child.
3
+ The agent layer for [usine](https://github.com/basaltbytes/usine): the Claude Code, Codex, and Pi child backends that an attempt's Pi parent delegates to, their prompts, and harness preflight. The parent runs in the orchestrator as a durable task (`@usine/durable`); its `subagent` tool starts the child that the agent's brain spec selects. [Attempt execution](https://docs.usine.dev/architecture/execution/) describes each harness.
4
4
 
5
- `@usine/agents/prompt` and `@usine/agents/runtime` are platform-free: prompt assembly, typed runtime errors, and the `AgentRuntime` and `ChildBackend` service contracts. The Node implementation lives at `@usine/agents/pi/live`:
5
+ 0.x APIs can change between minor releases. Supply `effect` at the workspace's pinned version. Harness SDKs and protocols are exact-pinned.
6
6
 
7
- ```ts
8
- import { layer as agentLayer } from "@usine/agents/pi/live";
9
- ```
7
+ ## Compose the child backends
10
8
 
11
- Use `@usine/agents/pi` to compose the Pi parent with your own child backend:
9
+ `/prompt` and `/runtime` are platform-free: prompt assembly, typed runtime errors, and the `ChildBackend` contracts. `childDispatcherLayer` (`/children`) picks a backend from the agent's `harness`. Provide its three backends from `/claude`, `/codex`, and `/pi`, which also export their SDK contracts and production SDK layers. `@usine/durable/live` composes them:
12
10
 
13
11
  ```ts
14
12
  import { Layer } from "effect";
15
- import { piParentLayer, piSdkLive } from "@usine/agents/pi";
16
-
17
- // childBackendLayer supplies ChildBackend from @usine/agents/runtime.
18
- const agentLayer = piParentLayer.pipe(
19
- Layer.provide(piSdkLive),
20
- Layer.provide(childBackendLayer),
13
+ import { childDispatcherLayer } from "@usine/agents/children";
14
+ import { claudeChildBackendLayer, claudeSdkLive } from "@usine/agents/claude";
15
+ import { codexAppServerLive, codexChildBackendLayer } from "@usine/agents/codex";
16
+ import { piChildBackendLayer, piSdkLive } from "@usine/agents/pi";
17
+
18
+ const childBackendLayer = childDispatcherLayer.pipe(
19
+ Layer.provide(
20
+ Layer.mergeAll(
21
+ claudeChildBackendLayer.pipe(Layer.provide(claudeSdkLive)),
22
+ codexChildBackendLayer.pipe(Layer.provide(codexAppServerLive)),
23
+ piChildBackendLayer.pipe(Layer.provide(piSdkLive)),
24
+ ),
25
+ ),
21
26
  );
22
27
  ```
23
28
 
24
- `@usine/agents/children` provides `childDispatcherLayer`, which selects a child backend by harness. Compose its `ClaudeChildBackend`, `CodexChildBackend`, and `PiChildBackend` requirements with the adapter layers from `/claude`, `/codex`, and `/pi`. These Node entry points also provide their SDK service contracts and production SDK layers for caller-controlled composition. `/pi/live` assembles the parent and all three child backends.
29
+ The Claude backend also needs `ClaudeLoginLock` (`/runtime`), one per worker, which every Claude command that can refresh the worker's login takes. Give the same instance (`claudeLoginLockLayer` from `@usine/executor`) to the backends and the executor running preflight.
25
30
 
26
- The production layer resolves the Pi parent model from the Run spec, installs the subagents extension, and dispatches each child from the rostered `harness` field. The Run spec also defines the per-Attempt child-session budget. Every accepted spawn logs its cumulative count and a 160-character task excerpt; an excess spawn stops the parent with `ChildSessionBudgetExceededError`. The parent accepts API-key-backed Pi models and ChatGPT subscription models from the `openai-codex` provider. Claude Code runs the worker’s `claude` executable through the Agent SDK with `bypassPermissions` and reads only worker-user settings. Codex uses app-server JSON-RPC over standard input and output with `approvalPolicy: never`; it verifies ChatGPT authentication with `account/read` before creating an ephemeral thread and records ChatGPT plan rate-window updates as `PoolRateLimitsUpdated` session events. Pi children remain API-key-only.
31
+ ## Harnesses and credentials
27
32
 
28
- The Attempt worktree counts as repository-controlled input. Pi ignores project `.pi/` settings, extensions, prompt templates, skills, and themes. Claude omits project setting sources, while Codex marks the worktree untrusted before starting its thread. AGENTS instructions remain available to the coding harnesses. Worker isolation remains the executor's responsibility.
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
+ - **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.
36
+ - **Pi children:** API keys only.
29
37
 
30
- Parent and child output becomes typed session events: `SessionStarted`, `TextOutput`, tool calls, pool observations, and `SessionFinished`. Completed assistant text produces `TextOutput` events; streaming deltas don't enter the Attempt transcript. Parent and child controls support steer and interrupt. A child session always names one parent session, so recursive child events cannot enter the public model.
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.
31
39
 
32
- Model identifiers stay opaque until the selected harness checks its registry. Agent-reported failures use `AgentResultError`; SDK, process, and protocol failures use `SessionOperationError`. `toAttemptFailure` maps only `SessionOperationError` to retryable infrastructure failure, while `ChildSessionBudgetExceededError` becomes a non-retryable `ChildSessionBudgetFailure`.
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).
33
41
 
34
- Configure Pi with either a provider API key or ChatGPT Plus/Pro OAuth for the parent. Pi children require API keys; Claude and Codex children use their CLI login state. The parent-start log records `apiKeys` or `chatgptPlan` according to the resolved credential. The harness SDK and protocol dependencies use exact versions.
42
+ ## Child hosts
35
43
 
36
- `@usine/agents/preflight` provides the Node `preflight(brain)` operation. Supply a `ChildProcessSpawner` layer to check a worker without running a prompt. It accepts Codex CLI **0.144.0 or 0.153.4**, verifies the Codex ChatGPT account, checks `claude auth status --json` for a Claude subscription, and checks Pi’s model and API-key authentication through the installed SDK. `HarnessUnavailableError` identifies a binary, version, or login gap; `SessionOperationError` reports an incomplete check, including a timeout or failed SDK connection. Install and log in as the worker user before starting usine.
44
+ `ChildRequest.host` (`/child-host`) sets where a child runs, and `ChildRequest.worker` names that machine's worker:
37
45
 
38
- A Codex API-key login, absent login, or unsupported account response produces `PoolUnavailableError`, which requires operator action. The verified authentication method and pool appear in structured logs. An authentication change during a running Codex session ends it before further rate windows are recorded.
46
+ - `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`.
39
48
 
40
- The opt-in live contracts run in disposable directories. They steer a child during a waiting command, assert the changed response and event framing, and interrupt another active command. The Codex contract also requires a real rate-window snapshot. Use a supported Codex CLI with a ChatGPT login; the Pi contract requires API-key authentication for the specified provider.
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`.
41
50
 
42
- Run them with explicit models:
51
+ 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.
43
52
 
44
- ```sh
45
- USINE_CODEX_INTEGRATION_MODEL=gpt-5.6-terra pnpm --filter @usine/agents test:integration:codex
46
- USINE_PI_INTEGRATION_MODEL=anthropic/claude-sonnet-4-6 pnpm --filter @usine/agents test:integration:pi
53
+ ## Session events and operator actions
54
+
55
+ Child output becomes typed events: `SessionStarted`, `TextOutput` for completed assistant text (streaming deltas stay out of the transcript), tool calls, pool observations, and `SessionFinished`. Each child session has one parent session; recursive child events can't enter the public model.
56
+
57
+ `RunningSession.intervene(action, onDelivery)` applies steer, interrupt, and holds. A message to an idle or paused child starts a turn and emits `SessionResumed`. A message to a working child joins the turn and returns `InterventionState.queued`; `onDelivery` runs once, with the child's state when the harness reads it, or with none if the child stops first. Pi reads it when the queued steering message starts, Codex when it records a `userMessage` item, and Claude Code with a result whose `queued_turn_count` no longer counts it. A finished turn has read all its queued messages.
58
+
59
+ ## Claude login lock
60
+
61
+ A Claude child holds the worker's `ClaudeLoginLock` 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
+
63
+ 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
+
65
+ ## Background work
66
+
67
+ Claude children use the CLI's background-task snapshots and session-idle signal to wait for background work and its follow-up answer, without an operator hold. Queued user turns delay completion; ambient watchers don't. An output stream that closes without a completed answer fails with `SessionOperationError`.
68
+
69
+ 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
+
71
+ ## Outcome markers
72
+
73
+ An unresolved blocker starts the final answer with `[usine:blocked]`, then details and incomplete work on that line or the next:
74
+
75
+ ```text
76
+ [usine:blocked]
77
+ Repository permission denied; publishing remains incomplete.
47
78
  ```
48
79
 
49
- The protocol conformance gate retains 0.144.0 as its baseline. Codex 0.153.4 was also checked against its generated protocol types and the real steering/interruption contracts, including Astra. Other CLI builds require verification before they are accepted.
80
+ An operator-only decision starts it with `[usine:question]` and the question, which the prompt tells the agent to post on the item first:
50
81
 
51
- Each Codex child owns one app-server process. On completion or interruption, usine closes its input and waits for graceful shutdown before publishing the result, so background commands are released. Scope cleanup uses the same bounded shutdown and logs before forcing a stalled process to stop.
82
+ ```text
83
+ [usine:question]
84
+ Should egress use HTTP CONNECT or a SOCKS proxy? Both fit the gateway.
85
+ ```
52
86
 
53
- Everything here is 0.x, so APIs can change between minor releases. Supply `effect` at the version pinned by the usine workspace.
87
+ `reportedOutcome` reads markers as `AgentBlockedError` and `AgentQuestionError`, and `reportText` writes them. A parent's blocker becomes a non-retryable `BlockedFailure` and its question a non-retryable `QuestionFailure`: the run fails, its loop suppresses the item, and the run waits for the operator. A child's harness failure (`AgentResultError`) goes to the parent as a `[usine:failed]` report that `reportedOutcome` reads back, so the attempt ends with `AgentFailure` and suppresses nothing: the harness posted nothing.
88
+
89
+ A child's report returns to the parent's `subagent` call as text opening with its marker, to return verbatim. The parent keeps the first: a later ordinary answer fails with it, and the paraphrase stays in the transcript. Successful siblings don't clear it. Held children can take guidance and replace their answer before release. A marker without details still fails.
90
+
91
+ Model ids stay opaque until the harness checks its registry. Harness failures use `AgentResultError`. `toAttemptFailure` maps only `SessionOperationError` (SDK, process, and protocol failures) to a retryable infrastructure failure; `ChildSessionBudgetExceededError` becomes a non-retryable `ChildSessionBudgetFailure`.
92
+
93
+ ## GitHub identity and reviews
94
+
95
+ Agents share the installation's GitHub App identity. The instructions require comment reviews at the reviewed head and the loop's outcome labels. A success label doesn't satisfy GitHub's required review: an independent reviewer approves and the operator merges. See [Write review and fix loops](https://docs.usine.dev/guides/review-fix-loops/).
96
+
97
+ ## Resume a harness session
98
+
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.
100
+
101
+ ## Preflight
102
+
103
+ `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
+
105
+ It accepts Codex CLI **0.144.0 or 0.153.4** 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
+
107
+ ## Live contract tests
108
+
109
+ The opt-in live contracts, in disposable directories, steer a child during a waiting command, checking the response and event framing, and interrupt another; Codex also needs a real rate-window snapshot. Claude and Codex cases also run through `test/fixtures/ssh-shim.sh`, an SSH stand-in that runs the remote command locally, to check it, its environment, and its pid file with the real CLIs.
110
+
111
+ They need a supported Codex CLI with a ChatGPT login, a Claude CLI with a subscription login, an API key for the Pi provider, and explicit models:
112
+
113
+ ```sh
114
+ USINE_CLAUDE_INTEGRATION_MODEL=claude-sonnet-5-5 pnpm --filter @usine/agents test:integration:claude
115
+ USINE_CODEX_INTEGRATION_MODEL=gpt-5.6-terra pnpm --filter @usine/agents test:integration:codex
116
+ USINE_PI_INTEGRATION_MODEL=anthropic/claude-sonnet-4-6 pnpm --filter @usine/agents test:integration:pi
117
+ ```
54
118
 
55
- Agents share the installation’s GitHub App identity. Runtime instructions require comment reviews at the reviewed head commit and the outcome labels defined by the Loop. An agent success label does not satisfy GitHub’s required-review rule; an independent reviewer supplies required approval and the operator merges. See the [review/fix guide](../../docs/guides/review-fix-loops.md).
119
+ The protocol conformance baseline is 0.144.0. Codex 0.153.4 also passed its generated protocol types and the real steering and interruption contracts, including Astra. Other CLI builds need verification first.
@@ -0,0 +1,60 @@
1
+ import type { AgentSessionId } from "@usine/core/agent/session";
2
+ import { Schema } from "effect";
3
+ declare const LocalProcess_base: Schema.Class<LocalProcess, Schema.TaggedStruct<"LocalProcess", {}>, {}>;
4
+ /** A host that runs children as local processes. */
5
+ export declare class LocalProcess extends LocalProcess_base {
6
+ }
7
+ /** The local SSH executable and the options before the destination. */
8
+ export declare const SshCommand: Schema.Struct<{
9
+ readonly command: Schema.NonEmptyString;
10
+ readonly args: Schema.$Array<Schema.String>;
11
+ }>;
12
+ export type SshCommand = typeof SshCommand.Type;
13
+ declare const RemoteShell_base: Schema.Class<RemoteShell, Schema.TaggedStruct<"RemoteShell", {
14
+ readonly ssh: Schema.Struct<{
15
+ readonly command: Schema.NonEmptyString;
16
+ readonly args: Schema.$Array<Schema.String>;
17
+ }>;
18
+ /**
19
+ * The SSH client's process environment: what SSH needs to connect, never the orchestrator's
20
+ * secrets (ADR 0011), plus the variables that `-o SendEnv=` options in `ssh` forward to the
21
+ * child. Use it for values that must not appear on the command line, such as a Gateway caller
22
+ * token. Never log this field.
23
+ */
24
+ readonly sshEnvironment: Schema.$Record<Schema.String, Schema.String>;
25
+ /** The SSH destination, passed as one argument. */
26
+ readonly destination: Schema.NonEmptyString;
27
+ }>, {}>;
28
+ /** A host that runs each child on another machine as one SSH command, through its standard streams. */
29
+ export declare class RemoteShell extends RemoteShell_base {
30
+ }
31
+ /** The host that runs an Attempt's children, chosen by the Worker that prepared its checkout. */
32
+ export declare const ChildHost: Schema.Union<readonly [typeof LocalProcess, typeof RemoteShell]>;
33
+ export type ChildHost = typeof ChildHost.Type;
34
+ /** A command for a host to run. */
35
+ export interface HostInvocation {
36
+ /** The working directory; defaults to the host user's home directory. */
37
+ readonly cwd?: string | undefined;
38
+ /**
39
+ * The command's complete environment on a local host, or the variables exported on top of a remote
40
+ * shell's environment; `undefined` values are skipped.
41
+ */
42
+ readonly environment?: Readonly<Record<string, string | undefined>> | undefined;
43
+ /** On a remote shell, the file that contains the command's PID while it runs, so cleanup can stop it after the SSH client exits. */
44
+ readonly pidFile?: string | undefined;
45
+ readonly command: string;
46
+ readonly args: ReadonlyArray<string>;
47
+ }
48
+ /** Returns the SSH client arguments that run `invocation`: the options, the destination, and one remote command line. */
49
+ export declare const sshArguments: (host: RemoteShell, invocation: HostInvocation) => Array<string>;
50
+ /** Returns the PID file path of a remote child: next to its worktree, named by the child number in its session ID. */
51
+ export declare const childPidFile: (worktree: string, session: AgentSessionId) => string;
52
+ /**
53
+ * A shell script that stops an Attempt's remaining remote children before its worktree is kept or
54
+ * removed: for each `<worktree>.child-*.pid`, it sends TERM, waits, then sends KILL. Run it with
55
+ * `sh -c` and the worktree as `$1`; it prints one outcome per PID file. An exited SSH client does
56
+ * not mean that its remote command stopped.
57
+ */
58
+ export declare const CHILD_KILL_SCRIPT: string;
59
+ export {};
60
+ //# sourceMappingURL=child-host.d.ts.map
@@ -0,0 +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;;;;;;IAO5C;;;;;OAKG;;IAEH,mDAAmD;;;AAZvD,uGAAuG;AACvG,qBAAa,WAAY,SAAQ,gBAchC;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,yHAAyH;AACzH,eAAO,MAAM,YAAY,SAAU,WAAW,cAAc,cAAc,KAAG,KAAK,CAAC,MAAM,CAKxF,CAAC;AAEF,sHAAsH;AACtH,eAAO,MAAM,YAAY,aAAc,MAAM,WAAW,cAAc,KAAG,MACD,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,QAgBtB,CAAC"}
@@ -0,0 +1,84 @@
1
+ import { Schema } from "effect";
2
+ /** A host that runs children as local processes. */
3
+ export class LocalProcess extends Schema.TaggedClass("@usine/agents/LocalProcess")("LocalProcess", {}) {
4
+ }
5
+ /** The local SSH executable and the options before the destination. */
6
+ export const SshCommand = Schema.Struct({
7
+ command: Schema.NonEmptyString,
8
+ args: Schema.Array(Schema.String),
9
+ });
10
+ /** A host that runs each child on another machine as one SSH command, through its standard streams. */
11
+ export class RemoteShell extends Schema.TaggedClass("@usine/agents/RemoteShell")("RemoteShell", {
12
+ ssh: SshCommand,
13
+ /**
14
+ * The SSH client's process environment: what SSH needs to connect, never the orchestrator's
15
+ * secrets (ADR 0011), plus the variables that `-o SendEnv=` options in `ssh` forward to the
16
+ * child. Use it for values that must not appear on the command line, such as a Gateway caller
17
+ * token. Never log this field.
18
+ */
19
+ sshEnvironment: Schema.Record(Schema.String, Schema.String),
20
+ /** The SSH destination, passed as one argument. */
21
+ destination: Schema.NonEmptyString,
22
+ }) {
23
+ }
24
+ /** The host that runs an Attempt's children, chosen by the Worker that prepared its checkout. */
25
+ export const ChildHost = Schema.Union([LocalProcess, RemoteShell]);
26
+ const VARIABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u;
27
+ const quote = (value) => `'${value.replaceAll("'", `'"'"'`)}'`;
28
+ const quoted = (words) => words.map(quote).join(" ");
29
+ /**
30
+ * Returns the remote POSIX shell command line for `invocation`, with every word quoted so it passes
31
+ * through OpenSSH unchanged. Throws if a variable name is not a shell identifier.
32
+ */
33
+ const remoteShellCommand = (invocation) => {
34
+ const exports = Object.entries(invocation.environment ?? {}).flatMap(([name, value]) => {
35
+ if (!VARIABLE_NAME.test(name))
36
+ throw new Error(`Cannot export ${name} in a remote shell`);
37
+ return value === undefined ? [] : [`${name}=${quote(value)}`];
38
+ });
39
+ const setup = [
40
+ ...(invocation.cwd === undefined ? [] : [`cd ${quote(invocation.cwd)}`]),
41
+ ...(exports.length === 0 ? [] : [`export ${exports.join(" ")}`]),
42
+ ];
43
+ const command = [invocation.command, ...invocation.args];
44
+ if (invocation.pidFile === undefined)
45
+ return [...setup, `exec ${quoted(command)}`].join(" && ");
46
+ // The inner shell writes its PID, then execs the command. The file exists only while the command
47
+ // runs, so cleanup never signals a reused PID.
48
+ const pidFile = quote(invocation.pidFile);
49
+ const run = `sh -c 'printf %s "$$" > "$0" && exec "$@"' ${pidFile} ${quoted(command)}`;
50
+ return `${[...setup, run].join(" && ")}; code=$?; rm -f ${pidFile}; exit "$code"`;
51
+ };
52
+ /** Returns the SSH client arguments that run `invocation`: the options, the destination, and one remote command line. */
53
+ export const sshArguments = (host, invocation) => [
54
+ ...host.ssh.args,
55
+ host.destination,
56
+ "--",
57
+ remoteShellCommand(invocation),
58
+ ];
59
+ /** Returns the PID file path of a remote child: next to its worktree, named by the child number in its session ID. */
60
+ export const childPidFile = (worktree, session) => `${worktree}.child-${session.slice(session.lastIndexOf(":") + 1)}.pid`;
61
+ /**
62
+ * A shell script that stops an Attempt's remaining remote children before its worktree is kept or
63
+ * removed: for each `<worktree>.child-*.pid`, it sends TERM, waits, then sends KILL. Run it with
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.
66
+ */
67
+ export const CHILD_KILL_SCRIPT = `
68
+ found=0
69
+ for pidfile in "$1".child-*.pid; do
70
+ test -f "$pidfile" || continue
71
+ found=1
72
+ pid=$(cat "$pidfile" 2>/dev/null)
73
+ if [ -n "$pid" ] && kill -TERM "$pid" 2>/dev/null; then
74
+ n=0
75
+ while kill -0 "$pid" 2>/dev/null && [ "$n" -lt 10 ]; do sleep 0.5; n=$((n+1)); done
76
+ if kill -0 "$pid" 2>/dev/null; then kill -KILL "$pid" 2>/dev/null; echo "$pidfile killed"; else echo "$pidfile stopped"; fi
77
+ else
78
+ echo "$pidfile absent"
79
+ fi
80
+ rm -f "$pidfile"
81
+ done
82
+ test "$found" = 1 || echo no-children
83
+ `.trim();
84
+ //# sourceMappingURL=child-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-host.js","sourceRoot":"","sources":["../../src/child-host/child-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { ChildProcess } from "effect/unstable/process";
2
+ import { type ChildHost, type HostInvocation } from "./child-host.ts";
3
+ /**
4
+ * Returns the local process command that runs `invocation` on `host`: the command itself with
5
+ * exactly `environment` on a local host, or one SSH command whose client gets only `sshEnvironment`
6
+ * on a remote host.
7
+ */
8
+ export declare const hostCommand: (host: ChildHost, invocation: HostInvocation & {
9
+ readonly environment: Readonly<Record<string, string>>;
10
+ }, options: ChildProcess.CommandOptions) => ChildProcess.Command;
11
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/child-host/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,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"}
@@ -0,0 +1,26 @@
1
+ import { ChildProcess } from "effect/unstable/process";
2
+ import { homedir } from "node:os";
3
+ import { sshArguments } from "./child-host.js";
4
+ /**
5
+ * Returns the local process command that runs `invocation` on `host`: the command itself with
6
+ * exactly `environment` on a local host, or one SSH command whose client gets only `sshEnvironment`
7
+ * on a remote host.
8
+ */
9
+ export const hostCommand = (host, invocation, options) => {
10
+ switch (host._tag) {
11
+ case "LocalProcess":
12
+ return ChildProcess.make(invocation.command, invocation.args, {
13
+ ...options,
14
+ cwd: invocation.cwd ?? homedir(),
15
+ env: { ...invocation.environment },
16
+ extendEnv: false,
17
+ });
18
+ case "RemoteShell":
19
+ return ChildProcess.make(host.ssh.command, sshArguments(host, invocation), {
20
+ ...options,
21
+ env: { ...host.sshEnvironment },
22
+ extendEnv: false,
23
+ });
24
+ }
25
+ };
26
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/child-host/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,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"}
@@ -6,8 +6,17 @@ export type PiAuthentication = {
6
6
  readonly subscription: boolean;
7
7
  };
8
8
  export type PiPool = Extract<BillingPool, "apiKeys" | "chatgptPlan">;
9
- /** The billing pool the chosen Pi provider spends from, checked before launch. */
9
+ /** Splits `provider/model` into provider and model ID; `undefined` unless both parts are present. */
10
+ export declare const splitPiModel: (model: string) => {
11
+ readonly provider: string;
12
+ readonly modelId: string;
13
+ } | undefined;
14
+ /** Converts Pi's credential check, with `subscription` set if the OAuth login is a subscription; returns `undefined` if Pi found no credential. */
15
+ export declare const piAuthentication: (check: {
16
+ readonly type: "api_key" | "oauth";
17
+ } | undefined, subscription: boolean) => PiAuthentication | undefined;
18
+ /** Returns the billing pool that a Pi provider requires; checked before launch. */
10
19
  export declare const requiredPiPool: (provider: string) => PiPool;
11
- /** Returns the billing pool of the credential that pays for the model request. */
20
+ /** Returns the billing pool of the credential used for the model request, or `undefined` if it is neither an API key nor a subscription login to `openai-codex`. */
12
21
  export declare const resolvePiPool: (provider: string, authentication: PiAuthentication | undefined) => PiPool | undefined;
13
22
  //# sourceMappingURL=authentication.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"authentication.d.ts","sourceRoot":"","sources":["../../src/pi/authentication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAAC;AAC/D,MAAM,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,SAAS,GAAG,aAAa,CAAC,CAAC;AAErE,kFAAkF;AAClF,eAAO,MAAM,cAAc,aAAc,MAAM,KAAG,MACO,CAAC;AAE1D,kFAAkF;AAClF,eAAO,MAAM,aAAa,aACd,MAAM,kBACA,gBAAgB,GAAG,SAAS,KAC3C,MAAM,GAAG,SAUX,CAAC"}
1
+ {"version":3,"file":"authentication.d.ts","sourceRoot":"","sources":["../../src/pi/authentication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAAC;AAC/D,MAAM,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,SAAS,GAAG,aAAa,CAAC,CAAC;AAErE,qGAAqG;AACrG,eAAO,MAAM,YAAY,UAChB,MAAM,KACZ;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAK5D,CAAC;AAEF,mJAAmJ;AACnJ,eAAO,MAAM,gBAAgB,UACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAA;CAAE,GAAG,SAAS,gBAC3C,OAAO,KACpB,gBAAgB,GAAG,SAKL,CAAC;AAElB,mFAAmF;AACnF,eAAO,MAAM,cAAc,aAAc,MAAM,KAAG,MACO,CAAC;AAE1D,oKAAoK;AACpK,eAAO,MAAM,aAAa,aACd,MAAM,kBACA,gBAAgB,GAAG,SAAS,KAC3C,MAAM,GAAG,SAUX,CAAC"}
@@ -1,6 +1,19 @@
1
- /** The billing pool the chosen Pi provider spends from, checked before launch. */
1
+ /** Splits `provider/model` into provider and model ID; `undefined` unless both parts are present. */
2
+ export const splitPiModel = (model) => {
3
+ const separator = model.indexOf("/");
4
+ return separator <= 0 || separator === model.length - 1
5
+ ? undefined
6
+ : { provider: model.slice(0, separator), modelId: model.slice(separator + 1) };
7
+ };
8
+ /** Converts Pi's credential check, with `subscription` set if the OAuth login is a subscription; returns `undefined` if Pi found no credential. */
9
+ export const piAuthentication = (check, subscription) => check?.type === "api_key"
10
+ ? { _tag: "ApiKey" }
11
+ : check?.type === "oauth"
12
+ ? { _tag: "OAuth", subscription }
13
+ : undefined;
14
+ /** Returns the billing pool that a Pi provider requires; checked before launch. */
2
15
  export const requiredPiPool = (provider) => provider === "openai-codex" ? "chatgptPlan" : "apiKeys";
3
- /** Returns the billing pool of the credential that pays for the model request. */
16
+ /** Returns the billing pool of the credential used for the model request, or `undefined` if it is neither an API key nor a subscription login to `openai-codex`. */
4
17
  export const resolvePiPool = (provider, authentication) => {
5
18
  if (authentication?._tag === "ApiKey")
6
19
  return "apiKeys";
@@ -1 +1 @@
1
- {"version":3,"file":"authentication.js","sourceRoot":"","sources":["../../src/pi/authentication.ts"],"names":[],"mappings":"AAOA,kFAAkF;AAClF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAgB,EAAU,EAAE,CACzD,QAAQ,KAAK,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;AAE1D,kFAAkF;AAClF,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,QAAgB,EAChB,cAA4C,EACxB,EAAE;IACtB,IAAI,cAAc,EAAE,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACxD,IACE,cAAc,EAAE,IAAI,KAAK,OAAO;QAChC,cAAc,CAAC,YAAY;QAC3B,QAAQ,KAAK,cAAc,EAC3B,CAAC;QACD,OAAO,aAAa,CAAC;IACvB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC"}
1
+ {"version":3,"file":"authentication.js","sourceRoot":"","sources":["../../src/pi/authentication.ts"],"names":[],"mappings":"AAOA,qGAAqG;AACrG,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,KAAa,EACwD,EAAE;IACvE,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,OAAO,SAAS,IAAI,CAAC,IAAI,SAAS,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC;QACrD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC;AACnF,CAAC,CAAC;AAEF,mJAAmJ;AACnJ,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,KAAyD,EACzD,YAAqB,EACS,EAAE,CAChC,KAAK,EAAE,IAAI,KAAK,SAAS;IACvB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;IACpB,CAAC,CAAC,KAAK,EAAE,IAAI,KAAK,OAAO;QACvB,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE;QACjC,CAAC,CAAC,SAAS,CAAC;AAElB,mFAAmF;AACnF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAgB,EAAU,EAAE,CACzD,QAAQ,KAAK,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;AAE1D,oKAAoK;AACpK,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,QAAgB,EAChB,cAA4C,EACxB,EAAE;IACtB,IAAI,cAAc,EAAE,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACxD,IACE,cAAc,EAAE,IAAI,KAAK,OAAO;QAChC,cAAc,CAAC,YAAY;QAC3B,QAAQ,KAAK,cAAc,EAC3B,CAAC;QACD,OAAO,aAAa,CAAC;IACvB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC"}
@@ -1,8 +1,5 @@
1
1
  import { Layer } from "effect";
2
2
  import { ChildBackend, ClaudeChildBackend, CodexChildBackend, PiChildBackend } from "../runtime.ts";
3
- /**
4
- * Selects Claude Code, Codex, or Pi for each delegated task from the Agent's harness.
5
- * Supply the three backend layers here, or use a concrete backend directly.
6
- */
3
+ /** Dispatches each delegated task to the Claude Code, Codex, or Pi backend according to the Agent's harness; requires all three backend layers. */
7
4
  export declare const childDispatcherLayer: Layer.Layer<ChildBackend, never, ClaudeChildBackend | CodexChildBackend | PiChildBackend>;
8
5
  //# sourceMappingURL=children.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"children.d.ts","sourceRoot":"","sources":["../../src/pi/children.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAyBpG;;;GAGG;AACH,eAAO,MAAM,oBAAoB,2FAAkD,CAAC"}
1
+ {"version":3,"file":"children.d.ts","sourceRoot":"","sources":["../../src/pi/children.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAyBpG,mJAAmJ;AACnJ,eAAO,MAAM,oBAAoB,2FAAkD,CAAC"}
@@ -21,9 +21,6 @@ const makeChildDispatcher = Effect.gen(function* () {
21
21
  }),
22
22
  });
23
23
  });
24
- /**
25
- * Selects Claude Code, Codex, or Pi for each delegated task from the Agent's harness.
26
- * Supply the three backend layers here, or use a concrete backend directly.
27
- */
24
+ /** Dispatches each delegated task to the Claude Code, Codex, or Pi backend according to the Agent's harness; requires all three backend layers. */
28
25
  export const childDispatcherLayer = Layer.effect(ChildBackend, makeChildDispatcher);
29
26
  //# sourceMappingURL=children.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"children.js","sourceRoot":"","sources":["../../src/pi/children.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpG,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC9C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,kBAAkB,CAAC;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC;IACvC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IAEjC,OAAO,YAAY,CAAC,EAAE,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,qBAAqB,CAAC,CAAC,QAAQ,CAAC,EAAE,OAAO;YACzD,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,2BAA2B,EAAE;gBAClD,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO;gBACtC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;aAC9B,CAAC,CAAC;YACH,QAAQ,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBACtC,KAAK,QAAQ;oBACX,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACvC,KAAK,OAAO;oBACV,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACtC,KAAK,IAAI;oBACP,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrC,CAAC;QACH,CAAC,CAAC;KACH,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC,MAAM,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC"}
1
+ {"version":3,"file":"children.js","sourceRoot":"","sources":["../../src/pi/children.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpG,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC9C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,kBAAkB,CAAC;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC;IACvC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IAEjC,OAAO,YAAY,CAAC,EAAE,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,qBAAqB,CAAC,CAAC,QAAQ,CAAC,EAAE,OAAO;YACzD,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,2BAA2B,EAAE;gBAClD,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO;gBACtC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;aAC9B,CAAC,CAAC;YACH,QAAQ,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBACtC,KAAK,QAAQ;oBACX,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACvC,KAAK,OAAO;oBACV,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACtC,KAAK,IAAI;oBACP,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrC,CAAC;QACH,CAAC,CAAC;KACH,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,mJAAmJ;AACnJ,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC,MAAM,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { type Options } from "@anthropic-ai/claude-agent-sdk";
2
2
  import { Context, Layer, Schema } from "effect";
3
- import { ClaudeChildBackend, ChildBackend } from "../runtime.ts";
3
+ import { ClaudeChildBackend, ClaudeLoginLock, ChildBackend } from "../runtime.ts";
4
4
  export interface ClaudeUserMessage {
5
5
  readonly type: "user";
6
6
  readonly message: {
@@ -9,12 +9,12 @@ export interface ClaudeUserMessage {
9
9
  };
10
10
  readonly parent_tool_use_id: null;
11
11
  }
12
- export type ClaudeQueryOptions = Pick<Options, "allowDangerouslySkipPermissions" | "cwd" | "effort" | "model" | "pathToClaudeCodeExecutable" | "permissionMode" | "persistSession" | "settingSources" | "thinking">;
12
+ export type ClaudeQueryOptions = Pick<Options, "allowDangerouslySkipPermissions" | "cwd" | "effort" | "env" | "model" | "pathToClaudeCodeExecutable" | "permissionMode" | "persistSession" | "settingSources" | "spawnClaudeCodeProcess" | "thinking" | "hooks">;
13
13
  export interface ClaudeModelInfo {
14
14
  readonly value: string;
15
15
  readonly resolvedModel?: string;
16
16
  }
17
- /** A live Claude Code session with its messages, model discovery, and stop controls. */
17
+ /** A running Claude Code session: its message stream, model list, and stop controls. */
18
18
  export interface ClaudeQuery extends AsyncIterable<Schema.Json> {
19
19
  readonly close: () => void;
20
20
  readonly interrupt: () => Promise<void>;
@@ -23,14 +23,13 @@ export interface ClaudeQuery extends AsyncIterable<Schema.Json> {
23
23
  declare const ClaudeSdk_base: Context.ServiceClass<ClaudeSdk, "@usine/agents/pi/ClaudeSdk", {
24
24
  readonly open: (input: AsyncIterable<ClaudeUserMessage>, options: ClaudeQueryOptions) => ClaudeQuery;
25
25
  }>;
26
- /** Opens Claude Code sessions with the supplied task input and session settings. */
26
+ /** The service that opens Claude Code sessions. */
27
27
  export declare class ClaudeSdk extends ClaudeSdk_base {
28
28
  }
29
- /** Provides Claude Code's session creation and model discovery. */
30
29
  export declare const claudeSdkLive: Layer.Layer<ClaudeSdk, never, never>;
31
- /** Uses Claude Code for every task delegated through `ChildBackend`. */
32
- export declare const claudeChildLayer: Layer.Layer<ChildBackend, never, ClaudeSdk>;
33
- /** Provides the Claude Code backend for direct use or harness-based dispatch. */
34
- export declare const claudeChildBackendLayer: Layer.Layer<ClaudeChildBackend, never, ClaudeSdk>;
30
+ /** Provides `ChildBackend` with Claude Code for every delegated task. */
31
+ export declare const claudeChildLayer: Layer.Layer<ChildBackend, never, ClaudeLoginLock | ClaudeSdk>;
32
+ /** Provides `ClaudeChildBackend` for direct use or harness-based dispatch. */
33
+ export declare const claudeChildBackendLayer: Layer.Layer<ClaudeChildBackend, never, ClaudeLoginLock | ClaudeSdk>;
35
34
  export {};
36
35
  //# sourceMappingURL=claude.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/pi/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,OAAO,EAGb,MAAM,gCAAgC,CAAC;AAUxC,OAAO,EAAE,OAAO,EAAoB,KAAK,EAAU,MAAM,EAAU,MAAM,QAAQ,CAAC;AAClF,OAAO,EAEL,kBAAkB,EAClB,YAAY,EAKb,MAAM,eAAe,CAAC;AAGvB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,QAAQ,CAAC,kBAAkB,EAAE,IAAI,CAAC;CACnC;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,OAAO,EACL,iCAAiC,GACjC,KAAK,GACL,QAAQ,GACR,OAAO,GACP,4BAA4B,GAC5B,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,UAAU,CACb,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,wFAAwF;AACxF,MAAM,WAAW,WAAY,SAAQ,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC;CACzE;;mBAMkB,CACb,KAAK,EAAE,aAAa,CAAC,iBAAiB,CAAC,EACvC,OAAO,EAAE,kBAAkB,KACxB,WAAW;;AAPpB,oFAAoF;AACpF,qBAAa,SAAU,SAAQ,cAQE;CAAG;AAOpC,mEAAmE;AACnE,eAAO,MAAM,aAAa,sCAexB,CAAC;AAkRH,wEAAwE;AACxE,eAAO,MAAM,gBAAgB,6CAA8C,CAAC;AAC5E,iFAAiF;AACjF,eAAO,MAAM,uBAAuB,mDAAoD,CAAC"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/pi/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,OAAO,EAMb,MAAM,gCAAgC,CAAC;AAiBxC,OAAO,EAEL,OAAO,EAMP,KAAK,EAKL,MAAM,EAGP,MAAM,QAAQ,CAAC;AAChB,OAAO,EAEL,kBAAkB,EAClB,eAAe,EACf,YAAY,EAKb,MAAM,eAAe,CAAC;AAMvB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,QAAQ,CAAC,kBAAkB,EAAE,IAAI,CAAC;CACnC;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,OAAO,EACL,iCAAiC,GACjC,KAAK,GACL,QAAQ,GACR,KAAK,GACL,OAAO,GACP,4BAA4B,GAC5B,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,wBAAwB,GACxB,UAAU,GACV,OAAO,CACV,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,wFAAwF;AACxF,MAAM,WAAW,WAAY,SAAQ,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC;CACzE;;mBAMkB,CACb,KAAK,EAAE,aAAa,CAAC,iBAAiB,CAAC,EACvC,OAAO,EAAE,kBAAkB,KACxB,WAAW;;AAPpB,mDAAmD;AACnD,qBAAa,SAAU,SAAQ,cAQE;CAAG;AAOpC,eAAO,MAAM,aAAa,sCAexB,CAAC;AAsvBH,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,+DAA8C,CAAC;AAC5E,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,qEAAoD,CAAC"}