@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,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator-session operations behind the operator API (SPEC §7.1.1): the
|
|
3
|
+
* signed-in operator's orchestrator conversations. Every write is attributed
|
|
4
|
+
* to the signed-in operator — the token's user, never a client-supplied
|
|
5
|
+
* identity — and `via` is never set on session events: session events are the
|
|
6
|
+
* delegate's own log, and here the operator IS the actor.
|
|
7
|
+
*
|
|
8
|
+
* Ownership lives here, in the server seam, because the store has no
|
|
9
|
+
* principal concept: a session belongs to its creating operator; other
|
|
10
|
+
* operators of the same repository can read it and cannot write to it. The
|
|
11
|
+
* store stays neutral; the API is the gatekeeper — writes of any kind to an
|
|
12
|
+
* archived session are refusals here, not store errors.
|
|
13
|
+
*/
|
|
14
|
+
import { humanActor } from '../events/envelope'
|
|
15
|
+
import type { SessionEventWrite } from '../events/sessions'
|
|
16
|
+
import { reduceSession, type SessionState, type SessionTurn } from '../sessions/reducer'
|
|
17
|
+
import type { StreamRead } from '../store/streams/types'
|
|
18
|
+
import type { BuildStore, SessionRecord } from '../store/types'
|
|
19
|
+
import type { OperatorSandboxService } from './sandbox'
|
|
20
|
+
|
|
21
|
+
export class OperatorSessionError extends Error {
|
|
22
|
+
constructor(
|
|
23
|
+
readonly code: 'not-found' | 'refusal' | 'forbidden',
|
|
24
|
+
message: string,
|
|
25
|
+
) {
|
|
26
|
+
super(message)
|
|
27
|
+
this.name = 'OperatorSessionError'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function requireSession(
|
|
32
|
+
store: BuildStore,
|
|
33
|
+
repo: string,
|
|
34
|
+
sid: string,
|
|
35
|
+
): Promise<SessionRecord> {
|
|
36
|
+
const record = await store.getSession(sid)
|
|
37
|
+
if (record === null || record.repo !== repo) {
|
|
38
|
+
throw new OperatorSessionError('not-found', `unknown session "${sid}"`)
|
|
39
|
+
}
|
|
40
|
+
return record
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function requireOwner(record: SessionRecord, user: string, operation: string): void {
|
|
44
|
+
if (record.operator !== user) {
|
|
45
|
+
throw new OperatorSessionError(
|
|
46
|
+
'forbidden',
|
|
47
|
+
`session "${record.id}" belongs to operator ${JSON.stringify(record.operator)}; ${JSON.stringify(user)} may not ${operation}`,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Every append funnels through here: one attribution point, one archived
|
|
53
|
+
* gate. The store has no principal concept, so this is where a token's user
|
|
54
|
+
* meets a session's operator. */
|
|
55
|
+
async function appendAs(
|
|
56
|
+
store: BuildStore,
|
|
57
|
+
record: SessionRecord,
|
|
58
|
+
user: string,
|
|
59
|
+
event: SessionEventWrite,
|
|
60
|
+
): Promise<void> {
|
|
61
|
+
requireOwner(record, user, `write ${event.type}`)
|
|
62
|
+
const state = reduceSession(await store.getSessionEvents(record.id))
|
|
63
|
+
if (state.status === 'archived') {
|
|
64
|
+
throw new OperatorSessionError('refusal', `session "${record.id}" is archived and read-only`)
|
|
65
|
+
}
|
|
66
|
+
await store.appendSessionEvent(record.id, event)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function listOperatorSessions(
|
|
70
|
+
store: BuildStore,
|
|
71
|
+
repo: string,
|
|
72
|
+
): Promise<SessionRecord[]> {
|
|
73
|
+
const sessions = await store.listSessions(repo)
|
|
74
|
+
return sessions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function createOperatorSession(
|
|
78
|
+
store: BuildStore,
|
|
79
|
+
repo: string,
|
|
80
|
+
user: string,
|
|
81
|
+
title?: string,
|
|
82
|
+
): Promise<SessionRecord> {
|
|
83
|
+
return store.createSession({
|
|
84
|
+
repo,
|
|
85
|
+
operator: user,
|
|
86
|
+
...(title !== undefined ? { title } : {}),
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface OperatorSessionView {
|
|
91
|
+
session: SessionRecord
|
|
92
|
+
state: SessionState
|
|
93
|
+
turns: SessionTurn[]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function getOperatorSession(
|
|
97
|
+
store: BuildStore,
|
|
98
|
+
repo: string,
|
|
99
|
+
sid: string,
|
|
100
|
+
): Promise<OperatorSessionView> {
|
|
101
|
+
const record = await requireSession(store, repo, sid)
|
|
102
|
+
const state = reduceSession(await store.getSessionEvents(record.id))
|
|
103
|
+
return { session: record, state, turns: state.turns }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function postOperatorMessage(
|
|
107
|
+
store: BuildStore,
|
|
108
|
+
repo: string,
|
|
109
|
+
sid: string,
|
|
110
|
+
user: string,
|
|
111
|
+
text: string,
|
|
112
|
+
): Promise<void> {
|
|
113
|
+
const record = await requireSession(store, repo, sid)
|
|
114
|
+
await appendAs(store, record, user, {
|
|
115
|
+
actor: humanActor(user),
|
|
116
|
+
type: 'message.posted',
|
|
117
|
+
payload: { text },
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function setOperatorWake(
|
|
122
|
+
store: BuildStore,
|
|
123
|
+
repo: string,
|
|
124
|
+
sid: string,
|
|
125
|
+
user: string,
|
|
126
|
+
globs: string[],
|
|
127
|
+
): Promise<void> {
|
|
128
|
+
const record = await requireSession(store, repo, sid)
|
|
129
|
+
await appendAs(store, record, user, {
|
|
130
|
+
actor: humanActor(user),
|
|
131
|
+
type: 'session.wake-set',
|
|
132
|
+
payload: { globs },
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function answerOperatorApproval(
|
|
137
|
+
store: BuildStore,
|
|
138
|
+
repo: string,
|
|
139
|
+
sid: string,
|
|
140
|
+
user: string,
|
|
141
|
+
turn: string,
|
|
142
|
+
toolCallId: string,
|
|
143
|
+
decision: 'approve' | 'deny',
|
|
144
|
+
): Promise<void> {
|
|
145
|
+
const record = await requireSession(store, repo, sid)
|
|
146
|
+
requireOwner(record, user, 'answer an approval')
|
|
147
|
+
const state = reduceSession(await store.getSessionEvents(record.id))
|
|
148
|
+
const pending = state.pendingApproval
|
|
149
|
+
if (
|
|
150
|
+
state.status === 'archived' ||
|
|
151
|
+
pending === undefined ||
|
|
152
|
+
pending.turn !== turn ||
|
|
153
|
+
pending.toolCallId !== toolCallId
|
|
154
|
+
) {
|
|
155
|
+
throw new OperatorSessionError(
|
|
156
|
+
'refusal',
|
|
157
|
+
`session "${record.id}" has no pending approval for turn ${JSON.stringify(turn)} tool call ${JSON.stringify(toolCallId)}`,
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
await appendAs(store, record, user, {
|
|
161
|
+
actor: humanActor(user),
|
|
162
|
+
type: 'approval.answered',
|
|
163
|
+
payload: { turn, toolCallId, decision },
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export async function archiveOperatorSession(
|
|
168
|
+
store: BuildStore,
|
|
169
|
+
repo: string,
|
|
170
|
+
sid: string,
|
|
171
|
+
user: string,
|
|
172
|
+
sandbox?: OperatorSandboxService,
|
|
173
|
+
): Promise<void> {
|
|
174
|
+
const record = await requireSession(store, repo, sid)
|
|
175
|
+
requireOwner(record, user, 'archive')
|
|
176
|
+
const state = reduceSession(await store.getSessionEvents(record.id))
|
|
177
|
+
if (state.status === 'archived') {
|
|
178
|
+
throw new OperatorSessionError('refusal', `session "${record.id}" is already archived`)
|
|
179
|
+
}
|
|
180
|
+
await store.appendSessionEvent(record.id, {
|
|
181
|
+
actor: humanActor(user),
|
|
182
|
+
type: 'session.archived',
|
|
183
|
+
payload: {},
|
|
184
|
+
})
|
|
185
|
+
// When the archive leaves the operator with no open session for this
|
|
186
|
+
// repository, their sandbox environment is released (snapshot purge,
|
|
187
|
+
// journal fact). The service derives the environment via its pure
|
|
188
|
+
// describe — never provisioning — and a never-provisioned operator is a
|
|
189
|
+
// no-op. Contained: a release failure must not fail an accepted archive.
|
|
190
|
+
if (sandbox !== undefined) {
|
|
191
|
+
const sessions = await store.listSessions(repo)
|
|
192
|
+
const open: SessionRecord[] = []
|
|
193
|
+
for (const session of sessions) {
|
|
194
|
+
if (session.operator !== user) continue
|
|
195
|
+
const sessionState = reduceSession(await store.getSessionEvents(session.id))
|
|
196
|
+
if (sessionState.status !== 'archived') open.push(session)
|
|
197
|
+
}
|
|
198
|
+
if (open.length === 0) {
|
|
199
|
+
try {
|
|
200
|
+
await sandbox.release(user, { repo })
|
|
201
|
+
} catch {
|
|
202
|
+
// The archive already succeeded; the environment stays journaled and
|
|
203
|
+
// the dispatcher's idle settlement stops it later.
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export async function readOperatorTurnStream(
|
|
210
|
+
store: BuildStore,
|
|
211
|
+
repo: string,
|
|
212
|
+
sid: string,
|
|
213
|
+
turn: string,
|
|
214
|
+
opts?: { since?: number; waitSeconds?: number },
|
|
215
|
+
): Promise<StreamRead> {
|
|
216
|
+
const record = await requireSession(store, repo, sid)
|
|
217
|
+
const state = reduceSession(await store.getSessionEvents(record.id))
|
|
218
|
+
const target = state.turns.find((candidate) => candidate.turn === turn)
|
|
219
|
+
if (target === undefined) {
|
|
220
|
+
throw new OperatorSessionError('not-found', `unknown turn "${turn}" in session "${record.id}"`)
|
|
221
|
+
}
|
|
222
|
+
return store.readStream(target.stream, opts)
|
|
223
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { Config } from '../config/schema'
|
|
2
|
+
import { reduceBuild } from '../kernel/reducer'
|
|
3
|
+
import {
|
|
4
|
+
changeBlockers,
|
|
5
|
+
createTicket,
|
|
6
|
+
moveTicket,
|
|
7
|
+
requireTicket,
|
|
8
|
+
updateTicket,
|
|
9
|
+
} from '../ports/tickets/operations'
|
|
10
|
+
import type { DependencyState, Ticket, TicketSource, TicketUpdate } from '../ports/types'
|
|
11
|
+
import type { BuildStore } from '../store/types'
|
|
12
|
+
import { defaultTriageState } from '../processes/dispatcher'
|
|
13
|
+
import { effectiveConfig, OperatorQueryError } from './query'
|
|
14
|
+
|
|
15
|
+
export interface OperatorTicketContext {
|
|
16
|
+
teamKey: string
|
|
17
|
+
claimedState?: string
|
|
18
|
+
createState?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface OperatorTicketBackend {
|
|
22
|
+
sourceFor(context: OperatorTicketContext): TicketSource | Promise<TicketSource>
|
|
23
|
+
statesFor(context: OperatorTicketContext, source: TicketSource): string[] | Promise<string[]>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface OperatorTicketQueue {
|
|
27
|
+
states: string[]
|
|
28
|
+
tickets: Ticket[]
|
|
29
|
+
diagnostics: string[]
|
|
30
|
+
criteria: { state?: string; labels?: string[] }
|
|
31
|
+
/** Effective repository lifecycle names; clients must not invent provider states. */
|
|
32
|
+
triageState: string
|
|
33
|
+
readyState: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface OperatorTicketBuild {
|
|
37
|
+
slug: string
|
|
38
|
+
status: string
|
|
39
|
+
updatedAt: string
|
|
40
|
+
link: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface OperatorTicketDetail {
|
|
44
|
+
ticket: Ticket
|
|
45
|
+
blockers: DependencyState[]
|
|
46
|
+
build: OperatorTicketBuild | null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function contextFor(config: Config, repo: string): OperatorTicketContext {
|
|
50
|
+
const tickets = config.tickets
|
|
51
|
+
if (tickets.teamKey === undefined) {
|
|
52
|
+
throw new OperatorQueryError(
|
|
53
|
+
'effective-config-unavailable',
|
|
54
|
+
`effective config unavailable for repository "${repo}": [tickets].teamKey is required for hosted ticket access`,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
teamKey: tickets.teamKey,
|
|
59
|
+
...(tickets.claimedState !== undefined ? { claimedState: tickets.claimedState } : {}),
|
|
60
|
+
...(tickets.createState !== undefined ? { createState: tickets.createState } : {}),
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function openOperatorTickets(opts: {
|
|
65
|
+
store: BuildStore
|
|
66
|
+
repo: string
|
|
67
|
+
backend: OperatorTicketBackend
|
|
68
|
+
}): Promise<{ config: Config; context: OperatorTicketContext; source: TicketSource }> {
|
|
69
|
+
const { config } = await effectiveConfig(opts.store, opts.repo)
|
|
70
|
+
const context = contextFor(config, opts.repo)
|
|
71
|
+
return { config, context, source: await opts.backend.sourceFor(context) }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function listOperatorTickets(opts: {
|
|
75
|
+
store: BuildStore
|
|
76
|
+
repo: string
|
|
77
|
+
backend: OperatorTicketBackend
|
|
78
|
+
state?: string
|
|
79
|
+
labels?: string[]
|
|
80
|
+
}): Promise<OperatorTicketQueue> {
|
|
81
|
+
const { config, context, source } = await openOperatorTickets(opts)
|
|
82
|
+
const triageState = defaultTriageState(config)
|
|
83
|
+
const readyState = config.tickets.readyState
|
|
84
|
+
// The operator surface is a grooming queue. Unlike the CLI's ticket list,
|
|
85
|
+
// labels narrow the default state instead of replacing its lifecycle gate.
|
|
86
|
+
const criteria = {
|
|
87
|
+
state: opts.state ?? triageState,
|
|
88
|
+
...(opts.labels !== undefined ? { labels: opts.labels } : {}),
|
|
89
|
+
}
|
|
90
|
+
const [listing, states] = await Promise.all([
|
|
91
|
+
source.listReady(criteria),
|
|
92
|
+
opts.backend.statesFor(context, source),
|
|
93
|
+
])
|
|
94
|
+
return {
|
|
95
|
+
states,
|
|
96
|
+
tickets: listing.tickets,
|
|
97
|
+
diagnostics: listing.diagnostics,
|
|
98
|
+
criteria,
|
|
99
|
+
triageState,
|
|
100
|
+
readyState,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function matchingBuild(
|
|
105
|
+
store: BuildStore,
|
|
106
|
+
repo: string,
|
|
107
|
+
id: string,
|
|
108
|
+
): Promise<OperatorTicketBuild | null> {
|
|
109
|
+
const matches = (await store.listBuilds()).filter(
|
|
110
|
+
(record) => record.repo === repo && record.ticket?.id === id,
|
|
111
|
+
)
|
|
112
|
+
const projected = await Promise.all(
|
|
113
|
+
matches.map(async (record) => ({
|
|
114
|
+
record,
|
|
115
|
+
state: reduceBuild(await store.getEvents(record.slug)),
|
|
116
|
+
})),
|
|
117
|
+
)
|
|
118
|
+
projected.sort((a, b) => {
|
|
119
|
+
const aActive = a.state.status !== 'done' && a.state.status !== 'aborted'
|
|
120
|
+
const bActive = b.state.status !== 'done' && b.state.status !== 'aborted'
|
|
121
|
+
if (aActive !== bActive) return aActive ? -1 : 1
|
|
122
|
+
return b.record.updatedAt.localeCompare(a.record.updatedAt)
|
|
123
|
+
})
|
|
124
|
+
const found = projected[0]
|
|
125
|
+
return found
|
|
126
|
+
? {
|
|
127
|
+
slug: found.record.slug,
|
|
128
|
+
status: found.state.status,
|
|
129
|
+
updatedAt: found.record.updatedAt,
|
|
130
|
+
link: `#build-${encodeURIComponent(found.record.slug)}`,
|
|
131
|
+
}
|
|
132
|
+
: null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function getOperatorTicket(opts: {
|
|
136
|
+
store: BuildStore
|
|
137
|
+
repo: string
|
|
138
|
+
backend: OperatorTicketBackend
|
|
139
|
+
id: string
|
|
140
|
+
}): Promise<OperatorTicketDetail> {
|
|
141
|
+
const { source } = await openOperatorTickets(opts)
|
|
142
|
+
const ticket = await requireTicket(source, opts.id)
|
|
143
|
+
const blockers = await source.dependencyStates(ticket.blockedBy ?? [])
|
|
144
|
+
return { ticket, blockers, build: await matchingBuild(opts.store, opts.repo, opts.id) }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function mutateOperatorTicket(opts: {
|
|
148
|
+
store: BuildStore
|
|
149
|
+
repo: string
|
|
150
|
+
backend: OperatorTicketBackend
|
|
151
|
+
operation:
|
|
152
|
+
| {
|
|
153
|
+
kind: 'create'
|
|
154
|
+
title: string
|
|
155
|
+
body: string
|
|
156
|
+
labels?: string[]
|
|
157
|
+
state?: string
|
|
158
|
+
blockedBy?: string[]
|
|
159
|
+
}
|
|
160
|
+
| { kind: 'update'; id: string; patch: TicketUpdate }
|
|
161
|
+
| { kind: 'move'; id: string; state: string }
|
|
162
|
+
| { kind: 'block' | 'unblock'; id: string; blockerIds: string[] }
|
|
163
|
+
}): Promise<OperatorTicketDetail> {
|
|
164
|
+
const { source } = await openOperatorTickets(opts)
|
|
165
|
+
const operation = opts.operation
|
|
166
|
+
if (operation.kind === 'update') await requireTicket(source, operation.id)
|
|
167
|
+
const ticket =
|
|
168
|
+
operation.kind === 'create'
|
|
169
|
+
? await createTicket(source, operation)
|
|
170
|
+
: operation.kind === 'update'
|
|
171
|
+
? (await updateTicket(source, operation.id, operation.patch))!
|
|
172
|
+
: operation.kind === 'move'
|
|
173
|
+
? await moveTicket(source, operation.id, operation.state)
|
|
174
|
+
: await changeBlockers(source, operation.id, operation.blockerIds, operation.kind)
|
|
175
|
+
const blockers = await source.dependencyStates(ticket.blockedBy ?? [])
|
|
176
|
+
return { ticket, blockers, build: await matchingBuild(opts.store, opts.repo, ticket.ref.id) }
|
|
177
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable plugin-authoring surface. Internal source paths are not public API;
|
|
3
|
+
* plugins should import only `@defrex/autobuild/plugin-sdk` (normally with
|
|
4
|
+
* `import type`) and run these same contracts against their adapters.
|
|
5
|
+
*/
|
|
6
|
+
export type * from '../ports/types'
|
|
7
|
+
export type * from '../ports/workspace/build-execution'
|
|
8
|
+
export type * from '../store/types'
|
|
9
|
+
export type {
|
|
10
|
+
RuntimeRegistration,
|
|
11
|
+
RuntimeRegistry,
|
|
12
|
+
SessionStreamInfo,
|
|
13
|
+
} from '../ports/runner/runtime'
|
|
14
|
+
export {
|
|
15
|
+
STREAM_PART_MAX_BYTES,
|
|
16
|
+
abortPart,
|
|
17
|
+
errorPart,
|
|
18
|
+
finishPart,
|
|
19
|
+
finishStepPart,
|
|
20
|
+
promptPart,
|
|
21
|
+
reasoningDeltaPart,
|
|
22
|
+
reasoningEndPart,
|
|
23
|
+
reasoningStartPart,
|
|
24
|
+
sessionPart,
|
|
25
|
+
startPart,
|
|
26
|
+
startStepPart,
|
|
27
|
+
textDeltaPart,
|
|
28
|
+
textEndPart,
|
|
29
|
+
textStartPart,
|
|
30
|
+
toolInputPart,
|
|
31
|
+
toolOutputPart,
|
|
32
|
+
truncationPart,
|
|
33
|
+
} from '../ports/runner/stream-parts'
|
|
34
|
+
export type {
|
|
35
|
+
OneShotCompletion,
|
|
36
|
+
OneShotCompletionInput,
|
|
37
|
+
OneShotCompletionResult,
|
|
38
|
+
} from '../ports/runner/one-shot'
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
CONTRACT_FOLLOW_UP,
|
|
42
|
+
CONTRACT_INVOCATION,
|
|
43
|
+
CONTRACT_ONE_SHOT_PROMPT,
|
|
44
|
+
CONTRACT_ONE_SHOT_TEXT,
|
|
45
|
+
CONTRACT_PERMANENT_FAILURE,
|
|
46
|
+
CONTRACT_RETRYABLE_FAILURE,
|
|
47
|
+
CONTRACT_SKILL,
|
|
48
|
+
describeAgentRunnerContract,
|
|
49
|
+
} from '../ports/runner/contract'
|
|
50
|
+
export type {
|
|
51
|
+
AgentRunnerContractFactory,
|
|
52
|
+
AgentRunnerContractHarness,
|
|
53
|
+
AgentRunnerContractOneShotHarness,
|
|
54
|
+
AgentRunnerContractOneShotObservation,
|
|
55
|
+
AgentRunnerContractScenario,
|
|
56
|
+
AgentRunnerContractTurnObservation,
|
|
57
|
+
} from '../ports/runner/contract'
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
PLUGIN_API_VERSION,
|
|
61
|
+
PluginApiCompatibilityError,
|
|
62
|
+
pluginApiCompatibility,
|
|
63
|
+
pluginManifestSchema,
|
|
64
|
+
parsePluginManifest,
|
|
65
|
+
} from '../plugins/manifest'
|
|
66
|
+
export type {
|
|
67
|
+
AgentRuntimePluginFactory,
|
|
68
|
+
AgentRuntimePluginRegistration,
|
|
69
|
+
AutobuildPluginManifest,
|
|
70
|
+
ForgePluginFactory,
|
|
71
|
+
ForgePluginRegistration,
|
|
72
|
+
PluginAdapterRegistration,
|
|
73
|
+
PluginApiCompatibility,
|
|
74
|
+
PluginContractDescriptor,
|
|
75
|
+
PluginFactory,
|
|
76
|
+
PluginFactoryContext,
|
|
77
|
+
TicketSourcePluginDescriptor,
|
|
78
|
+
TicketSourcePluginFactory,
|
|
79
|
+
TicketSourcePluginRegistration,
|
|
80
|
+
WorkspaceProviderPluginFactory,
|
|
81
|
+
WorkspaceProviderPluginRegistration,
|
|
82
|
+
} from '../plugins/manifest'
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
describeTicketSourceContract,
|
|
86
|
+
CONTRACT_TICKET_BODY,
|
|
87
|
+
contractIdempotencyKey,
|
|
88
|
+
contractLabelName,
|
|
89
|
+
contractTicketTitle,
|
|
90
|
+
} from '../ports/tickets/contract'
|
|
91
|
+
export type {
|
|
92
|
+
TicketSourceContractFactory,
|
|
93
|
+
TicketSourceContractHarness,
|
|
94
|
+
TicketSourceContractStates,
|
|
95
|
+
} from '../ports/tickets/contract'
|
|
96
|
+
|
|
97
|
+
export { describeWorkspaceProviderContract } from '../ports/workspace/contract'
|
|
98
|
+
export type {
|
|
99
|
+
WorkspaceProviderContractFactory,
|
|
100
|
+
WorkspaceProviderContractHarness,
|
|
101
|
+
} from '../ports/workspace/contract'
|
|
102
|
+
|
|
103
|
+
export { describeForgeContract } from '../ports/forge/contract'
|
|
104
|
+
export type {
|
|
105
|
+
ForgeContractControls,
|
|
106
|
+
ForgeContractFactory,
|
|
107
|
+
ForgeContractFactoryOptions,
|
|
108
|
+
ForgeContractHarness,
|
|
109
|
+
} from '../ports/forge/contract'
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
describeBlobStoreContract,
|
|
113
|
+
describeBuildStoreContract,
|
|
114
|
+
CONTRACT_T0,
|
|
115
|
+
ISO_TS,
|
|
116
|
+
buildCreatedWrite,
|
|
117
|
+
harvestStartedWrite,
|
|
118
|
+
messagePostedWrite,
|
|
119
|
+
planCompletedWrite,
|
|
120
|
+
sampleBuildInput,
|
|
121
|
+
sampleEventWrite,
|
|
122
|
+
} from '../store/contract'
|
|
123
|
+
export type {
|
|
124
|
+
BlobStoreFactory,
|
|
125
|
+
BlobStoreHarness,
|
|
126
|
+
BuildStoreFactory,
|
|
127
|
+
BuildStoreHarness,
|
|
128
|
+
} from '../store/contract'
|
|
129
|
+
|
|
130
|
+
export { FakeTicketSource } from '../ports/tickets/fake'
|
|
131
|
+
export { validateTicketUpdate } from '../ports/tickets/update'
|
|
132
|
+
export { FakeWorkspaceProvider } from '../ports/workspace/fake'
|
|
133
|
+
export { FakeForge } from '../ports/forge/fake'
|
|
134
|
+
export { ScriptedAgentRunner } from '../ports/runner/fake'
|
|
135
|
+
export { MemoryBlobStore, MemoryBuildStore } from '../store/memory'
|
|
136
|
+
|
|
137
|
+
// Runtime primitives needed by out-of-tree BuildStore adapters.
|
|
138
|
+
export { validateEventWrite } from '../events/catalog'
|
|
139
|
+
export type { AbEvent, EventEnvelope, EventWrite } from '../events/catalog'
|
|
140
|
+
export type { EventType } from '../events/payloads'
|
|
141
|
+
export { validateRepositoryEventWrite } from '../events/repository'
|
|
142
|
+
export type {
|
|
143
|
+
RepositoryEvent,
|
|
144
|
+
RepositoryEventEnvelope,
|
|
145
|
+
RepositoryEventType,
|
|
146
|
+
RepositoryEventWrite,
|
|
147
|
+
} from '../events/repository'
|
|
148
|
+
export { BuildScopeError, createBuildScopedStore } from '../store/build-scope'
|
|
149
|
+
export { createSessionStreamSink } from '../store/streams/session-writer'
|
|
150
|
+
export { pollingSubscribe } from '../store/subscribe'
|
|
151
|
+
export { assembleUIMessageDocument } from '../store/streams/assemble'
|
|
152
|
+
export { readStreamWithWait, STREAM_WAIT_POLL_MS } from '../store/streams/wait'
|
|
153
|
+
export {
|
|
154
|
+
clampWaitSeconds,
|
|
155
|
+
MAX_STREAM_WAIT_SECONDS,
|
|
156
|
+
serializedBatchSize,
|
|
157
|
+
STREAM_BATCH_MAX_BYTES,
|
|
158
|
+
STREAM_FORMAT,
|
|
159
|
+
StreamBatchTooLargeError,
|
|
160
|
+
StreamClosedError,
|
|
161
|
+
streamArtifactInput,
|
|
162
|
+
validateStreamParts,
|
|
163
|
+
} from '../store/streams/types'
|
|
164
|
+
export type {
|
|
165
|
+
StreamArtifactRef,
|
|
166
|
+
StreamChunk,
|
|
167
|
+
StreamOutcome,
|
|
168
|
+
StreamPart,
|
|
169
|
+
StreamRead,
|
|
170
|
+
StreamRecord,
|
|
171
|
+
StreamScope,
|
|
172
|
+
StreamStatus,
|
|
173
|
+
} from '../store/streams/types'
|
|
174
|
+
export { contentHash, systemClock, toBytes, validateExpectedSeq } from '../store/types'
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { loadConfig } from '../config/load'
|
|
3
|
+
import { describeForgeContract, type ForgeContractFactory } from '../ports/forge/contract'
|
|
4
|
+
import {
|
|
5
|
+
describeAgentRunnerContract,
|
|
6
|
+
type AgentRunnerContractFactory,
|
|
7
|
+
} from '../ports/runner/contract'
|
|
8
|
+
import {
|
|
9
|
+
describeTicketSourceContract,
|
|
10
|
+
type TicketSourceContractFactory,
|
|
11
|
+
} from '../ports/tickets/contract'
|
|
12
|
+
import {
|
|
13
|
+
describeWorkspaceProviderContract,
|
|
14
|
+
type WorkspaceProviderContractFactory,
|
|
15
|
+
} from '../ports/workspace/contract'
|
|
16
|
+
import { CONTRACT_ADAPTER_ENV, CONTRACT_PORT_ENV, CONTRACT_REPO_ENV } from './contract-env'
|
|
17
|
+
import { loadPlugins } from './load'
|
|
18
|
+
import { PLUGIN_PORTS, type PluginPort } from './registry'
|
|
19
|
+
|
|
20
|
+
function requiredEnv(name: string): string {
|
|
21
|
+
const value = process.env[name]
|
|
22
|
+
if (value === undefined || value.trim() === '') {
|
|
23
|
+
throw new Error(`plugin contract runner requires private environment ${name}`)
|
|
24
|
+
}
|
|
25
|
+
return value
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const repoRoot = requiredEnv(CONTRACT_REPO_ENV)
|
|
29
|
+
const rawPort = requiredEnv(CONTRACT_PORT_ENV)
|
|
30
|
+
const adapter = requiredEnv(CONTRACT_ADAPTER_ENV)
|
|
31
|
+
if (!PLUGIN_PORTS.includes(rawPort as PluginPort)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`${CONTRACT_PORT_ENV} must be one of ${PLUGIN_PORTS.join(', ')}; got "${rawPort}"`,
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
const port = rawPort as PluginPort
|
|
37
|
+
const config = await loadConfig(join(repoRoot, 'autobuild.toml'))
|
|
38
|
+
const registry = await loadPlugins(config.plugins, repoRoot)
|
|
39
|
+
const registration = registry.registration(port, adapter)
|
|
40
|
+
if (registration === undefined) {
|
|
41
|
+
throw new Error(`unknown ${port} adapter "${adapter}"`)
|
|
42
|
+
}
|
|
43
|
+
if (registration.contract === undefined) {
|
|
44
|
+
throw new Error(`${port} adapter "${adapter}" has no contract fixture descriptor`)
|
|
45
|
+
}
|
|
46
|
+
if (registration.contract.live === true && process.env.AB_RUN_LIVE_PORT_CONTRACTS !== '1') {
|
|
47
|
+
throw new Error('live plugin contracts require AB_RUN_LIVE_PORT_CONTRACTS=1')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const contractFactory = await registration.contract.factory({
|
|
51
|
+
config: {},
|
|
52
|
+
env: process.env,
|
|
53
|
+
repoRoot,
|
|
54
|
+
})
|
|
55
|
+
if (typeof contractFactory !== 'function') {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`${port} adapter "${adapter}" contract.factory must return the shared suite's harness factory`,
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
switch (port) {
|
|
62
|
+
case 'ticket-source':
|
|
63
|
+
describeTicketSourceContract(adapter, contractFactory as TicketSourceContractFactory)
|
|
64
|
+
break
|
|
65
|
+
case 'agent-runtime':
|
|
66
|
+
describeAgentRunnerContract(adapter, contractFactory as AgentRunnerContractFactory)
|
|
67
|
+
break
|
|
68
|
+
case 'workspace-provider':
|
|
69
|
+
describeWorkspaceProviderContract(adapter, contractFactory as WorkspaceProviderContractFactory)
|
|
70
|
+
break
|
|
71
|
+
case 'forge':
|
|
72
|
+
describeForgeContract(adapter, contractFactory as ForgeContractFactory)
|
|
73
|
+
break
|
|
74
|
+
}
|