@defrex/autobuild 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +192 -0
- package/SPEC.md +2269 -0
- package/bin/ab-build-runner.ts +47 -0
- package/bin/ab-dev.ts +27 -0
- package/bin/ab-dispatch-kernel.ts +92 -0
- package/bin/ab-harvest-runner.ts +29 -0
- package/bin/ab-init-probe.ts +19 -0
- package/bin/ab-process-group-reaper.ts +32 -0
- package/bin/ab.ts +6 -0
- package/bin/agent/ab +3 -0
- package/docs/answering-blocked-builds.md +89 -0
- package/docs/architecture.md +726 -0
- package/docs/assets/headline-wide.png +0 -0
- package/docs/configuration.md +1623 -0
- package/docs/hosted-dispatcher-cutover-validation.md +178 -0
- package/docs/hosted-dispatcher.md +204 -0
- package/docs/mcp.md +119 -0
- package/docs/operator-api.md +186 -0
- package/docs/remote-store-protocol.md +972 -0
- package/docs/remote-ticket-protocol.md +79 -0
- package/docs/setup.md +289 -0
- package/docs/spec-standard.md +63 -0
- package/docs/ticket-dependencies.md +59 -0
- package/docs/vercel-sandbox-rollout-probe.md +7 -0
- package/package.json +135 -0
- package/packages/core/src/cli/args.ts +74 -0
- package/packages/core/src/cli/artifact.ts +182 -0
- package/packages/core/src/cli/binary.ts +198 -0
- package/packages/core/src/cli/build-control.ts +789 -0
- package/packages/core/src/cli/build-progress.ts +55 -0
- package/packages/core/src/cli/bulk-control.ts +385 -0
- package/packages/core/src/cli/context.ts +470 -0
- package/packages/core/src/cli/dashboard/actions.ts +59 -0
- package/packages/core/src/cli/dashboard/cells.ts +92 -0
- package/packages/core/src/cli/dashboard/composer.ts +230 -0
- package/packages/core/src/cli/dashboard/detail.ts +100 -0
- package/packages/core/src/cli/dashboard/frame-image.ts +373 -0
- package/packages/core/src/cli/dashboard/live.ts +152 -0
- package/packages/core/src/cli/dashboard/model.ts +1173 -0
- package/packages/core/src/cli/dashboard/poll.ts +196 -0
- package/packages/core/src/cli/dashboard/render.ts +1568 -0
- package/packages/core/src/cli/dashboard/selection.ts +66 -0
- package/packages/core/src/cli/dashboard/session-feed.ts +244 -0
- package/packages/core/src/cli/dashboard/session-view.ts +506 -0
- package/packages/core/src/cli/dashboard/transcript.ts +108 -0
- package/packages/core/src/cli/dev.ts +46 -0
- package/packages/core/src/cli/dispatch-frontend.ts +1045 -0
- package/packages/core/src/cli/dispatch-process.ts +259 -0
- package/packages/core/src/cli/dispatch.ts +3692 -0
- package/packages/core/src/cli/dotenv.ts +48 -0
- package/packages/core/src/cli/env.ts +206 -0
- package/packages/core/src/cli/harvest.ts +698 -0
- package/packages/core/src/cli/help.ts +713 -0
- package/packages/core/src/cli/init-agent.ts +106 -0
- package/packages/core/src/cli/init-validation.ts +583 -0
- package/packages/core/src/cli/init.ts +756 -0
- package/packages/core/src/cli/installation.ts +333 -0
- package/packages/core/src/cli/keyboard.ts +261 -0
- package/packages/core/src/cli/main.ts +1399 -0
- package/packages/core/src/cli/mcp.ts +279 -0
- package/packages/core/src/cli/models.ts +65 -0
- package/packages/core/src/cli/observe.ts +55 -0
- package/packages/core/src/cli/plugin.ts +227 -0
- package/packages/core/src/cli/pr-attachments.ts +138 -0
- package/packages/core/src/cli/pr-summary.ts +124 -0
- package/packages/core/src/cli/publication-settlement.ts +188 -0
- package/packages/core/src/cli/repo-state.ts +213 -0
- package/packages/core/src/cli/repository-status.ts +80 -0
- package/packages/core/src/cli/self-update.ts +443 -0
- package/packages/core/src/cli/status.ts +895 -0
- package/packages/core/src/cli/store-opening.contract.ts +289 -0
- package/packages/core/src/cli/store-opening.ts +167 -0
- package/packages/core/src/cli/store-ref.ts +33 -0
- package/packages/core/src/cli/terminal-restore.ts +174 -0
- package/packages/core/src/cli/terminal.ts +361 -0
- package/packages/core/src/cli/terminals.ts +995 -0
- package/packages/core/src/cli/testkit.ts +194 -0
- package/packages/core/src/cli/ticket.ts +484 -0
- package/packages/core/src/cli/upgrade-agent.ts +180 -0
- package/packages/core/src/cli/upgrade-commit.ts +468 -0
- package/packages/core/src/cli/upgrade-notice.ts +91 -0
- package/packages/core/src/cli/upgrade-progress.ts +110 -0
- package/packages/core/src/cli/upgrade.ts +888 -0
- package/packages/core/src/cli/wait.ts +587 -0
- package/packages/core/src/cli/watch.ts +932 -0
- package/packages/core/src/config/live.ts +255 -0
- package/packages/core/src/config/load.ts +98 -0
- package/packages/core/src/config/roles.ts +438 -0
- package/packages/core/src/config/schema.ts +774 -0
- package/packages/core/src/distribution.ts +24 -0
- package/packages/core/src/events/catalog.ts +201 -0
- package/packages/core/src/events/envelope.ts +59 -0
- package/packages/core/src/events/payloads.ts +618 -0
- package/packages/core/src/events/repository.ts +559 -0
- package/packages/core/src/events/sessions.ts +180 -0
- package/packages/core/src/harvest/schema.ts +147 -0
- package/packages/core/src/ids.ts +45 -0
- package/packages/core/src/integration/harness.ts +681 -0
- package/packages/core/src/kernel/auto-merge.ts +259 -0
- package/packages/core/src/kernel/converge.ts +117 -0
- package/packages/core/src/kernel/dispatch-settings.ts +60 -0
- package/packages/core/src/kernel/dispatch-status.ts +141 -0
- package/packages/core/src/kernel/engine.ts +1047 -0
- package/packages/core/src/kernel/harvest.ts +823 -0
- package/packages/core/src/kernel/origin.ts +69 -0
- package/packages/core/src/kernel/phases.ts +196 -0
- package/packages/core/src/kernel/plan-verify-selection.ts +105 -0
- package/packages/core/src/kernel/pr-attachments.ts +82 -0
- package/packages/core/src/kernel/reducer.ts +752 -0
- package/packages/core/src/kernel/stall.ts +155 -0
- package/packages/core/src/kernel/verify-applicability.ts +52 -0
- package/packages/core/src/markdown.ts +67 -0
- package/packages/core/src/ontology.ts +288 -0
- package/packages/core/src/open-map.ts +203 -0
- package/packages/core/src/operator/annotations.ts +187 -0
- package/packages/core/src/operator/client.ts +311 -0
- package/packages/core/src/operator/control.ts +117 -0
- package/packages/core/src/operator/index.ts +62 -0
- package/packages/core/src/operator/presentation.ts +22 -0
- package/packages/core/src/operator/protocol.ts +98 -0
- package/packages/core/src/operator/query.ts +201 -0
- package/packages/core/src/operator/registry.ts +1043 -0
- package/packages/core/src/operator/requests.ts +138 -0
- package/packages/core/src/operator/sandbox.ts +458 -0
- package/packages/core/src/operator/server.ts +616 -0
- package/packages/core/src/operator/sessions.ts +223 -0
- package/packages/core/src/operator/tickets.ts +177 -0
- package/packages/core/src/plugin-sdk/index.ts +174 -0
- package/packages/core/src/plugins/contract-entry.ts +74 -0
- package/packages/core/src/plugins/contract-env.ts +3 -0
- package/packages/core/src/plugins/load.ts +259 -0
- package/packages/core/src/plugins/manifest.ts +236 -0
- package/packages/core/src/plugins/registry.ts +274 -0
- package/packages/core/src/plugins/runtimes.ts +75 -0
- package/packages/core/src/ports/forge/contract.ts +258 -0
- package/packages/core/src/ports/forge/create.ts +74 -0
- package/packages/core/src/ports/forge/fake.ts +488 -0
- package/packages/core/src/ports/forge/github-pr-attachments.ts +351 -0
- package/packages/core/src/ports/forge/github-transport.ts +352 -0
- package/packages/core/src/ports/forge/github.ts +1162 -0
- package/packages/core/src/ports/forge/local-git.ts +568 -0
- package/packages/core/src/ports/runner/claude.ts +873 -0
- package/packages/core/src/ports/runner/cli-stream.ts +90 -0
- package/packages/core/src/ports/runner/codex.ts +873 -0
- package/packages/core/src/ports/runner/contract.ts +492 -0
- package/packages/core/src/ports/runner/fake.ts +213 -0
- package/packages/core/src/ports/runner/json-record.ts +2 -0
- package/packages/core/src/ports/runner/live-contract-fixture.ts +176 -0
- package/packages/core/src/ports/runner/one-shot.ts +29 -0
- package/packages/core/src/ports/runner/pi-bridge-core.ts +136 -0
- package/packages/core/src/ports/runner/pi-bridge.js +6 -0
- package/packages/core/src/ports/runner/pi-rpc.ts +454 -0
- package/packages/core/src/ports/runner/pi.ts +617 -0
- package/packages/core/src/ports/runner/production.ts +74 -0
- package/packages/core/src/ports/runner/provider-error.ts +121 -0
- package/packages/core/src/ports/runner/routing.ts +234 -0
- package/packages/core/src/ports/runner/runtime.ts +214 -0
- package/packages/core/src/ports/runner/session-env.ts +34 -0
- package/packages/core/src/ports/runner/stream-parts.ts +103 -0
- package/packages/core/src/ports/tickets/contract.ts +570 -0
- package/packages/core/src/ports/tickets/create.ts +135 -0
- package/packages/core/src/ports/tickets/fake.ts +215 -0
- package/packages/core/src/ports/tickets/file.ts +559 -0
- package/packages/core/src/ports/tickets/linear.ts +811 -0
- package/packages/core/src/ports/tickets/operations.ts +161 -0
- package/packages/core/src/ports/tickets/remote-index.ts +13 -0
- package/packages/core/src/ports/tickets/remote-protocol.ts +80 -0
- package/packages/core/src/ports/tickets/remote-server.ts +185 -0
- package/packages/core/src/ports/tickets/remote.ts +130 -0
- package/packages/core/src/ports/tickets/update.ts +51 -0
- package/packages/core/src/ports/types.ts +528 -0
- package/packages/core/src/ports/workspace/build-execution.ts +123 -0
- package/packages/core/src/ports/workspace/contract.ts +136 -0
- package/packages/core/src/ports/workspace/create.ts +132 -0
- package/packages/core/src/ports/workspace/distribution-archive.ts +317 -0
- package/packages/core/src/ports/workspace/fake.ts +432 -0
- package/packages/core/src/ports/workspace/git-worktree.ts +848 -0
- package/packages/core/src/ports/workspace/harvest-execution.ts +111 -0
- package/packages/core/src/ports/workspace/in-process-build-execution.ts +30 -0
- package/packages/core/src/ports/workspace/local-build-execution.ts +88 -0
- package/packages/core/src/ports/workspace/operator-sandbox.ts +142 -0
- package/packages/core/src/ports/workspace/phase-scratch.ts +156 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +2541 -0
- package/packages/core/src/processes/build-child-exit.ts +78 -0
- package/packages/core/src/processes/build-child.ts +119 -0
- package/packages/core/src/processes/build-execution-state.ts +95 -0
- package/packages/core/src/processes/build-parent-watch.ts +35 -0
- package/packages/core/src/processes/build-runner.ts +2614 -0
- package/packages/core/src/processes/dispatcher.ts +2825 -0
- package/packages/core/src/processes/execution-settlement.ts +171 -0
- package/packages/core/src/processes/harvest-child.ts +115 -0
- package/packages/core/src/processes/harvest-execution-state.ts +121 -0
- package/packages/core/src/processes/harvest-runner.ts +1656 -0
- package/packages/core/src/processes/harvest.ts +595 -0
- package/packages/core/src/processes/infrastructure-failure-budget.ts +146 -0
- package/packages/core/src/processes/phase-failure-budget.ts +52 -0
- package/packages/core/src/processes/process-group-reaper.ts +35 -0
- package/packages/core/src/processes/process-group.ts +65 -0
- package/packages/core/src/processes/publication-loss.ts +62 -0
- package/packages/core/src/processes/publication-state.ts +93 -0
- package/packages/core/src/processes/sandbox-state.ts +101 -0
- package/packages/core/src/registry.ts +24 -0
- package/packages/core/src/sessions/reducer.ts +185 -0
- package/packages/core/src/skills.ts +7 -0
- package/packages/core/src/spec-standard.ts +47 -0
- package/packages/core/src/store/adapter.ts +80 -0
- package/packages/core/src/store/build-scope.ts +272 -0
- package/packages/core/src/store/contract.ts +2581 -0
- package/packages/core/src/store/local/blobs.ts +38 -0
- package/packages/core/src/store/local/schema.ts +180 -0
- package/packages/core/src/store/local/store.ts +1713 -0
- package/packages/core/src/store/memory.ts +1138 -0
- package/packages/core/src/store/phase-session.ts +425 -0
- package/packages/core/src/store/remote/client.ts +780 -0
- package/packages/core/src/store/remote/index.ts +12 -0
- package/packages/core/src/store/remote/protocol.ts +348 -0
- package/packages/core/src/store/remote/server.ts +923 -0
- package/packages/core/src/store/remote/token.ts +137 -0
- package/packages/core/src/store/remote/version.ts +10 -0
- package/packages/core/src/store/retention.ts +91 -0
- package/packages/core/src/store/session-handle.ts +265 -0
- package/packages/core/src/store/streams/assemble.ts +79 -0
- package/packages/core/src/store/streams/lock.ts +42 -0
- package/packages/core/src/store/streams/session-writer.ts +306 -0
- package/packages/core/src/store/streams/types.ts +171 -0
- package/packages/core/src/store/streams/wait.ts +127 -0
- package/packages/core/src/store/subscribe.ts +105 -0
- package/packages/core/src/store/types.ts +466 -0
- package/packages/core/src/testing/fixed.ts +44 -0
- package/packages/core/src/testing/store-failures.ts +32 -0
- package/packages/core/src/zod-issues.ts +91 -0
- package/skills/code-review/SKILL.md +100 -0
- package/skills/finalize/SKILL.md +44 -0
- package/skills/guide/SKILL.md +1781 -0
- package/skills/guide/references/operator-api.md +164 -0
- package/skills/guide/references/plugin-authoring.md +354 -0
- package/skills/guide/references/remote-store-protocol.md +972 -0
- package/skills/guide/references/remote-ticket-protocol.md +79 -0
- package/skills/guide/references/setup.md +289 -0
- package/skills/guide/references/spec-standard.md +63 -0
- package/skills/guide/references/ticket-dependencies.md +59 -0
- package/skills/harvest/SKILL.md +66 -0
- package/skills/harvest-review/SKILL.md +63 -0
- package/skills/implement/SKILL.md +82 -0
- package/skills/plan/SKILL.md +123 -0
- package/skills/plan-review/SKILL.md +121 -0
- package/skills/reconcile/SKILL.md +65 -0
- package/skills/spec/SKILL.md +132 -0
- package/skills/tickets/SKILL.md +139 -0
- package/templates/autobuild.toml +36 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ports (SPEC §3.2): interfaces to the world, each with swappable adapters.
|
|
3
|
+
* The kernel and processes depend only on these types; adapters live in
|
|
4
|
+
* `ports/<port>/` and fakes for seam tests in `ports/fakes/`.
|
|
5
|
+
*/
|
|
6
|
+
import type {
|
|
7
|
+
HostedPrAttachmentAsset,
|
|
8
|
+
PrAttachment,
|
|
9
|
+
PrImageHostTarget,
|
|
10
|
+
TicketRef,
|
|
11
|
+
WorkspaceBase,
|
|
12
|
+
} from '../ontology'
|
|
13
|
+
import type { StreamOutcome, StreamPart } from '../store/streams/types'
|
|
14
|
+
import type { BuildExecution } from './workspace/build-execution'
|
|
15
|
+
import type { HarvestExecution } from './workspace/harvest-execution'
|
|
16
|
+
import type { OperatorSandboxExecution } from './workspace/operator-sandbox'
|
|
17
|
+
|
|
18
|
+
// ── Session streams (SPEC §9) ─────────────────────────────────────────────
|
|
19
|
+
//
|
|
20
|
+
// The live, protocol-shaped view of a build session: a runner that holds the
|
|
21
|
+
// optional streaming capability translates its harness's native output into
|
|
22
|
+
// AI SDK UI Message Stream parts and appends them as they happen. Streams
|
|
23
|
+
// are presentation, never routing; outcomes still travel only the typed CLI.
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The per-turn write side of a session stream. `append` NEVER throws: a
|
|
27
|
+
* failing stream path must never fail a turn. Adapters call it with the
|
|
28
|
+
* part vocabulary of `ai-ui-message-stream/v1` plus the `data-ab-*`
|
|
29
|
+
* extension parts.
|
|
30
|
+
*/
|
|
31
|
+
export interface SessionStreamEmitter {
|
|
32
|
+
append(parts: StreamPart[]): void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The bracket-scoped sink a runtime registration's `openSessionStream`
|
|
37
|
+
* capability receives: `open` creates the store stream and returns its id,
|
|
38
|
+
* after which the sink is also an emitter; `close` is idempotent (first
|
|
39
|
+
* outcome wins) and best-effort.
|
|
40
|
+
*/
|
|
41
|
+
export interface SessionStreamSink extends SessionStreamEmitter {
|
|
42
|
+
open(label: string): Promise<string>
|
|
43
|
+
close(outcome: StreamOutcome): Promise<void>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── TicketSource (SPEC §3.2, §13) ────────────────────────────────────────────
|
|
47
|
+
//
|
|
48
|
+
// Policy (§13): the TicketSource initiates and receives projections; it is
|
|
49
|
+
// never consulted mid-build and never used as artifact storage. A file-based
|
|
50
|
+
// adapter with nowhere to put blobs must be fully workable.
|
|
51
|
+
|
|
52
|
+
export interface Ticket {
|
|
53
|
+
ref: TicketRef
|
|
54
|
+
/** Stable source-native key used to correlate an externally created ticket
|
|
55
|
+
* with Autobuild's durable creation reservation. It is separate from the
|
|
56
|
+
* operator-facing `ref.id` and optional for legacy/plugin projections. */
|
|
57
|
+
creationKey?: string
|
|
58
|
+
title: string
|
|
59
|
+
/** The ticket body — pre-build, the spec lives here (§6.3). */
|
|
60
|
+
body: string
|
|
61
|
+
state?: string
|
|
62
|
+
labels: string[]
|
|
63
|
+
/** Source-local ids of tickets declared as blockers. Absent ≡ none. */
|
|
64
|
+
blockedBy?: string[]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A partial source scan. Diagnostics name source records that were excluded
|
|
68
|
+
* from eligibility; infrastructure failures and source-wide invariants still
|
|
69
|
+
* reject `listReady` rather than appearing here. */
|
|
70
|
+
export interface TicketListing {
|
|
71
|
+
tickets: Ticket[]
|
|
72
|
+
diagnostics: string[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface TicketDraft {
|
|
76
|
+
title: string
|
|
77
|
+
body: string
|
|
78
|
+
/** Labels applied at creation. Names need not already exist in the source;
|
|
79
|
+
* adapters own any provider-side label-registry reconciliation. */
|
|
80
|
+
labels?: string[]
|
|
81
|
+
/** Source-local blocker ids, recorded natively at creation (§13). An
|
|
82
|
+
* adapter must record every one or throw — never discard silently. */
|
|
83
|
+
blockedBy?: string[]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** One node of the dependency graph, as this source sees it. */
|
|
87
|
+
export interface DependencyState {
|
|
88
|
+
/** The requested source-local id, echoed back. */
|
|
89
|
+
id: string
|
|
90
|
+
/** False = no such ticket in this source. */
|
|
91
|
+
exists: boolean
|
|
92
|
+
/**
|
|
93
|
+
* Complete per this source's NATIVE lifecycle semantics — the adapter owns
|
|
94
|
+
* this meaning and callers must not re-derive it from `state` (§13: the
|
|
95
|
+
* provider's taxonomy never leaks above the port). Linear: `state.type` in
|
|
96
|
+
* `completed | canceled`. File: state equals the source's done state.
|
|
97
|
+
*/
|
|
98
|
+
resolved: boolean
|
|
99
|
+
/** This node's own declared blockers, so callers can close the graph. */
|
|
100
|
+
blockedBy: string[]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface TicketCreateOptions {
|
|
104
|
+
/** Explicit workflow state for this create (harvest always targets Triage). */
|
|
105
|
+
state?: string
|
|
106
|
+
/** Source-level adoption key for crash-safe external creation. File and fake
|
|
107
|
+
* sources treat it as opaque; Linear requires a durably reserved UUID v4,
|
|
108
|
+
* sends it verbatim as the issue id, and queries that id when adopting. */
|
|
109
|
+
idempotencyKey?: string
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Editable ticket fields. Omitted fields are preserved; state is deliberately
|
|
113
|
+
* absent because `transition()` exclusively owns lifecycle changes. */
|
|
114
|
+
export interface TicketUpdate {
|
|
115
|
+
/** Required when named: an update cannot clear the ticket's title. */
|
|
116
|
+
title?: string
|
|
117
|
+
/** Required when named: pre-build, this is the spec (§6.3). */
|
|
118
|
+
body?: string
|
|
119
|
+
/** A complete label replacement. An explicit empty array clears labels.
|
|
120
|
+
* Names need not already exist in the source; adapters own any
|
|
121
|
+
* provider-side label-registry reconciliation. */
|
|
122
|
+
labels?: string[]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface TicketSource {
|
|
126
|
+
readonly name: string
|
|
127
|
+
/** Ready tickets matching the dispatch criteria (label/state — §3.3), plus
|
|
128
|
+
* actionable diagnostics for individual source records excluded as invalid.
|
|
129
|
+
* Fatal source failures and invariants still reject the call. */
|
|
130
|
+
listReady(criteria: { labels?: string[]; state?: string }): Promise<TicketListing>
|
|
131
|
+
get(id: string): Promise<Ticket | null>
|
|
132
|
+
/** Claim-before-launch (§12): acquires nonterminal work and returns true.
|
|
133
|
+
* Returns false for an unknown, already-claimed/contested, or terminal ticket.
|
|
134
|
+
* Refusing a terminal ticket leaves its state unchanged; claim never reopens
|
|
135
|
+
* completed work. An explicit transition to a nonterminal state permits a
|
|
136
|
+
* later claim. */
|
|
137
|
+
claim(id: string): Promise<boolean>
|
|
138
|
+
comment(id: string, body: string): Promise<void>
|
|
139
|
+
transition(id: string, state: string): Promise<void>
|
|
140
|
+
create(draft: TicketDraft, opts?: TicketCreateOptions): Promise<Ticket>
|
|
141
|
+
/** Partially replace editable fields. The patch must be nonempty and strict;
|
|
142
|
+
* omitted fields (including unmodeled provider metadata) remain untouched. */
|
|
143
|
+
update(id: string, patch: TicketUpdate): Promise<void>
|
|
144
|
+
/** Add one same-source blocker. Retries are idempotent; the target and
|
|
145
|
+
* blocker must exist, and a ticket may not block itself. */
|
|
146
|
+
addBlocker(id: string, blockerId: string): Promise<void>
|
|
147
|
+
/** Remove one same-source blocker. Missing relations are successful no-ops;
|
|
148
|
+
* only the target ticket is required to still exist. */
|
|
149
|
+
removeBlocker(id: string, blockerId: string): Promise<void>
|
|
150
|
+
/**
|
|
151
|
+
* Dependency-graph nodes for `ids`. The result covers EVERY requested id, in
|
|
152
|
+
* request order; a missing id comes back `{exists: false, resolved: false,
|
|
153
|
+
* blockedBy: []}`. Read at ticket creation and at dispatch time only — both
|
|
154
|
+
* are initiation, so §13's "never consulted mid-build" is untouched.
|
|
155
|
+
*/
|
|
156
|
+
dependencyStates(ids: string[]): Promise<DependencyState[]>
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── Workspace (SPEC §3.2, §7) ────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
export interface WorkspaceHandle {
|
|
162
|
+
provider: string
|
|
163
|
+
/** Provider-scoped identifier (worktree path or sandbox name). */
|
|
164
|
+
ref: string
|
|
165
|
+
/** Absolute working-copy path in the provider's execution environment. It
|
|
166
|
+
* is not necessarily reachable from the dispatcher. */
|
|
167
|
+
path: string
|
|
168
|
+
/** Dispatcher-reachable path when one exists. Remote providers omit it. */
|
|
169
|
+
localPath?: string
|
|
170
|
+
branch: string
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface WorkspaceProvisionResult extends WorkspaceHandle {
|
|
174
|
+
/** Durable evidence for the commit this provision selected or reused. */
|
|
175
|
+
base: WorkspaceBase
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface WorkspacePublication {
|
|
179
|
+
/** Observe whether the exact requested commit already reached the durable
|
|
180
|
+
* remote branch after an interrupted acknowledgement. */
|
|
181
|
+
isPublished?(input: { sha: string; branch: string }): Promise<boolean>
|
|
182
|
+
/** Publish exactly one commit to exactly one branch; no arbitrary command
|
|
183
|
+
* surface is exposed to the dispatcher or build child. */
|
|
184
|
+
publish(input: { ref: string; sha: string; branch: string }): Promise<void>
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Snapshot-purge evidence recorded with a workspace cleanup fact. Absence of
|
|
188
|
+
* leftover snapshots can be proven from these durable events without querying
|
|
189
|
+
* the provider. */
|
|
190
|
+
export interface WorkspaceReapSnapshots {
|
|
191
|
+
outcome: 'confirmed' | 'absent' | 'unknown'
|
|
192
|
+
deleted?: number
|
|
193
|
+
error?: string
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface WorkspaceReapOutcome {
|
|
197
|
+
outcome: 'confirmed' | 'absent'
|
|
198
|
+
snapshots: WorkspaceReapSnapshots
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface WorkspaceRecovery {
|
|
202
|
+
/** Fence an unavailable environment. Unknown outcomes must reject so the
|
|
203
|
+
* dispatcher retains the lease/workspace fact and retries after its fence. */
|
|
204
|
+
reap(handle: WorkspaceHandle): Promise<WorkspaceReapOutcome>
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface WorkspaceProvider {
|
|
208
|
+
readonly name: string
|
|
209
|
+
/** Optional workspace-adjacent executor. Remote providers substitute here;
|
|
210
|
+
* locally reachable providers use the shipped subprocess capability. */
|
|
211
|
+
readonly buildExecution?: BuildExecution
|
|
212
|
+
/** Optional hosted-harvest executor. Present on providers that can
|
|
213
|
+
* provision a disposable environment for harvest sessions; its absence
|
|
214
|
+
* keeps harvest running locally in the dispatcher's workspace. */
|
|
215
|
+
readonly harvestExecution?: HarvestExecution
|
|
216
|
+
/** Optional operator-sandbox host (AUT-340): one persistent,
|
|
217
|
+
* credential-free environment per operator × repository behind registry
|
|
218
|
+
* tools. Present on providers that can provision it; absent means the
|
|
219
|
+
* provider cannot host an operator sandbox, and sandbox tools stay
|
|
220
|
+
* unavailable over that provider. */
|
|
221
|
+
readonly orchestratorSandbox?: OperatorSandboxExecution
|
|
222
|
+
/** Trusted dispatcher-only publication capability for remote workspaces. */
|
|
223
|
+
readonly publication?: WorkspacePublication
|
|
224
|
+
/** Optional only for remote/disposable providers. */
|
|
225
|
+
readonly recovery?: WorkspaceRecovery
|
|
226
|
+
provision(opts: {
|
|
227
|
+
repo: string
|
|
228
|
+
baseBranch: string
|
|
229
|
+
branch: string
|
|
230
|
+
/** Immutable recovery checkpoint, never a moved base branch. */
|
|
231
|
+
revision?: string
|
|
232
|
+
/** Monotonic environment generation used in provider idempotency keys. */
|
|
233
|
+
generation?: number
|
|
234
|
+
}): Promise<WorkspaceProvisionResult>
|
|
235
|
+
release(handle: WorkspaceHandle): Promise<void>
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── Forge (SPEC §3.2, §8.6, §15.7) ───────────────────────────────────────────
|
|
239
|
+
//
|
|
240
|
+
// D7: agents never touch the remote — push, PR creation, and forge API calls
|
|
241
|
+
// are plumbing triggered by terminal commands. D1: PR→main is squash merge,
|
|
242
|
+
// main→feature is a merge commit, rebase is banned.
|
|
243
|
+
|
|
244
|
+
export interface PrRef {
|
|
245
|
+
number: number
|
|
246
|
+
/** Provider locator. HTTP(S) when the forge has a web UI; otherwise a
|
|
247
|
+
* meaningful provider-native identifier such as a local Git ref. */
|
|
248
|
+
url: string
|
|
249
|
+
headSha: string
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export type PrState =
|
|
253
|
+
| { state: 'open'; mergeable: boolean | null }
|
|
254
|
+
| { state: 'merged'; sha: string }
|
|
255
|
+
| { state: 'closed' }
|
|
256
|
+
|
|
257
|
+
/** Abort cleanup may race landing. A merged result is terminal and must never
|
|
258
|
+
* be overwritten by a close attempt. */
|
|
259
|
+
export type ClosePrResult = { state: 'closed' } | { state: 'merged'; sha: string }
|
|
260
|
+
|
|
261
|
+
/** Result of reconciling durable auto-merge intent with the forge. Native
|
|
262
|
+
* state is acknowledged only by `applied`; the other results deliberately
|
|
263
|
+
* leave the command pending for a later janitor poll. */
|
|
264
|
+
export type AutoMergeDeferralCode =
|
|
265
|
+
| 'github-plan-limitation'
|
|
266
|
+
| 'repository-auto-merge-disabled'
|
|
267
|
+
| 'unproven-gate-state'
|
|
268
|
+
/** A persistent, definite state — GitHub has computed merge conflicts with
|
|
269
|
+
* the base branch. Recovery is updating the branch or resolving the
|
|
270
|
+
* conflicts, not gate-state investigation. */
|
|
271
|
+
| 'merge-conflicts'
|
|
272
|
+
/** Mergeability not yet computed — transient, resolves on retry. */
|
|
273
|
+
| 'mergeability-uncomputed'
|
|
274
|
+
| 'local-base-checkout-dirty'
|
|
275
|
+
| 'local-git-identity-missing'
|
|
276
|
+
|
|
277
|
+
/** How a deferral resolves. `pipeline-resolved` codes are recovered by the
|
|
278
|
+
* pipeline itself — reconcile for merge conflicts, a later poll for
|
|
279
|
+
* not-yet-computed mergeability — and record no operator observation.
|
|
280
|
+
* `human-actionable` codes need a person to fix the named condition and keep
|
|
281
|
+
* the one-observation-per-PR-and-command rule. Adding a code without choosing
|
|
282
|
+
* a class is a compile error. */
|
|
283
|
+
export type AutoMergeDeferralClass = 'pipeline-resolved' | 'human-actionable'
|
|
284
|
+
|
|
285
|
+
export const autoMergeDeferralClasses = {
|
|
286
|
+
'github-plan-limitation': 'human-actionable',
|
|
287
|
+
'repository-auto-merge-disabled': 'human-actionable',
|
|
288
|
+
'unproven-gate-state': 'human-actionable',
|
|
289
|
+
'merge-conflicts': 'pipeline-resolved',
|
|
290
|
+
'mergeability-uncomputed': 'pipeline-resolved',
|
|
291
|
+
'local-base-checkout-dirty': 'human-actionable',
|
|
292
|
+
'local-git-identity-missing': 'human-actionable',
|
|
293
|
+
} as const satisfies Record<AutoMergeDeferralCode, AutoMergeDeferralClass>
|
|
294
|
+
|
|
295
|
+
export interface AutoMergeDeferralReason {
|
|
296
|
+
/** Stable machine-readable family used by durable operator observations. */
|
|
297
|
+
code: AutoMergeDeferralCode
|
|
298
|
+
/** Provider detail. It must identify auto-merge inspection/application, not PR creation. */
|
|
299
|
+
detail: string
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export type AutoMergeResult =
|
|
303
|
+
| { kind: 'applied' }
|
|
304
|
+
| { kind: 'ungated'; headSha: string }
|
|
305
|
+
| { kind: 'deferred'; reason?: AutoMergeDeferralReason }
|
|
306
|
+
|
|
307
|
+
/** One explicitly designated image artifact to copy to a review-window host. */
|
|
308
|
+
export interface PrAttachmentUploadRequest {
|
|
309
|
+
workspacePath: string
|
|
310
|
+
target: PrImageHostTarget
|
|
311
|
+
prUrl: string
|
|
312
|
+
attachment: PrAttachment
|
|
313
|
+
content: Uint8Array
|
|
314
|
+
/** Full lowercase SHA-256 / BuildStore blobRef for `content`. */
|
|
315
|
+
sha256: string
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export interface PrAttachmentReclaimRequest {
|
|
319
|
+
/** Cleanup deliberately runs from the main repository after workspace removal. */
|
|
320
|
+
workspacePath: string
|
|
321
|
+
asset: HostedPrAttachmentAsset
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Narrow optional Forge capability. Its absence is a supported text-only
|
|
325
|
+
* path, not a verification outcome. */
|
|
326
|
+
export interface PrAttachmentHosting {
|
|
327
|
+
upload(request: PrAttachmentUploadRequest): Promise<HostedPrAttachmentAsset>
|
|
328
|
+
reclaim(request: PrAttachmentReclaimRequest): Promise<void>
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export interface Forge {
|
|
332
|
+
readonly name: string
|
|
333
|
+
/** Optional because another forge may support PRs but not public image assets. */
|
|
334
|
+
readonly prAttachments?: PrAttachmentHosting
|
|
335
|
+
/** Publish the workspace's current HEAD to this remote destination branch. */
|
|
336
|
+
pushBranch(workspacePath: string, branch: string): Promise<void>
|
|
337
|
+
openPr(opts: {
|
|
338
|
+
workspacePath: string
|
|
339
|
+
head: string
|
|
340
|
+
base: string
|
|
341
|
+
title: string
|
|
342
|
+
body: string
|
|
343
|
+
/** Untouched deposited pr-description. Providers may use it as the
|
|
344
|
+
* landing message; title/body remain the portable review projection. */
|
|
345
|
+
mergeMessage?: string
|
|
346
|
+
}): Promise<PrRef>
|
|
347
|
+
/** Optional authoritative base resolver. Capability-less adapters retain
|
|
348
|
+
* the legacy origin fetch performed by the caller. The destination is a
|
|
349
|
+
* build-private ref in the workspace's shared Git database. */
|
|
350
|
+
snapshotBase?(opts: {
|
|
351
|
+
workspacePath: string
|
|
352
|
+
base: string
|
|
353
|
+
destinationRef: string
|
|
354
|
+
}): Promise<string>
|
|
355
|
+
/** Janitor poll (§15.7): merged/closed/mergeability for one PR. */
|
|
356
|
+
getPrState(workspacePath: string, number: number): Promise<PrState>
|
|
357
|
+
/** Optional additive abort-cleanup capabilities. Older plugins remain
|
|
358
|
+
* loadable, but cleanup stays pending with an actionable diagnostic. */
|
|
359
|
+
closePr?(workspacePath: string, number: number): Promise<ClosePrResult>
|
|
360
|
+
deleteBranch?(workspacePath: string, branch: string): Promise<void>
|
|
361
|
+
/**
|
|
362
|
+
* Reconcile GitHub-native auto-merge desired state. Enabling returns an
|
|
363
|
+
* ungated candidate only after proving the base branch has no merge-blocking
|
|
364
|
+
* gate. Transient merge states defer quietly; non-transient inability to
|
|
365
|
+
* inspect or apply consent returns a reason-bearing deferral. The operation
|
|
366
|
+
* is idempotent and safe to retry across the forge-call/event-append crash window.
|
|
367
|
+
*/
|
|
368
|
+
setAutoMerge(workspacePath: string, number: number, enabled: boolean): Promise<AutoMergeResult>
|
|
369
|
+
/**
|
|
370
|
+
* Perform a normal squash merge, guarded by the inspected PR head. This is
|
|
371
|
+
* never an admin/force operation: forge-side protection still applies.
|
|
372
|
+
*/
|
|
373
|
+
squashMerge(workspacePath: string, number: number, expectedHeadSha: string): Promise<void>
|
|
374
|
+
/** Post the build's summary comment (§7.5). */
|
|
375
|
+
commentOnPr(workspacePath: string, number: number, body: string): Promise<void>
|
|
376
|
+
/** Optional checkout-less capability: the current tip of a remote branch.
|
|
377
|
+
* Throws when the branch does not exist (including 404); callers decide
|
|
378
|
+
* whether absence is an error or an `undefined`. Its absence retains the
|
|
379
|
+
* legacy host `git ls-remote` base resolution. */
|
|
380
|
+
remoteBranchSha?(branch: string): Promise<string>
|
|
381
|
+
/** Optional checkout-less capability: raw file bytes from the repository.
|
|
382
|
+
* Ref omitted → the repository's default branch. Throws when the path does
|
|
383
|
+
* not exist. Its absence retains reading configuration from a checkout. */
|
|
384
|
+
readFile?(path: string, ref?: string): Promise<string>
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ── AgentRunner (SPEC §9) ────────────────────────────────────────────────────
|
|
388
|
+
//
|
|
389
|
+
// Session-based, because review loops need memory: the producer *continues*
|
|
390
|
+
// its session across rounds; the reviewer gets a fresh one each round (§10).
|
|
391
|
+
// Every production adapter also exposes this distribution's `ab` launcher at
|
|
392
|
+
// the front of the tool environment's PATH; CLI availability is a runner
|
|
393
|
+
// guarantee and does not depend on a global installation or host ordering.
|
|
394
|
+
|
|
395
|
+
export interface AgentSessionHandle {
|
|
396
|
+
id: string
|
|
397
|
+
runner: string
|
|
398
|
+
model?: string
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
interface AgentTurnResultBase {
|
|
402
|
+
/** Final text of the turn — informational only; outcomes travel the typed
|
|
403
|
+
* channel (`ab done|verdict|escalate`), never parsed from output (§8.4). */
|
|
404
|
+
text: string
|
|
405
|
+
usage: { inputTokens: number; outputTokens: number; turns: number }
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/** A turn whose provider/runtime completed normally. This does not mean the
|
|
409
|
+
* phase completed: only a typed terminal event can express that outcome. */
|
|
410
|
+
export interface AgentTurnCompleted extends AgentTurnResultBase {
|
|
411
|
+
kind: 'completed'
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export type AgentFailureCause = 'availability' | 'exhaustion' | 'credentials' | 'configuration'
|
|
415
|
+
|
|
416
|
+
export interface AgentTurnFailure {
|
|
417
|
+
/** Provider/runner text, preserved verbatim when one was supplied. */
|
|
418
|
+
message: string
|
|
419
|
+
/** False means "apply the existing bounded retry policy"; it does not claim
|
|
420
|
+
* the failure is transient. True remains the compatibility signal used by
|
|
421
|
+
* retry policy. Provider exhaustion is permanent for retry purposes while
|
|
422
|
+
* still being eligible for a configured alternate. */
|
|
423
|
+
permanent: boolean
|
|
424
|
+
/** Additive structured routing cause. Legacy plugin runners may omit it;
|
|
425
|
+
* their existing permanent bit then supplies the conservative behavior. */
|
|
426
|
+
cause?: AgentFailureCause
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/** A turn that reached a provider/runtime-declared failure while retaining an
|
|
430
|
+
* endable session handle, so transcript deposition remains guaranteed. */
|
|
431
|
+
export interface AgentTurnFailed extends AgentTurnResultBase {
|
|
432
|
+
kind: 'failed'
|
|
433
|
+
failure: AgentTurnFailure
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** SDK/provider-declared failures must use the failed variant. A completed turn
|
|
437
|
+
* with no typed terminal is the distinct `no-terminal` condition (§8.4). */
|
|
438
|
+
export type AgentTurnResult = AgentTurnCompleted | AgentTurnFailed
|
|
439
|
+
|
|
440
|
+
/** Transcripts come back through the interface, not scraped from disk (§9). */
|
|
441
|
+
export interface Transcript {
|
|
442
|
+
content: string
|
|
443
|
+
metadata: {
|
|
444
|
+
runner: string
|
|
445
|
+
model?: string
|
|
446
|
+
usage: { inputTokens: number; outputTokens: number; turns: number }
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export interface AgentStartOpts {
|
|
451
|
+
/** Installed skill to invoke, e.g. `ab-plan`. */
|
|
452
|
+
skill: string
|
|
453
|
+
/** Opaque argument passed to the skill (build slug or harvest run id). */
|
|
454
|
+
invocation?: string
|
|
455
|
+
/** Backward-compatible build name; new callers should also set invocation. */
|
|
456
|
+
buildSlug?: string
|
|
457
|
+
workspacePath: string
|
|
458
|
+
model?: string
|
|
459
|
+
/** Extra CLI argv tokens for the selected runtime, in declared order. */
|
|
460
|
+
args?: readonly string[]
|
|
461
|
+
/** Scoped ambient auth (D8): AB_STORE, AB_BUILD, AB_PHASE, AB_SESSION,
|
|
462
|
+
* AB_TOKEN. Adapters merge it over their process environment and then apply
|
|
463
|
+
* the runner-controlled Autobuild CLI PATH prefix. */
|
|
464
|
+
env: Record<string, string>
|
|
465
|
+
/** Caller-owned cancellation for this turn. Aborting must leave any created
|
|
466
|
+
* session endable so its transcript can still be deposited. */
|
|
467
|
+
signal?: AbortSignal
|
|
468
|
+
/** Live session stream, present only when the resolved runtime declared
|
|
469
|
+
* the streaming capability (SPEC §9). A turn without it emits no parts. */
|
|
470
|
+
stream?: SessionStreamEmitter
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** Per-turn refresh for a continued session (§10): each continue turn is a
|
|
474
|
+
* NEW session bracket (next round, fresh session id), so the runner
|
|
475
|
+
* re-issues ambient auth for the turn (D8) — merged over the start env. A
|
|
476
|
+
* turn left on round 1's env would have the CLI resolve the stale round and
|
|
477
|
+
* reject the continued round's terminal as a D5 second call (§8.4). */
|
|
478
|
+
export function agentInvocation(opts: AgentStartOpts): string {
|
|
479
|
+
const invocation = opts.invocation ?? opts.buildSlug
|
|
480
|
+
if (invocation === undefined || invocation === '') {
|
|
481
|
+
throw new Error('AgentStartOpts requires a non-empty invocation')
|
|
482
|
+
}
|
|
483
|
+
return invocation
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export interface AgentContinueOpts {
|
|
487
|
+
env?: Record<string, string>
|
|
488
|
+
/** Caller-owned cancellation for this continued turn. */
|
|
489
|
+
signal?: AbortSignal
|
|
490
|
+
/** Live session stream, present only when the resolved runtime declared
|
|
491
|
+
* the streaming capability (SPEC §9). A turn without it emits no parts. */
|
|
492
|
+
stream?: SessionStreamEmitter
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export interface AgentRunner {
|
|
496
|
+
readonly name: string
|
|
497
|
+
start(opts: AgentStartOpts): Promise<{ session: AgentSessionHandle; result: AgentTurnResult }>
|
|
498
|
+
/** Review-loop rounds (§10). Adapters without native resumption implement
|
|
499
|
+
* this as start-with-rehydrate-from-store (§9). */
|
|
500
|
+
continue(
|
|
501
|
+
session: AgentSessionHandle,
|
|
502
|
+
message: string,
|
|
503
|
+
opts?: AgentContinueOpts,
|
|
504
|
+
): Promise<AgentTurnResult>
|
|
505
|
+
/** Always called; the transcript goes to the store — the corpus is
|
|
506
|
+
* guaranteed complete (§9). */
|
|
507
|
+
end(session: AgentSessionHandle): Promise<Transcript>
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// ── TelemetrySource (SPEC §3.2, §12) ─────────────────────────────────────────
|
|
511
|
+
|
|
512
|
+
export interface Signal {
|
|
513
|
+
source: string
|
|
514
|
+
id: string
|
|
515
|
+
title: string
|
|
516
|
+
url?: string
|
|
517
|
+
/** Evidence for the proposal this signal may become (spec standard §6.1). */
|
|
518
|
+
evidence: Record<string, unknown>
|
|
519
|
+
occurrences?: number
|
|
520
|
+
usersAffected?: number
|
|
521
|
+
firstSeen?: string
|
|
522
|
+
lastSeen?: string
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export interface TelemetrySource {
|
|
526
|
+
readonly name: string
|
|
527
|
+
fetchSignals(since?: string): Promise<Signal[]>
|
|
528
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/** Immutable identity passed when a workspace-adjacent executor is asked to
|
|
2
|
+
* start a build. Build state, workspace location, config, progress, and outcome
|
|
3
|
+
* are deliberately absent: both sides obtain those only from the BuildStore. */
|
|
4
|
+
export interface BuildExecutionStart {
|
|
5
|
+
slug: string
|
|
6
|
+
storeRef: string
|
|
7
|
+
instance: string
|
|
8
|
+
/** Provider-native locator from the current workspace.provisioned fact. */
|
|
9
|
+
workspaceRef: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Serialized private-child envelope. Host PIDs are meaningful only to the
|
|
13
|
+
* local executor; an environment-owned VM is supervised as one unit by its
|
|
14
|
+
* provider. Keeping the modes discriminated prevents accidental cross-namespace
|
|
15
|
+
* signalling. */
|
|
16
|
+
export type BuildChildLaunch = BuildExecutionStart &
|
|
17
|
+
(
|
|
18
|
+
| { supervision: { kind: 'local-parent'; parentPid: number } }
|
|
19
|
+
| { supervision: { kind: 'environment' } }
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
export function parseBuildChildLaunch(value: unknown): BuildChildLaunch | undefined {
|
|
23
|
+
if (value === null || typeof value !== 'object') return undefined
|
|
24
|
+
const candidate = value as Partial<BuildChildLaunch> & { supervision?: Record<string, unknown> }
|
|
25
|
+
if (
|
|
26
|
+
typeof candidate.slug !== 'string' ||
|
|
27
|
+
candidate.slug.length === 0 ||
|
|
28
|
+
typeof candidate.storeRef !== 'string' ||
|
|
29
|
+
candidate.storeRef.length === 0 ||
|
|
30
|
+
typeof candidate.instance !== 'string' ||
|
|
31
|
+
candidate.instance.length === 0 ||
|
|
32
|
+
typeof candidate.workspaceRef !== 'string' ||
|
|
33
|
+
candidate.workspaceRef.length === 0 ||
|
|
34
|
+
candidate.supervision === undefined
|
|
35
|
+
)
|
|
36
|
+
return undefined
|
|
37
|
+
const supervision = candidate.supervision
|
|
38
|
+
if (supervision.kind === 'environment') {
|
|
39
|
+
if (Object.hasOwn(supervision, 'parentPid')) return undefined
|
|
40
|
+
return candidate as BuildChildLaunch
|
|
41
|
+
}
|
|
42
|
+
if (
|
|
43
|
+
supervision.kind === 'local-parent' &&
|
|
44
|
+
Number.isInteger(supervision.parentPid) &&
|
|
45
|
+
(supervision.parentPid as number) > 0
|
|
46
|
+
)
|
|
47
|
+
return candidate as BuildChildLaunch
|
|
48
|
+
return undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const BUILD_EXECUTION_LEASE_TTL_MS = 60_000
|
|
52
|
+
|
|
53
|
+
/** Child exit is liveness evidence only. It carries no pipeline outcome. */
|
|
54
|
+
export interface BuildExecutionExit {
|
|
55
|
+
exitCode: number | null
|
|
56
|
+
signal?: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Immutable provider identity recorded as soon as an execution starts. */
|
|
60
|
+
export interface BuildExecutionIdentity {
|
|
61
|
+
provider: string
|
|
62
|
+
workspaceRef: string
|
|
63
|
+
/** Provider-native VM/container identity when it differs from workspaceRef. */
|
|
64
|
+
environmentId?: string
|
|
65
|
+
/** Provider-native session identity. A resumed persistent VM gets a new one. */
|
|
66
|
+
sessionId?: string
|
|
67
|
+
/** Provider-native detached command identity, recorded durably at launch so
|
|
68
|
+
* a later process can re-observe the execution without process memory. */
|
|
69
|
+
commandId?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Teardown must distinguish proof from an interrupted/ambiguous acknowledgement. */
|
|
73
|
+
export type BuildExecutionTeardownResult =
|
|
74
|
+
| { outcome: 'confirmed' | 'absent' }
|
|
75
|
+
| { outcome: 'unknown'; error: string }
|
|
76
|
+
|
|
77
|
+
/** How a handle's lifetime is tied to its supervising process. `local-parent`
|
|
78
|
+
* means the kernel owns the process tree and must reap it at teardown;
|
|
79
|
+
* `environment` means the guest environment owns the execution and the kernel
|
|
80
|
+
* supervises only its local wait — teardown detaches instead of stopping, so
|
|
81
|
+
* the guest outlives the supervising process and a later invocation settles
|
|
82
|
+
* the execution from the Store plus provider liveness. */
|
|
83
|
+
export type BuildExecutionSupervision = 'local-parent' | 'environment'
|
|
84
|
+
|
|
85
|
+
export interface BuildExecutionHandle {
|
|
86
|
+
/** Available for supervision/tests, never used as build state. */
|
|
87
|
+
readonly pid?: number
|
|
88
|
+
readonly identity?: BuildExecutionIdentity
|
|
89
|
+
/** Explicit supervision kind. Never inferred from `environmentId` — a local
|
|
90
|
+
* pid and a remote VM id are different namespaces. */
|
|
91
|
+
readonly supervision: BuildExecutionSupervision
|
|
92
|
+
/** Resolves only after the execution environment has reaped everything the
|
|
93
|
+
* build started. The exit projection still describes the build leader. */
|
|
94
|
+
readonly completion: Promise<BuildExecutionExit>
|
|
95
|
+
/** Idempotent shutdown: graceful, bounded force escalation, then full reap. */
|
|
96
|
+
stop(): Promise<BuildExecutionTeardownResult | void>
|
|
97
|
+
/** Stop supervising without touching the environment: abort the local wait
|
|
98
|
+
* only. No command kill, no environment stop, no lease release, no
|
|
99
|
+
* `execution.ended`, no publication settlement. The teardown mode when the
|
|
100
|
+
* guest must outlive the supervising process. */
|
|
101
|
+
detach(): Promise<void>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Provider liveness observation of one recorded execution, derived from
|
|
105
|
+
* provider state rather than process memory. `ended` carries the observed
|
|
106
|
+
* exit code; `lost` means the provider proved the environment/command no
|
|
107
|
+
* longer exists without an exit code. */
|
|
108
|
+
export type ExecutionObservation =
|
|
109
|
+
| { state: 'running' }
|
|
110
|
+
| { state: 'ended'; exitCode?: number }
|
|
111
|
+
| { state: 'lost' }
|
|
112
|
+
|
|
113
|
+
/** The substitutable seam at the workspace boundary. A remote workspace
|
|
114
|
+
* provider may implement this by starting the fixed build process beside its
|
|
115
|
+
* checkout; the kernel need not know where that is. Every provider owns full
|
|
116
|
+
* teardown of the environment and descendants it starts before completion. */
|
|
117
|
+
export interface BuildExecution {
|
|
118
|
+
start(input: BuildExecutionStart): Promise<BuildExecutionHandle>
|
|
119
|
+
/** Optional liveness observation of a previously recorded execution, from
|
|
120
|
+
* provider state alone. Absent = this provider cannot observe; callers
|
|
121
|
+
* supervise exactly as before ("cannot observe" never means "act"). */
|
|
122
|
+
observe?(identity: BuildExecutionIdentity): Promise<ExecutionObservation>
|
|
123
|
+
}
|