@usine/durable 0.0.0-stage → 0.1.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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -2
  3. package/dist/attempt/documents.d.ts +86 -0
  4. package/dist/attempt/documents.d.ts.map +1 -0
  5. package/dist/attempt/documents.js +30 -0
  6. package/dist/attempt/documents.js.map +1 -0
  7. package/dist/attempt/events.d.ts +22 -0
  8. package/dist/attempt/events.d.ts.map +1 -0
  9. package/dist/attempt/events.js +110 -0
  10. package/dist/attempt/events.js.map +1 -0
  11. package/dist/attempt/extension.d.ts +48 -0
  12. package/dist/attempt/extension.d.ts.map +1 -0
  13. package/dist/attempt/extension.js +34 -0
  14. package/dist/attempt/extension.js.map +1 -0
  15. package/dist/attempt/hubs.d.ts +117 -0
  16. package/dist/attempt/hubs.d.ts.map +1 -0
  17. package/dist/attempt/hubs.js +137 -0
  18. package/dist/attempt/hubs.js.map +1 -0
  19. package/dist/attempt/outcome.d.ts +22 -0
  20. package/dist/attempt/outcome.d.ts.map +1 -0
  21. package/dist/attempt/outcome.js +84 -0
  22. package/dist/attempt/outcome.js.map +1 -0
  23. package/dist/attempt/parent.d.ts +27 -0
  24. package/dist/attempt/parent.d.ts.map +1 -0
  25. package/dist/attempt/parent.js +109 -0
  26. package/dist/attempt/parent.js.map +1 -0
  27. package/dist/attempt/relay.d.ts +41 -0
  28. package/dist/attempt/relay.d.ts.map +1 -0
  29. package/dist/attempt/relay.js +457 -0
  30. package/dist/attempt/relay.js.map +1 -0
  31. package/dist/attempt/repair.d.ts +8 -0
  32. package/dist/attempt/repair.d.ts.map +1 -0
  33. package/dist/attempt/repair.js +130 -0
  34. package/dist/attempt/repair.js.map +1 -0
  35. package/dist/attempt/request.d.ts +18 -0
  36. package/dist/attempt/request.d.ts.map +1 -0
  37. package/dist/attempt/request.js +24 -0
  38. package/dist/attempt/request.js.map +1 -0
  39. package/dist/attempt/runtime.d.ts +10 -0
  40. package/dist/attempt/runtime.d.ts.map +1 -0
  41. package/dist/attempt/runtime.js +422 -0
  42. package/dist/attempt/runtime.js.map +1 -0
  43. package/dist/attempt/services.d.ts +15 -0
  44. package/dist/attempt/services.d.ts.map +1 -0
  45. package/dist/attempt/services.js +2 -0
  46. package/dist/attempt/services.js.map +1 -0
  47. package/dist/attempt/subagent.d.ts +17 -0
  48. package/dist/attempt/subagent.d.ts.map +1 -0
  49. package/dist/attempt/subagent.js +203 -0
  50. package/dist/attempt/subagent.js.map +1 -0
  51. package/dist/attempt/task.d.ts +61 -0
  52. package/dist/attempt/task.d.ts.map +1 -0
  53. package/dist/attempt/task.js +302 -0
  54. package/dist/attempt/task.js.map +1 -0
  55. package/dist/attempt/watch.d.ts +8 -0
  56. package/dist/attempt/watch.d.ts.map +1 -0
  57. package/dist/attempt/watch.js +25 -0
  58. package/dist/attempt/watch.js.map +1 -0
  59. package/dist/attempt-runtime.d.ts +123 -0
  60. package/dist/attempt-runtime.d.ts.map +1 -0
  61. package/dist/attempt-runtime.js +73 -0
  62. package/dist/attempt-runtime.js.map +1 -0
  63. package/dist/backup.d.ts +26 -0
  64. package/dist/backup.d.ts.map +1 -0
  65. package/dist/backup.js +100 -0
  66. package/dist/backup.js.map +1 -0
  67. package/dist/harness.d.ts +87 -0
  68. package/dist/harness.d.ts.map +1 -0
  69. package/dist/harness.js +271 -0
  70. package/dist/harness.js.map +1 -0
  71. package/dist/live.d.ts +15 -0
  72. package/dist/live.d.ts.map +1 -0
  73. package/dist/live.js +51 -0
  74. package/dist/live.js.map +1 -0
  75. package/dist/storage-path.d.ts +4 -0
  76. package/dist/storage-path.d.ts.map +1 -0
  77. package/dist/storage-path.js +2 -0
  78. package/dist/storage-path.js.map +1 -0
  79. package/dist/testing.d.ts +111 -0
  80. package/dist/testing.d.ts.map +1 -0
  81. package/dist/testing.js +303 -0
  82. package/dist/testing.js.map +1 -0
  83. package/package.json +72 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Philippe L'ATTENTION
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,82 @@
1
- # Temporary Holding Version
1
+ # @usine/durable
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Runs an attempt's Pi parent in the orchestrator on [`@earendil-works/pi-durable`](https://www.npmjs.com/package/@earendil-works/pi-durable) storage. Each attempt is one durable task, `usine.attempt`, with one parent conversation. The parent's only tool, `subagent`, starts the agent's Claude Code, Codex, or Pi child in the attempt's checkout. After an orchestrator restart, unfinished attempts continue from their last committed step. [Attempt execution](https://docs.usine.dev/architecture/execution/) covers the durable layer and live channel.
4
+
5
+ ## Entry points
6
+
7
+ - `/attempt-runtime`: the `AttemptRuntime` contract that the executor calls, with `AttemptRequest`, `StoredRequest`, and `AttemptReport`.
8
+ - `/harness`: the `DurableHarness` service, which manages the open pi-durable harness, and its platform-free `layer(options)`.
9
+ - `/backup`: `backup(instanceDirectory, destination)`, an online copy of `.usine/durable.sqlite` through SQLite's backup API, hard-linked into place so it never overwrites a file. `usine backup` writes it next to the runtime database copy.
10
+ - `/live`: the Node composition: the harness on `<instance>/.usine/durable.sqlite`, Pi's configured models and credentials, and the child backends from `@usine/agents`.
11
+ - `/testing`: a scripted parent model and stub children for whole-attempt tests, and a stub `AttemptRuntime` for executor tests.
12
+
13
+ ```ts
14
+ import { layer as durableLayer } from "@usine/durable/live";
15
+
16
+ // Provides AttemptRuntime and DurableHarness; requires ChildProcessSpawner and ClaudeLoginLock,
17
+ // the per-Worker lock the executor's preflight shares (claudeLoginLockLayer from @usine/executor).
18
+ const attempts = durableLayer(instanceDirectory);
19
+ ```
20
+
21
+ Building the layer installs the usine extension and resumes tasks. Closing it writes no task outcome, so the next start resumes the same work.
22
+
23
+ ## Run an attempt
24
+
25
+ `execute(request, io)` creates the attempt's task, or finds it by attempt id after a restart, and waits for the outcome. Attempt and task ids are committed together, so a later call always finds the task. `isLive(attemptId)` is true from the first `execute` until the terminal envelope is sent or the task is canceled, so a restarted orchestrator reattaches the attempt.
26
+
27
+ After the terminal envelope, `finished(attemptId)` returns `ParentReported` with the report, `ParentFailed` with the failure, or `ResultRecorded` once the executor calls `recordResult(attemptId, result)` with the harvested result. If the orchestrator stopped before the engine recorded the outcome, it finishes the attempt with this instead of running the agent again. A canceled task leaves nothing.
28
+
29
+ `recordUnsupervised(attemptId, unsupervised)` stores the unsupervised time that the runaway guard counted, and `unsupervised(attemptId)` reads it back for a reattached attempt's guard (#178). It never decreases and is none until a guard records it.
30
+
31
+ `io` receives `Started` with the resolved parent model, `SessionEvent`s for the parent's committed entries and the children's events in order, `InterventionApplied` or `InterventionRejected`, and one `Final`, `Failed`, or `Interrupted`. The next sequence is stored, so a reattached `execute` continues the numbering. Progress is committed once per parent entry, after its envelopes are appended, and once per other envelope. After a crash before a commit, the same envelopes are relayed with the same sequences, and the transcript keeps one copy.
32
+
33
+ `AttemptRequest.host` (`@usine/agents/child-host`) is where the worker runs children: `LocalProcess` here or `RemoteShell` over SSH. The `subagent` tool passes it and the worker's name to every child, and tags child logs with the attempt id. `AttemptRequest.environment` is the whole environment of every local child and its commands; on `RemoteShell`, it's exported over the machine's own.
34
+
35
+ The task doesn't store the host or environment. It stores the run spec, worker, and worktree as a `StoredRequest`, which `request(attemptId)` returns, so a reattachment knows its run spec, base commit, and worktree. After a restart, the parent waits for the next `execute` to supply the host and environment before launching another child.
36
+
37
+ `AttemptRequest.gatewayCredential`, supplied the same way, is the credential kind that the worker's gateway uses for the children, from the worker's latest preflight, or none if they use their harness's login. The `subagent` tool records it on each child's `ChildSession` at start, so usage counts the child in the right pool. A preflight during the attempt applies to its next child.
38
+
39
+ Children aren't resumed after a restart: the tool call ends as interrupted with the output committed so far, and the parent model decides next.
40
+
41
+ ## Operator input
42
+
43
+ `intervene(attemptId, intervention)` needs a live `execute`. It records the inbound envelope, applies the action, and records the result, in order with the relay's envelopes, so a sink's `append` must not wait for it. Input sent before `execute` has read durable storage and started relaying fails. `attached(attemptId)` completes once an `execute` in this process relays the channel; the runaway guard waits on it to deliver a reattached attempt's due warning (#178).
44
+
45
+ - `OperatorMessage` places a hold on the parent or a child; `Release` ends it. A held parent's answers wait for the operator instead of ending the attempt, and a released waiting parent continues with a new turn. A held child's report goes to the parent only after `Release`. `Steer` places no hold.
46
+ - Input to a working parent joins after the current tool round; to a working child, at its next step. Until then, `InterventionApplied` says `queued` and `AttemptRecordDoc.queuedInputs` keeps it. A second receipt for the same input sequence reports the read, with a child's state, or `InterventionRejected` if it never happens, including for a child lost to a restart.
47
+ - Input to a paused or waiting parent, or an idle or paused child, starts its next turn.
48
+ - `Interrupt` aborts the parent's turn and children, withdraws queued input, and holds the parent until a `Steer`, `OperatorMessage`, or `Release` resumes it.
49
+
50
+ `AttemptReport.answeredOperator` is true if the parent's last turn replied to an operator message or steer, not to the release guidance, without delegating after it. `harvestedResult(report, facts)` builds the `StepResult`, and fails such a report with `AgentFailure` if the checkout has no facts.
51
+
52
+ `cancel(attemptId)` aborts the task bottom-up, waits until it's terminal, and ends `execute` with `Interrupted`. A child whose launch hasn't returned, such as a Claude child waiting to relaunch, starts no process.
53
+
54
+ ## Recovery from a poisoned session or a stuck step
55
+
56
+ Each time the harness opens, before tasks resume, the runtime reads every live task's `pi.live`, `pi.inbox`, `pi.usage`, `pi.agent`, and `pi.provider` documents. An unreadable document, for example one whose delta chain doesn't apply, stops every step that needs it. In one commit, the runtime retires it unread, recreates it with its initial value, and records the reason on the attempt whose `usine.attempt` task has the conversation. It then aborts that task: the abort applies to the generation and tool tasks first, and the abort handler fails the attempt with an `InfraFailure` giving the reason, which `execute` and a reattachment report. Other attempts keep running.
57
+
58
+ A pi-durable commit that fails after storage admission poisons the session. `DurableHarness` logs the cause, closes the session, reopens the storage, and resumes. A `DurableHarness.use` call failed by the reopen runs once more.
59
+
60
+ pi-durable reports failures that don't fail their caller through `onReport`. The harness logs a failure's first report, then its repeat count at most every five seconds. 1,000 reports of one failure with no commit adopted means a step that can't succeed. pi-durable retries it in microtasks, with no timer or I/O until the harness closes, so the harness closes from inside the report and reopens the storage. That clears stale scheduler state and repeats the document check.
61
+
62
+ If the reopened harness is stuck again within a minute, it stops for good: it logs the cause as an error, later calls fail with a `DurableHarnessError` naming it, and running attempts fail with `InfraFailure`. The next orchestrator start reopens the storage.
63
+
64
+ pi-durable has no cross-process locking: tasks and documents from a second writer can't be read back. `usine up` locks the instance folder to prevent this.
65
+
66
+ ## Testing
67
+
68
+ ```ts
69
+ import { answer, delegate, layer, StubChildren } from "@usine/durable/testing";
70
+
71
+ const attempts = layer({
72
+ database: "/tmp/attempt.sqlite",
73
+ parent: [delegate("Fix the flaky test"), answer("Fixed it.")],
74
+ });
75
+ ```
76
+
77
+ - `layer({ database, parent })` provides `AttemptRuntime`, `DurableHarness`, `ChildBackend`, and `StubChildren` on a SQLite file. To simulate a restart, build a second layer on the file with the remaining answers.
78
+ - `parent` lists the parent model's answers, one per request: `delegate(task)` calls `subagent`, `answer(text)` is a final answer, and a function gets the request's context and returns an answer.
79
+ - `PARENT_MODEL`, `faux/faux-1`, is the scripted model. It accepts efforts up to `high`; an empty `RunSpec.parentModel` selects it.
80
+ - `StubChildren.next` waits for the next child. A `StubChild` exposes `request`, `emit(event)`, `reply(message)`, `block(message)`, `interrupted`, and its `interventions`. A held child replies after `Release`.
81
+ - `fauxParent(steps)` and `stubChildBackendLayer` are the two halves, to compose `DurableHarness` with other storage.
82
+ - `stubAttemptRuntime(storage, parent)` is an in-memory `AttemptRuntime` for executor and engine tests without a parent model. `parent` scripts each attempt's final message or `AttemptReport` (`completes`, `worksFor(millis)`, or a `StubParent`). Rebuilding it on the same `StubStorage` simulates a restart. `StubStorage.unsupervised` contains what `recordUnsupervised` stored, and `attached` completes when `execute` starts recording.
@@ -0,0 +1,86 @@
1
+ import { type ConversationId, type SubmissionId, type TaskId } from "@earendil-works/pi-durable";
2
+ import type * as AttemptChannel from "@usine/core/attempt/channel";
3
+ /**
4
+ * The return value of a completed `usine.attempt` task. `answeredOperator` is absent on older
5
+ * results.
6
+ */
7
+ export type AttemptResult = {
8
+ message: string;
9
+ answeredOperator?: boolean;
10
+ };
11
+ /**
12
+ * An accepted, unread operator input, queued either by the busy parent (with its submission) or by
13
+ * a working child (with its session ID).
14
+ */
15
+ type QueuedInput = {
16
+ inputSequence: number;
17
+ submission: SubmissionId;
18
+ intervention: typeof AttemptChannel.Intervention.Encoded;
19
+ } | {
20
+ inputSequence: number;
21
+ child: string;
22
+ intervention: typeof AttemptChannel.Intervention.Encoded;
23
+ };
24
+ /** The durable record of one Attempt, keyed by Attempt ID and kept after the task ends. */
25
+ export type AttemptRecord = {
26
+ /** Written in the same commit as the task. */
27
+ taskId: TaskId<AttemptResult> | null;
28
+ /** The parent model resolved at start. */
29
+ parentModel: string | null;
30
+ parentConversation: ConversationId | null;
31
+ /** The number of child spawn requests, including those rejected by the child-session budget. */
32
+ children: number;
33
+ /**
34
+ * The first blocker, question, or harness failure reported by a child, which fails the Attempt
35
+ * unless the parent relays one. A missing `kind` means `blocked`.
36
+ */
37
+ childBlocker: {
38
+ sessionId: string;
39
+ message: string;
40
+ kind?: "blocked" | "question" | "failed";
41
+ } | null;
42
+ childBudgetExceeded: {
43
+ attempted: number;
44
+ max: number;
45
+ } | null;
46
+ /** The next outbound and inbound sequence numbers. */
47
+ nextOutbound: number;
48
+ nextInbound: number;
49
+ /**
50
+ * The newest parent entry whose session events were all relayed, and the parent's last relayed
51
+ * token count.
52
+ */
53
+ relayedEntry: number;
54
+ relayedTokens: number;
55
+ terminalRelayed: boolean;
56
+ /** The harvested `StepResult` as JSON, absent until the executor records it. */
57
+ result?: string;
58
+ /**
59
+ * The reason the parent conversation couldn't be read from storage. Written before the task is
60
+ * aborted, so the Attempt fails with it.
61
+ */
62
+ storageFailure?: string;
63
+ /** Operator inputs with a queued receipt and no delivery receipt, oldest first. */
64
+ queuedInputs?: QueuedInput[];
65
+ /**
66
+ * The unsupervised time in milliseconds, as last recorded by a runaway guard with a wall-clock
67
+ * ceiling.
68
+ */
69
+ unsupervisedMillis?: number;
70
+ };
71
+ export declare const ATTEMPT_RECORD_KIND = "usine.attempt";
72
+ export declare const AttemptRecordDoc: import("@earendil-works/pi-durable").SessionDocFamilyToken<AttemptRecord, null>;
73
+ /**
74
+ * The operator supervision state of one running Attempt (ADR 0018), scoped to its task. `held`
75
+ * lists the session IDs under a Hold, including the parent's.
76
+ */
77
+ export type Hold = {
78
+ paused: boolean;
79
+ guidance: string[];
80
+ held: string[];
81
+ };
82
+ /** The message sent to a released parent if the operator provided none. */
83
+ export declare const RELEASE_GUIDANCE = "The operator released you without further guidance. Continue the task; if it is already done, give your final answer.";
84
+ export declare const HoldDoc: import("@earendil-works/pi-durable").TaskDocToken<Hold>;
85
+ export {};
86
+ //# sourceMappingURL=documents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/attempt/documents.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAGnB,KAAK,YAAY,EACjB,KAAK,MAAM,EACZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,KAAK,cAAc,MAAM,6BAA6B,CAAC;AAEnE;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5E;;;GAGG;AACH,KAAK,WAAW,GACZ;IACE,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,YAAY,CAAC;IACzB,YAAY,EAAE,OAAO,cAAc,CAAC,YAAY,CAAC,OAAO,CAAC;CAC1D,GACD;IACE,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,OAAO,cAAc,CAAC,YAAY,CAAC,OAAO,CAAC;CAC1D,CAAC;AAEN,2FAA2F;AAC3F,MAAM,MAAM,aAAa,GAAG;IAC1B,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;IACrC,0CAA0C;IAC1C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kBAAkB,EAAE,cAAc,GAAG,IAAI,CAAC;IAC1C,gGAAgG;IAChG,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,YAAY,EAAE;QACZ,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;KAC1C,GAAG,IAAI,CAAC;IACT,mBAAmB,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/D,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AAEnD,eAAO,MAAM,gBAAgB,iFAkB3B,CAAC;AAEH;;;GAGG;AACH,MAAM,MAAM,IAAI,GAAG;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,0HAC4F,CAAC;AAE1H,eAAO,MAAM,OAAO,yDAKlB,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { defineDoc, defineDocFamily, } from "@earendil-works/pi-durable";
2
+ export const ATTEMPT_RECORD_KIND = "usine.attempt";
3
+ export const AttemptRecordDoc = defineDocFamily({
4
+ kind: ATTEMPT_RECORD_KIND,
5
+ version: 1,
6
+ scope: "session",
7
+ family: true,
8
+ initial: () => ({
9
+ taskId: null,
10
+ parentModel: null,
11
+ parentConversation: null,
12
+ children: 0,
13
+ childBlocker: null,
14
+ childBudgetExceeded: null,
15
+ nextOutbound: 1,
16
+ nextInbound: 1,
17
+ relayedEntry: 0,
18
+ relayedTokens: 0,
19
+ terminalRelayed: false,
20
+ }),
21
+ });
22
+ /** The message sent to a released parent if the operator provided none. */
23
+ export const RELEASE_GUIDANCE = "The operator released you without further guidance. Continue the task; if it is already done, give your final answer.";
24
+ export const HoldDoc = defineDoc({
25
+ kind: "usine.hold",
26
+ version: 1,
27
+ scope: "task",
28
+ initial: () => ({ paused: false, guidance: [], held: [] }),
29
+ });
30
+ //# sourceMappingURL=documents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documents.js","sourceRoot":"","sources":["../../src/attempt/documents.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EACT,eAAe,GAGhB,MAAM,4BAA4B,CAAC;AAsEpC,MAAM,CAAC,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAEnD,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAsB;IACnE,IAAI,EAAE,mBAAmB;IACzB,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,IAAI;IACZ,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;QACd,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,IAAI;QACjB,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,CAAC;QACX,YAAY,EAAE,IAAI;QAClB,mBAAmB,EAAE,IAAI;QACzB,YAAY,EAAE,CAAC;QACf,WAAW,EAAE,CAAC;QACd,YAAY,EAAE,CAAC;QACf,aAAa,EAAE,CAAC;QAChB,eAAe,EAAE,KAAK;KACvB,CAAC;CACH,CAAC,CAAC;AAYH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,gBAAgB,GAC3B,uHAAuH,CAAC;AAE1H,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAO;IACrC,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,MAAM;IACb,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;CAC3D,CAAC,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type EntryRecord, type UsageState } from "@earendil-works/pi-durable";
2
+ import { type AgentSession, type AgentSessionEvent, TokenCount, UsdAmount } from "@usine/core/agent/session";
3
+ import { Option } from "effect";
4
+ /** Returns the trimmed text of an assistant entry, or `None` if it has no text. */
5
+ export declare const assistantText: (entry: EntryRecord) => Option.Option<string>;
6
+ /**
7
+ * Returns the session events for one committed parent entry, in Pi SDK emission order. User input,
8
+ * prompt changes, and compaction summaries produce no events.
9
+ */
10
+ export declare const entryEvents: (entry: EntryRecord, session: AgentSession) => ReadonlyArray<AgentSessionEvent>;
11
+ /** A conversation's token count and cost. `costUsd` is `None` if no model was priced. */
12
+ interface LedgerUsage {
13
+ readonly totalTokens: TokenCount;
14
+ readonly costUsd: Option.Option<UsdAmount>;
15
+ }
16
+ /**
17
+ * Sums a conversation's `pi.usage` ledger over every model. Tokens are output plus uncached input,
18
+ * where cache writes count and cache reads don't.
19
+ */
20
+ export declare const ledgerUsage: (usage: Readonly<UsageState> | undefined) => LedgerUsage;
21
+ export {};
22
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/attempt/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,UAAU,EAChB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EAEtB,UAAU,EAGV,SAAS,EACV,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AA0BxC,mFAAmF;AACnF,eAAO,MAAM,aAAa,UAAW,WAAW,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAQnE,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,WAAW,UACf,WAAW,WACT,YAAY,KACpB,aAAa,CAAC,iBAAiB,CA2CjC,CAAC;AAiBF,yFAAyF;AACzF,UAAU,WAAW;IACnB,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;CAC5C;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,UAAW,QAAQ,CAAC,UAAU,CAAC,GAAG,SAAS,KAAG,WAerE,CAAC"}
@@ -0,0 +1,110 @@
1
+ import { AssistantEntry, ToolResultEntry, } from "@earendil-works/pi-durable";
2
+ import { TextOutput, TokenCount, ToolFinished, ToolStarted, UsdAmount, } from "@usine/core/agent/session";
3
+ import { Option, Schema } from "effect";
4
+ const TextBlock = Schema.Struct({ type: Schema.Literal("text"), text: Schema.String });
5
+ const ToolCallBlock = Schema.Struct({
6
+ type: Schema.Literal("toolCall"),
7
+ id: Schema.NonEmptyString,
8
+ name: Schema.NonEmptyString,
9
+ arguments: Schema.Json,
10
+ });
11
+ const AssistantMessage = Schema.Struct({
12
+ role: Schema.Literal("assistant"),
13
+ content: Schema.Array(Schema.Json),
14
+ });
15
+ const ToolResultMessage = Schema.Struct({
16
+ role: Schema.Literal("toolResult"),
17
+ toolCallId: Schema.NonEmptyString,
18
+ toolName: Schema.NonEmptyString,
19
+ content: Schema.Json,
20
+ details: Schema.optionalKey(Schema.Json),
21
+ isError: Schema.Boolean,
22
+ });
23
+ const decodeAssistant = Schema.decodeUnknownOption(AssistantMessage);
24
+ const decodeToolResult = Schema.decodeUnknownOption(ToolResultMessage);
25
+ const isTextBlock = Schema.is(TextBlock);
26
+ const isToolCallBlock = Schema.is(ToolCallBlock);
27
+ /** Returns the trimmed text of an assistant entry, or `None` if it has no text. */
28
+ export const assistantText = (entry) => Option.flatMap(decodeAssistant(entry.model?.[0]), (message) => {
29
+ const text = message.content
30
+ .filter(isTextBlock)
31
+ .map(({ text }) => text)
32
+ .join("")
33
+ .trim();
34
+ return text.length > 0 ? Option.some(text) : Option.none();
35
+ });
36
+ /**
37
+ * Returns the session events for one committed parent entry, in Pi SDK emission order. User input,
38
+ * prompt changes, and compaction summaries produce no events.
39
+ */
40
+ export const entryEvents = (entry, session) => {
41
+ if (entry.kind === AssistantEntry.kind) {
42
+ return Option.match(decodeAssistant(entry.model?.[0]), {
43
+ onNone: () => [],
44
+ onSome: (message) => {
45
+ const events = [];
46
+ const text = assistantText(entry);
47
+ if (Option.isSome(text))
48
+ events.push(new TextOutput({ session, text: text.value }));
49
+ for (const block of message.content.filter(isToolCallBlock)) {
50
+ events.push(new ToolStarted({
51
+ session,
52
+ callId: block.id,
53
+ tool: block.name,
54
+ input: block.arguments,
55
+ }));
56
+ }
57
+ return events;
58
+ },
59
+ });
60
+ }
61
+ if (entry.kind === ToolResultEntry.kind) {
62
+ return Option.match(decodeToolResult(entry.model?.[0]), {
63
+ onNone: () => [],
64
+ onSome: (result) => {
65
+ const output = result.details === undefined
66
+ ? { content: result.content }
67
+ : { content: result.content, details: result.details };
68
+ return [
69
+ new ToolFinished({
70
+ session,
71
+ callId: result.toolCallId,
72
+ tool: result.toolName,
73
+ output,
74
+ isError: result.isError,
75
+ }),
76
+ ];
77
+ },
78
+ });
79
+ }
80
+ return [];
81
+ };
82
+ const Count = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
83
+ const Dollars = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0));
84
+ const UsageLedger = Schema.Struct({
85
+ models: Schema.Record(Schema.String, Schema.Struct({
86
+ input: Count,
87
+ output: Count,
88
+ cacheWrite: Count,
89
+ cost: Schema.optionalKey(Schema.Struct({ total: Dollars })),
90
+ })),
91
+ });
92
+ const decodeUsage = Schema.decodeUnknownOption(UsageLedger);
93
+ /**
94
+ * Sums a conversation's `pi.usage` ledger over every model. Tokens are output plus uncached input,
95
+ * where cache writes count and cache reads don't.
96
+ */
97
+ export const ledgerUsage = (usage) => {
98
+ const models = Option.match(decodeUsage(usage), {
99
+ onNone: () => [],
100
+ onSome: ({ models }) => Object.values(models),
101
+ });
102
+ const priced = models.flatMap(({ cost }) => (cost === undefined ? [] : [cost.total]));
103
+ return {
104
+ totalTokens: TokenCount.make(models.reduce((total, model) => total + model.output + model.input + model.cacheWrite, 0)),
105
+ costUsd: priced.length === 0
106
+ ? Option.none()
107
+ : Option.some(UsdAmount.make(priced.reduce((total, value) => total + value, 0))),
108
+ };
109
+ };
110
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/attempt/events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { type Extension } from "@earendil-works/pi-durable";
2
+ import type { ExtensionServices } from "./services.ts";
3
+ /**
4
+ * Returns the usine extension: the `usine.attempt` task, the `subagent` tool, and the parent's
5
+ * guidance section.
6
+ */
7
+ export declare const usineExtension: (services: ExtensionServices) => {
8
+ extension: Extension<import("@earendil-works/pi-durable").ToolRegistration<import("@earendil-works/pi-ai").TSchema, import("@earendil-works/chord").JsonValue>>;
9
+ task: import("@earendil-works/pi-durable").Task<{
10
+ readonly spec: {
11
+ readonly attemptId: string;
12
+ readonly repository: string;
13
+ readonly revision: string;
14
+ readonly baseCommit: string;
15
+ readonly payload: import("effect/Schema").Json;
16
+ readonly task: string;
17
+ readonly agent: {
18
+ readonly name: string;
19
+ readonly brain: {
20
+ readonly harness: "claude" | "codex" | "pi";
21
+ readonly model: string;
22
+ readonly effort: "high" | "low" | "max" | "medium" | "minimal" | "off" | "xhigh";
23
+ };
24
+ readonly parentModel: string | null;
25
+ readonly parentEffort?: "high" | "low" | "max" | "medium" | "minimal" | "off" | "xhigh";
26
+ readonly persona: string;
27
+ };
28
+ readonly parentModel: string | null;
29
+ readonly maxChildren: number;
30
+ readonly parentEffort: "high" | "low" | "max" | "medium" | "minimal" | "off" | "xhigh";
31
+ readonly resume: {
32
+ readonly _tag: "Pushed";
33
+ readonly ref: string;
34
+ readonly commit: string;
35
+ } | null;
36
+ readonly conflictingSalvage?: {
37
+ readonly _tag: "Pushed";
38
+ readonly ref: string;
39
+ readonly commit: string;
40
+ } | null;
41
+ readonly preparedAt?: string | null;
42
+ readonly botLogin?: string | null;
43
+ };
44
+ readonly worker: string;
45
+ readonly cwd: string;
46
+ }, import("./task.ts").AttemptCheckpoint, import("./documents.ts").AttemptResult, object>;
47
+ };
48
+ //# sourceMappingURL=extension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../../src/attempt/extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,SAAS,EAAW,MAAM,4BAA4B,CAAC;AAEtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAevD;;;GAGG;AACH,eAAO,MAAM,cAAc,aAAc,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAczD,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { defineExtension, section } from "@earendil-works/pi-durable";
2
+ import { BLOCKED_MARKER, FAILED_MARKER, QUESTION_MARKER } from "@usine/agents/runtime";
3
+ import { subagentTool } from "./subagent.js";
4
+ import { attemptTask } from "./task.js";
5
+ /** The extension name, stored in every parent conversation that selects it. */
6
+ const USINE_EXTENSION = "usine";
7
+ const parentGuidance = [
8
+ "You coordinate one usine Attempt. Your only tool is `subagent`: it delegates one focused task to a child agent working in the Attempt's checkout and returns the child's report.",
9
+ "- Delegate only independent, focused work; the child cannot create more children.",
10
+ `- When a child's report starts with ${BLOCKED_MARKER}, ${QUESTION_MARKER}, or ${FAILED_MARKER}, return that report verbatim as your own final answer instead of summarising it.`,
11
+ "- A `subagent` call that ends as interrupted is not a blocker: the orchestrator restarted and the checkout keeps everything the child did. Delegate again with a task that tells the child to inspect the checkout and continue from its current state.",
12
+ "- The operator can write to you while you work; a message sent during a delegation reaches you when the delegation returns. Treat it as part of this task. Answer a question and end your turn: the operator then replies or releases you to continue. Follow guidance and keep working. A reply to the operator is never the task's final answer; give the final answer only when the task is done or blocked.",
13
+ ].join("\n");
14
+ /**
15
+ * Returns the usine extension: the `usine.attempt` task, the `subagent` tool, and the parent's
16
+ * guidance section.
17
+ */
18
+ export const usineExtension = (services) => {
19
+ const tool = subagentTool(services);
20
+ let extension;
21
+ const task = attemptTask(services, () => {
22
+ if (extension === undefined)
23
+ throw new Error("The usine extension is not built yet");
24
+ return { extension, tool };
25
+ });
26
+ extension = defineExtension({
27
+ name: USINE_EXTENSION,
28
+ tools: [tool],
29
+ sections: [section(USINE_EXTENSION, () => parentGuidance, { tag: false })],
30
+ tasks: [task],
31
+ });
32
+ return { extension, task };
33
+ };
34
+ //# sourceMappingURL=extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.js","sourceRoot":"","sources":["../../src/attempt/extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACtF,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEvF,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,+EAA+E;AAC/E,MAAM,eAAe,GAAG,OAAO,CAAC;AAEhC,MAAM,cAAc,GAAG;IACrB,kLAAkL;IAClL,mFAAmF;IACnF,uCAAuC,cAAc,KAAK,eAAe,QAAQ,aAAa,mFAAmF;IACjL,yPAAyP;IACzP,iZAAiZ;CAClZ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAA2B,EAAE,EAAE;IAC5D,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACpC,IAAI,SAAgC,CAAC;IACrC,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,EAAE,GAAG,EAAE;QACtC,IAAI,SAAS,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACrF,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;IACH,SAAS,GAAG,eAAe,CAAC;QAC1B,IAAI,EAAE,eAAe;QACrB,KAAK,EAAE,CAAC,IAAI,CAAC;QACb,QAAQ,EAAE,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,cAAc,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1E,KAAK,EAAE,CAAC,IAAI,CAAC;KACd,CAAC,CAAC;IACH,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC,CAAC"}
@@ -0,0 +1,117 @@
1
+ import type { ConversationId, EntryId, TaskId } from "@earendil-works/pi-durable";
2
+ import type { RunningSession } from "@usine/agents/runtime";
3
+ import type { AgentSessionEvent, AgentSessionId } from "@usine/core/agent/session";
4
+ import type { AttemptId } from "@usine/core/attempt";
5
+ import type * as AttemptChannel from "@usine/core/attempt/channel";
6
+ import { Deferred, Effect, Option, Queue, Semaphore } from "effect";
7
+ import type { AttemptRequest } from "../attempt-runtime.ts";
8
+ import type { HarnessListener } from "../harness.ts";
9
+ /**
10
+ * Whether a child read a queued operator input, with the child's state when it read it, or `None`
11
+ * if it stopped first.
12
+ */
13
+ export interface ChildDelivery {
14
+ readonly _tag: "Delivery";
15
+ readonly inputSequence: AttemptChannel.Sequence;
16
+ readonly intervention: AttemptChannel.Intervention;
17
+ readonly delivered: Option.Option<AttemptChannel.InterventionState>;
18
+ }
19
+ /** Activity queued for relay, in order of occurrence. */
20
+ export type RelayItem = {
21
+ readonly _tag: "Entry";
22
+ readonly entry: EntryId;
23
+ } | {
24
+ readonly _tag: "Event";
25
+ readonly event: AgentSessionEvent;
26
+ } | {
27
+ readonly _tag: "Notice";
28
+ readonly notice: AttemptChannel.WorkerNotice;
29
+ } | ChildDelivery | {
30
+ readonly _tag: "Wake";
31
+ };
32
+ /**
33
+ * The relay position of the attached channel, persisted in the Attempt record after each parent
34
+ * entry and envelope.
35
+ */
36
+ export interface RelayProgress {
37
+ outbound: AttemptChannel.Progress;
38
+ inbound: number;
39
+ entry: number;
40
+ tokens: number;
41
+ }
42
+ /** The sink of the `execute` call relaying this Attempt. */
43
+ interface AttachedChannel {
44
+ readonly io: AttemptChannel.Sink;
45
+ readonly progress: RelayProgress;
46
+ }
47
+ /** The in-process state of one Attempt. */
48
+ export interface AttemptHub {
49
+ readonly attemptId: AttemptId;
50
+ readonly taskId: TaskId;
51
+ readonly queue: Queue.Queue<RelayItem>;
52
+ /** Serializes outbound envelopes between the relay and interventions. */
53
+ readonly emission: Semaphore.Semaphore;
54
+ /** Serializes operator interventions. */
55
+ readonly routing: Semaphore.Semaphore;
56
+ /** The live children by session ID. */
57
+ readonly children: Map<AgentSessionId, RunningSession>;
58
+ /**
59
+ * The sequence numbers of operator inputs queued by children in this process and not yet reported
60
+ * read or unread. An input missing from this set was queued by a previous process.
61
+ */
62
+ readonly childInputs: Set<number>;
63
+ parent: Option.Option<ConversationId>;
64
+ channel: Option.Option<AttachedChannel>;
65
+ /** The Worker notices received before the parent started, sent right after `Started`. */
66
+ early: ReadonlyArray<AttemptChannel.WorkerNotice>;
67
+ /**
68
+ * Completes with the request of the `execute` call relaying this Attempt. Replaced when that call
69
+ * detaches.
70
+ */
71
+ attached: Deferred.Deferred<AttemptRequest>;
72
+ }
73
+ /**
74
+ * Returns the notices to buffer after `notice`: the latest one, preceded by the last
75
+ * `WorkerUnresponsive` if the latest is a `WorkerResponsive`.
76
+ */
77
+ export declare const keepNotice: (kept: ReadonlyArray<AttemptChannel.WorkerNotice>, notice: AttemptChannel.WorkerNotice) => ReadonlyArray<AttemptChannel.WorkerNotice>;
78
+ /** The result of `AttemptHubs.notice`. */
79
+ export type NoticeRouting = {
80
+ readonly _tag: "Queued";
81
+ } | {
82
+ readonly _tag: "Kept";
83
+ readonly kept: ReadonlyArray<AttemptChannel.WorkerNotice>;
84
+ };
85
+ export interface AttemptHubs {
86
+ /** Returns the Attempt's hub, creating it on first use with the buffered Worker notices. */
87
+ readonly hub: (attemptId: AttemptId, taskId: TaskId) => Effect.Effect<AttemptHub>;
88
+ readonly find: (attemptId: AttemptId) => Option.Option<AttemptHub>;
89
+ /** Routes the parent conversation's entries to the hub from now on. */
90
+ readonly bindParent: (hub: AttemptHub, parent: ConversationId) => void;
91
+ /** Queues a session event that has no parent entry. */
92
+ readonly event: (hub: AttemptHub, event: AgentSessionEvent) => Effect.Effect<void>;
93
+ /**
94
+ * Queues a Worker notice. If the Attempt has no hub in this process, buffers it with `keepNotice`
95
+ * until the hub is created.
96
+ */
97
+ readonly notice: (attemptId: AttemptId, notice: AttemptChannel.WorkerNotice) => Effect.Effect<NoticeRouting>;
98
+ readonly delivery: (hub: AttemptHub, delivery: ChildDelivery) => Effect.Effect<void>;
99
+ /**
100
+ * Completes when an `execute` call is relaying the Attempt, even if the hub doesn't exist yet.
101
+ */
102
+ readonly attached: (attemptId: AttemptId) => Effect.Effect<void>;
103
+ /** Routes commits to hubs and wakes every hub after a reopen or stop. */
104
+ readonly listener: HarnessListener;
105
+ }
106
+ export declare const attemptHubs: Effect.Effect<{
107
+ hub: (attemptId: AttemptId, taskId: TaskId) => Effect.Effect<AttemptHub, never, never>;
108
+ find: (attemptId: string & import("effect/Brand").Brand<"AttemptId">) => Option.Option<AttemptHub>;
109
+ bindParent: (hub: AttemptHub, parent: ConversationId) => void;
110
+ event: (target: AttemptHub, event: import("@usine/core/agent/session").PoolRateLimitsUpdated | import("@usine/core/agent/session").ProviderRecovered | import("@usine/core/agent/session").ProviderRetrying | import("@usine/core/agent/session").SessionFinished | import("@usine/core/agent/session").SessionIdle | import("@usine/core/agent/session").SessionResumed | import("@usine/core/agent/session").SessionStarted | import("@usine/core/agent/session").SessionTokensUpdated | import("@usine/core/agent/session").TextOutput | import("@usine/core/agent/session").ToolFinished | import("@usine/core/agent/session").ToolStarted) => Effect.Effect<void, never, never>;
111
+ notice: (attemptId: string & import("effect/Brand").Brand<"AttemptId">, notice: AttemptChannel.WorkerResponsive | AttemptChannel.WorkerUnresponsive) => Effect.Effect<NoticeRouting, never, never>;
112
+ delivery: (target: AttemptHub, delivery: ChildDelivery) => Effect.Effect<void, never, never>;
113
+ attached: (attemptId: string & import("effect/Brand").Brand<"AttemptId">) => Effect.Effect<void, never, never>;
114
+ listener: HarnessListener;
115
+ }, never, never>;
116
+ export {};
117
+ //# sourceMappingURL=hubs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hubs.d.ts","sourceRoot":"","sources":["../../src/attempt/hubs.ts"],"names":[],"mappings":"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"}