@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.
Files changed (251) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +192 -0
  3. package/SPEC.md +2269 -0
  4. package/bin/ab-build-runner.ts +47 -0
  5. package/bin/ab-dev.ts +27 -0
  6. package/bin/ab-dispatch-kernel.ts +92 -0
  7. package/bin/ab-harvest-runner.ts +29 -0
  8. package/bin/ab-init-probe.ts +19 -0
  9. package/bin/ab-process-group-reaper.ts +32 -0
  10. package/bin/ab.ts +6 -0
  11. package/bin/agent/ab +3 -0
  12. package/docs/answering-blocked-builds.md +89 -0
  13. package/docs/architecture.md +726 -0
  14. package/docs/assets/headline-wide.png +0 -0
  15. package/docs/configuration.md +1623 -0
  16. package/docs/hosted-dispatcher-cutover-validation.md +178 -0
  17. package/docs/hosted-dispatcher.md +204 -0
  18. package/docs/mcp.md +119 -0
  19. package/docs/operator-api.md +186 -0
  20. package/docs/remote-store-protocol.md +972 -0
  21. package/docs/remote-ticket-protocol.md +79 -0
  22. package/docs/setup.md +289 -0
  23. package/docs/spec-standard.md +63 -0
  24. package/docs/ticket-dependencies.md +59 -0
  25. package/docs/vercel-sandbox-rollout-probe.md +7 -0
  26. package/package.json +135 -0
  27. package/packages/core/src/cli/args.ts +74 -0
  28. package/packages/core/src/cli/artifact.ts +182 -0
  29. package/packages/core/src/cli/binary.ts +198 -0
  30. package/packages/core/src/cli/build-control.ts +789 -0
  31. package/packages/core/src/cli/build-progress.ts +55 -0
  32. package/packages/core/src/cli/bulk-control.ts +385 -0
  33. package/packages/core/src/cli/context.ts +470 -0
  34. package/packages/core/src/cli/dashboard/actions.ts +59 -0
  35. package/packages/core/src/cli/dashboard/cells.ts +92 -0
  36. package/packages/core/src/cli/dashboard/composer.ts +230 -0
  37. package/packages/core/src/cli/dashboard/detail.ts +100 -0
  38. package/packages/core/src/cli/dashboard/frame-image.ts +373 -0
  39. package/packages/core/src/cli/dashboard/live.ts +152 -0
  40. package/packages/core/src/cli/dashboard/model.ts +1173 -0
  41. package/packages/core/src/cli/dashboard/poll.ts +196 -0
  42. package/packages/core/src/cli/dashboard/render.ts +1568 -0
  43. package/packages/core/src/cli/dashboard/selection.ts +66 -0
  44. package/packages/core/src/cli/dashboard/session-feed.ts +244 -0
  45. package/packages/core/src/cli/dashboard/session-view.ts +506 -0
  46. package/packages/core/src/cli/dashboard/transcript.ts +108 -0
  47. package/packages/core/src/cli/dev.ts +46 -0
  48. package/packages/core/src/cli/dispatch-frontend.ts +1045 -0
  49. package/packages/core/src/cli/dispatch-process.ts +259 -0
  50. package/packages/core/src/cli/dispatch.ts +3692 -0
  51. package/packages/core/src/cli/dotenv.ts +48 -0
  52. package/packages/core/src/cli/env.ts +206 -0
  53. package/packages/core/src/cli/harvest.ts +698 -0
  54. package/packages/core/src/cli/help.ts +713 -0
  55. package/packages/core/src/cli/init-agent.ts +106 -0
  56. package/packages/core/src/cli/init-validation.ts +583 -0
  57. package/packages/core/src/cli/init.ts +756 -0
  58. package/packages/core/src/cli/installation.ts +333 -0
  59. package/packages/core/src/cli/keyboard.ts +261 -0
  60. package/packages/core/src/cli/main.ts +1399 -0
  61. package/packages/core/src/cli/mcp.ts +279 -0
  62. package/packages/core/src/cli/models.ts +65 -0
  63. package/packages/core/src/cli/observe.ts +55 -0
  64. package/packages/core/src/cli/plugin.ts +227 -0
  65. package/packages/core/src/cli/pr-attachments.ts +138 -0
  66. package/packages/core/src/cli/pr-summary.ts +124 -0
  67. package/packages/core/src/cli/publication-settlement.ts +188 -0
  68. package/packages/core/src/cli/repo-state.ts +213 -0
  69. package/packages/core/src/cli/repository-status.ts +80 -0
  70. package/packages/core/src/cli/self-update.ts +443 -0
  71. package/packages/core/src/cli/status.ts +895 -0
  72. package/packages/core/src/cli/store-opening.contract.ts +289 -0
  73. package/packages/core/src/cli/store-opening.ts +167 -0
  74. package/packages/core/src/cli/store-ref.ts +33 -0
  75. package/packages/core/src/cli/terminal-restore.ts +174 -0
  76. package/packages/core/src/cli/terminal.ts +361 -0
  77. package/packages/core/src/cli/terminals.ts +995 -0
  78. package/packages/core/src/cli/testkit.ts +194 -0
  79. package/packages/core/src/cli/ticket.ts +484 -0
  80. package/packages/core/src/cli/upgrade-agent.ts +180 -0
  81. package/packages/core/src/cli/upgrade-commit.ts +468 -0
  82. package/packages/core/src/cli/upgrade-notice.ts +91 -0
  83. package/packages/core/src/cli/upgrade-progress.ts +110 -0
  84. package/packages/core/src/cli/upgrade.ts +888 -0
  85. package/packages/core/src/cli/wait.ts +587 -0
  86. package/packages/core/src/cli/watch.ts +932 -0
  87. package/packages/core/src/config/live.ts +255 -0
  88. package/packages/core/src/config/load.ts +98 -0
  89. package/packages/core/src/config/roles.ts +438 -0
  90. package/packages/core/src/config/schema.ts +774 -0
  91. package/packages/core/src/distribution.ts +24 -0
  92. package/packages/core/src/events/catalog.ts +201 -0
  93. package/packages/core/src/events/envelope.ts +59 -0
  94. package/packages/core/src/events/payloads.ts +618 -0
  95. package/packages/core/src/events/repository.ts +559 -0
  96. package/packages/core/src/events/sessions.ts +180 -0
  97. package/packages/core/src/harvest/schema.ts +147 -0
  98. package/packages/core/src/ids.ts +45 -0
  99. package/packages/core/src/integration/harness.ts +681 -0
  100. package/packages/core/src/kernel/auto-merge.ts +259 -0
  101. package/packages/core/src/kernel/converge.ts +117 -0
  102. package/packages/core/src/kernel/dispatch-settings.ts +60 -0
  103. package/packages/core/src/kernel/dispatch-status.ts +141 -0
  104. package/packages/core/src/kernel/engine.ts +1047 -0
  105. package/packages/core/src/kernel/harvest.ts +823 -0
  106. package/packages/core/src/kernel/origin.ts +69 -0
  107. package/packages/core/src/kernel/phases.ts +196 -0
  108. package/packages/core/src/kernel/plan-verify-selection.ts +105 -0
  109. package/packages/core/src/kernel/pr-attachments.ts +82 -0
  110. package/packages/core/src/kernel/reducer.ts +752 -0
  111. package/packages/core/src/kernel/stall.ts +155 -0
  112. package/packages/core/src/kernel/verify-applicability.ts +52 -0
  113. package/packages/core/src/markdown.ts +67 -0
  114. package/packages/core/src/ontology.ts +288 -0
  115. package/packages/core/src/open-map.ts +203 -0
  116. package/packages/core/src/operator/annotations.ts +187 -0
  117. package/packages/core/src/operator/client.ts +311 -0
  118. package/packages/core/src/operator/control.ts +117 -0
  119. package/packages/core/src/operator/index.ts +62 -0
  120. package/packages/core/src/operator/presentation.ts +22 -0
  121. package/packages/core/src/operator/protocol.ts +98 -0
  122. package/packages/core/src/operator/query.ts +201 -0
  123. package/packages/core/src/operator/registry.ts +1043 -0
  124. package/packages/core/src/operator/requests.ts +138 -0
  125. package/packages/core/src/operator/sandbox.ts +458 -0
  126. package/packages/core/src/operator/server.ts +616 -0
  127. package/packages/core/src/operator/sessions.ts +223 -0
  128. package/packages/core/src/operator/tickets.ts +177 -0
  129. package/packages/core/src/plugin-sdk/index.ts +174 -0
  130. package/packages/core/src/plugins/contract-entry.ts +74 -0
  131. package/packages/core/src/plugins/contract-env.ts +3 -0
  132. package/packages/core/src/plugins/load.ts +259 -0
  133. package/packages/core/src/plugins/manifest.ts +236 -0
  134. package/packages/core/src/plugins/registry.ts +274 -0
  135. package/packages/core/src/plugins/runtimes.ts +75 -0
  136. package/packages/core/src/ports/forge/contract.ts +258 -0
  137. package/packages/core/src/ports/forge/create.ts +74 -0
  138. package/packages/core/src/ports/forge/fake.ts +488 -0
  139. package/packages/core/src/ports/forge/github-pr-attachments.ts +351 -0
  140. package/packages/core/src/ports/forge/github-transport.ts +352 -0
  141. package/packages/core/src/ports/forge/github.ts +1162 -0
  142. package/packages/core/src/ports/forge/local-git.ts +568 -0
  143. package/packages/core/src/ports/runner/claude.ts +873 -0
  144. package/packages/core/src/ports/runner/cli-stream.ts +90 -0
  145. package/packages/core/src/ports/runner/codex.ts +873 -0
  146. package/packages/core/src/ports/runner/contract.ts +492 -0
  147. package/packages/core/src/ports/runner/fake.ts +213 -0
  148. package/packages/core/src/ports/runner/json-record.ts +2 -0
  149. package/packages/core/src/ports/runner/live-contract-fixture.ts +176 -0
  150. package/packages/core/src/ports/runner/one-shot.ts +29 -0
  151. package/packages/core/src/ports/runner/pi-bridge-core.ts +136 -0
  152. package/packages/core/src/ports/runner/pi-bridge.js +6 -0
  153. package/packages/core/src/ports/runner/pi-rpc.ts +454 -0
  154. package/packages/core/src/ports/runner/pi.ts +617 -0
  155. package/packages/core/src/ports/runner/production.ts +74 -0
  156. package/packages/core/src/ports/runner/provider-error.ts +121 -0
  157. package/packages/core/src/ports/runner/routing.ts +234 -0
  158. package/packages/core/src/ports/runner/runtime.ts +214 -0
  159. package/packages/core/src/ports/runner/session-env.ts +34 -0
  160. package/packages/core/src/ports/runner/stream-parts.ts +103 -0
  161. package/packages/core/src/ports/tickets/contract.ts +570 -0
  162. package/packages/core/src/ports/tickets/create.ts +135 -0
  163. package/packages/core/src/ports/tickets/fake.ts +215 -0
  164. package/packages/core/src/ports/tickets/file.ts +559 -0
  165. package/packages/core/src/ports/tickets/linear.ts +811 -0
  166. package/packages/core/src/ports/tickets/operations.ts +161 -0
  167. package/packages/core/src/ports/tickets/remote-index.ts +13 -0
  168. package/packages/core/src/ports/tickets/remote-protocol.ts +80 -0
  169. package/packages/core/src/ports/tickets/remote-server.ts +185 -0
  170. package/packages/core/src/ports/tickets/remote.ts +130 -0
  171. package/packages/core/src/ports/tickets/update.ts +51 -0
  172. package/packages/core/src/ports/types.ts +528 -0
  173. package/packages/core/src/ports/workspace/build-execution.ts +123 -0
  174. package/packages/core/src/ports/workspace/contract.ts +136 -0
  175. package/packages/core/src/ports/workspace/create.ts +132 -0
  176. package/packages/core/src/ports/workspace/distribution-archive.ts +317 -0
  177. package/packages/core/src/ports/workspace/fake.ts +432 -0
  178. package/packages/core/src/ports/workspace/git-worktree.ts +848 -0
  179. package/packages/core/src/ports/workspace/harvest-execution.ts +111 -0
  180. package/packages/core/src/ports/workspace/in-process-build-execution.ts +30 -0
  181. package/packages/core/src/ports/workspace/local-build-execution.ts +88 -0
  182. package/packages/core/src/ports/workspace/operator-sandbox.ts +142 -0
  183. package/packages/core/src/ports/workspace/phase-scratch.ts +156 -0
  184. package/packages/core/src/ports/workspace/vercel-sandbox.ts +2541 -0
  185. package/packages/core/src/processes/build-child-exit.ts +78 -0
  186. package/packages/core/src/processes/build-child.ts +119 -0
  187. package/packages/core/src/processes/build-execution-state.ts +95 -0
  188. package/packages/core/src/processes/build-parent-watch.ts +35 -0
  189. package/packages/core/src/processes/build-runner.ts +2614 -0
  190. package/packages/core/src/processes/dispatcher.ts +2825 -0
  191. package/packages/core/src/processes/execution-settlement.ts +171 -0
  192. package/packages/core/src/processes/harvest-child.ts +115 -0
  193. package/packages/core/src/processes/harvest-execution-state.ts +121 -0
  194. package/packages/core/src/processes/harvest-runner.ts +1656 -0
  195. package/packages/core/src/processes/harvest.ts +595 -0
  196. package/packages/core/src/processes/infrastructure-failure-budget.ts +146 -0
  197. package/packages/core/src/processes/phase-failure-budget.ts +52 -0
  198. package/packages/core/src/processes/process-group-reaper.ts +35 -0
  199. package/packages/core/src/processes/process-group.ts +65 -0
  200. package/packages/core/src/processes/publication-loss.ts +62 -0
  201. package/packages/core/src/processes/publication-state.ts +93 -0
  202. package/packages/core/src/processes/sandbox-state.ts +101 -0
  203. package/packages/core/src/registry.ts +24 -0
  204. package/packages/core/src/sessions/reducer.ts +185 -0
  205. package/packages/core/src/skills.ts +7 -0
  206. package/packages/core/src/spec-standard.ts +47 -0
  207. package/packages/core/src/store/adapter.ts +80 -0
  208. package/packages/core/src/store/build-scope.ts +272 -0
  209. package/packages/core/src/store/contract.ts +2581 -0
  210. package/packages/core/src/store/local/blobs.ts +38 -0
  211. package/packages/core/src/store/local/schema.ts +180 -0
  212. package/packages/core/src/store/local/store.ts +1713 -0
  213. package/packages/core/src/store/memory.ts +1138 -0
  214. package/packages/core/src/store/phase-session.ts +425 -0
  215. package/packages/core/src/store/remote/client.ts +780 -0
  216. package/packages/core/src/store/remote/index.ts +12 -0
  217. package/packages/core/src/store/remote/protocol.ts +348 -0
  218. package/packages/core/src/store/remote/server.ts +923 -0
  219. package/packages/core/src/store/remote/token.ts +137 -0
  220. package/packages/core/src/store/remote/version.ts +10 -0
  221. package/packages/core/src/store/retention.ts +91 -0
  222. package/packages/core/src/store/session-handle.ts +265 -0
  223. package/packages/core/src/store/streams/assemble.ts +79 -0
  224. package/packages/core/src/store/streams/lock.ts +42 -0
  225. package/packages/core/src/store/streams/session-writer.ts +306 -0
  226. package/packages/core/src/store/streams/types.ts +171 -0
  227. package/packages/core/src/store/streams/wait.ts +127 -0
  228. package/packages/core/src/store/subscribe.ts +105 -0
  229. package/packages/core/src/store/types.ts +466 -0
  230. package/packages/core/src/testing/fixed.ts +44 -0
  231. package/packages/core/src/testing/store-failures.ts +32 -0
  232. package/packages/core/src/zod-issues.ts +91 -0
  233. package/skills/code-review/SKILL.md +100 -0
  234. package/skills/finalize/SKILL.md +44 -0
  235. package/skills/guide/SKILL.md +1781 -0
  236. package/skills/guide/references/operator-api.md +164 -0
  237. package/skills/guide/references/plugin-authoring.md +354 -0
  238. package/skills/guide/references/remote-store-protocol.md +972 -0
  239. package/skills/guide/references/remote-ticket-protocol.md +79 -0
  240. package/skills/guide/references/setup.md +289 -0
  241. package/skills/guide/references/spec-standard.md +63 -0
  242. package/skills/guide/references/ticket-dependencies.md +59 -0
  243. package/skills/harvest/SKILL.md +66 -0
  244. package/skills/harvest-review/SKILL.md +63 -0
  245. package/skills/implement/SKILL.md +82 -0
  246. package/skills/plan/SKILL.md +123 -0
  247. package/skills/plan-review/SKILL.md +121 -0
  248. package/skills/reconcile/SKILL.md +65 -0
  249. package/skills/spec/SKILL.md +132 -0
  250. package/skills/tickets/SKILL.md +139 -0
  251. package/templates/autobuild.toml +36 -0
@@ -0,0 +1,2 @@
1
+ /** Minimal JSON-object shape shared by stream writer internals. */
2
+ export type JsonRecord = Record<string, unknown>
@@ -0,0 +1,176 @@
1
+ import { expect } from 'bun:test'
2
+ import { chmod, copyFile, mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import type { AgentRunner, AgentTurnResult, Transcript } from '../types'
6
+ import type { OneShotCompletion } from './one-shot'
7
+ import { AGENT_BIN_DIR } from './session-env'
8
+
9
+ interface LiveFixture {
10
+ cwd: string
11
+ evidencePath: string
12
+ cleanup: () => Promise<void>
13
+ }
14
+
15
+ interface ProbeEvidence {
16
+ phase: string
17
+ retained: string
18
+ collide: string
19
+ abPath: string
20
+ helpStatus: string
21
+ }
22
+
23
+ async function createLiveFixture(): Promise<LiveFixture> {
24
+ const cwd = await mkdtemp(join(tmpdir(), 'ab-runner-live-contract-'))
25
+ const skillDir = join(cwd, '.agents', 'skills', 'ab-runner-contract')
26
+ const claudeSkills = join(cwd, '.claude', 'skills')
27
+ await mkdir(skillDir, { recursive: true })
28
+ await mkdir(claudeSkills, { recursive: true })
29
+ const skillPath = join(skillDir, 'SKILL.md')
30
+ await writeFile(
31
+ skillPath,
32
+ `---\nname: ab-runner-contract\ndescription: Live AgentRunner contract probe.\ndisable-model-invocation: true\n---\n\n# Runner contract probe\n\nImmediately use the bash tool to execute \`./contract-probe\` exactly once.\nWait for it to finish, then reply with the single word \`probed\`.\n`,
33
+ )
34
+ const claudeSkill = join(claudeSkills, 'ab-runner-contract')
35
+ try {
36
+ await symlink(skillDir, claudeSkill, 'dir')
37
+ } catch {
38
+ await mkdir(claudeSkill, { recursive: true })
39
+ await copyFile(skillPath, join(claudeSkill, 'SKILL.md'))
40
+ }
41
+
42
+ const evidencePath = join(cwd, 'contract-evidence.tsv')
43
+ const probePath = join(cwd, 'contract-probe')
44
+ await writeFile(
45
+ probePath,
46
+ `#!/bin/sh\nset -eu\nab_path="$(command -v ab)"\nif ab --help >/dev/null 2>&1; then help=0; else help=$?; fi\nprintf '%s\\t%s\\t%s\\t%s\\t%s\\n' "\${AB_CONTRACT_PHASE:-}" "\${AB_CONTRACT_RETAINED:-}" "\${AB_CONTRACT_COLLIDE:-}" "$ab_path" "$help" >> ${JSON.stringify(evidencePath)}\n`,
47
+ )
48
+ await chmod(probePath, 0o755)
49
+ return {
50
+ cwd,
51
+ evidencePath,
52
+ cleanup: () => rm(cwd, { recursive: true, force: true }),
53
+ }
54
+ }
55
+
56
+ async function evidence(path: string): Promise<ProbeEvidence[]> {
57
+ const content = await readFile(path, 'utf8')
58
+ return content
59
+ .trim()
60
+ .split('\n')
61
+ .filter((line) => line !== '')
62
+ .map((line) => {
63
+ const [phase = '', retained = '', collide = '', abPath = '', helpStatus = ''] =
64
+ line.split('\t')
65
+ return { phase, retained, collide, abPath, helpStatus }
66
+ })
67
+ }
68
+
69
+ function expectCompleted(result: AgentTurnResult): void {
70
+ expect(result.kind).toBe('completed')
71
+ expect(Number.isInteger(result.usage.inputTokens)).toBe(true)
72
+ expect(Number.isInteger(result.usage.outputTokens)).toBe(true)
73
+ expect(result.usage.turns).toBeGreaterThan(0)
74
+ }
75
+
76
+ function expectTranscript(transcript: Transcript, runner: AgentRunner, model: string): void {
77
+ expect(transcript.content.trim()).not.toBe('')
78
+ expect(transcript.metadata.runner).toBe(runner.name)
79
+ expect(transcript.metadata.model).toBe(model)
80
+ expect(transcript.metadata.usage.turns).toBeGreaterThanOrEqual(2)
81
+ }
82
+
83
+ /** Successful real-provider smoke coverage. Deterministic failure taxonomy is
84
+ * deliberately covered by the full injected adapter contract instead. */
85
+ export async function runLiveAgentRunnerContract(
86
+ runner: AgentRunner & OneShotCompletion,
87
+ model: string,
88
+ ): Promise<void> {
89
+ const fixture = await createLiveFixture()
90
+ let failure: unknown
91
+ try {
92
+ const started = await runner.start({
93
+ skill: 'ab-runner-contract',
94
+ invocation: 'live-contract',
95
+ workspacePath: fixture.cwd,
96
+ model,
97
+ env: {
98
+ AB_CONTRACT_PHASE: 'start',
99
+ AB_CONTRACT_RETAINED: 'retained',
100
+ AB_CONTRACT_COLLIDE: 'start-value',
101
+ },
102
+ })
103
+ let transcript: Transcript | undefined
104
+ try {
105
+ expect(started.session.id.trim()).not.toBe('')
106
+ expect(started.session.runner).toBe(runner.name)
107
+ expect(started.session.model).toBe(model)
108
+ expectCompleted(started.result)
109
+ expect(await evidence(fixture.evidencePath)).toEqual([
110
+ {
111
+ phase: 'start',
112
+ retained: 'retained',
113
+ collide: 'start-value',
114
+ abPath: join(AGENT_BIN_DIR, 'ab'),
115
+ helpStatus: '0',
116
+ },
117
+ ])
118
+
119
+ const continued = await runner.continue(
120
+ started.session,
121
+ 'Use the bash tool to execute `./contract-probe` exactly once, wait for it, then reply with exactly the same single word as your previous response.',
122
+ {
123
+ env: {
124
+ AB_CONTRACT_PHASE: 'continue',
125
+ AB_CONTRACT_COLLIDE: 'continue-value',
126
+ },
127
+ },
128
+ )
129
+ expectCompleted(continued)
130
+ expect(continued.text.trim()).toBe('probed')
131
+ expect(await evidence(fixture.evidencePath)).toEqual([
132
+ {
133
+ phase: 'start',
134
+ retained: 'retained',
135
+ collide: 'start-value',
136
+ abPath: join(AGENT_BIN_DIR, 'ab'),
137
+ helpStatus: '0',
138
+ },
139
+ {
140
+ phase: 'continue',
141
+ retained: 'retained',
142
+ collide: 'continue-value',
143
+ abPath: join(AGENT_BIN_DIR, 'ab'),
144
+ helpStatus: '0',
145
+ },
146
+ ])
147
+ } catch (error) {
148
+ failure = error
149
+ }
150
+
151
+ try {
152
+ transcript = await runner.end(started.session)
153
+ } catch (endError) {
154
+ if (failure !== undefined) {
155
+ throw new AggregateError(
156
+ [failure, endError],
157
+ 'live AgentRunner assertion and session end both failed',
158
+ )
159
+ }
160
+ throw endError
161
+ }
162
+ if (failure !== undefined) throw failure
163
+ expectTranscript(transcript, runner, model)
164
+
165
+ const nonce = crypto.randomUUID()
166
+ const oneShot = await runner.complete({
167
+ prompt: `Reply with exactly this token and no other text: ${nonce}`,
168
+ cwd: fixture.cwd,
169
+ env: { AB_CONTRACT_ONE_SHOT: nonce },
170
+ model,
171
+ })
172
+ expect(oneShot.text.trim()).toContain(nonce)
173
+ } finally {
174
+ await fixture.cleanup()
175
+ }
176
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * A narrow, tool-free, non-phase judgment capability. Unlike AgentRunner,
3
+ * one-shot completions have no resumable session, skill invocation,
4
+ * transcript, or typed terminal: callers provide one prompt and consume text
5
+ * as an untrusted proposal behind deterministic validation.
6
+ */
7
+
8
+ export interface OneShotCompletionInput {
9
+ prompt: string
10
+ /** Working directory used for project context and runtime resolution. */
11
+ cwd: string
12
+ /** Runtime environment (credentials, provider settings, and so on). */
13
+ env: Record<string, string>
14
+ /** Absent means the runtime's own default model. */
15
+ model?: string
16
+ /** Extra CLI argv tokens for the selected runtime, in declared order. */
17
+ args?: readonly string[]
18
+ /** Caller-owned cancellation, normally a deterministic deadline. */
19
+ signal?: AbortSignal
20
+ }
21
+
22
+ export interface OneShotCompletionResult {
23
+ text: string
24
+ }
25
+
26
+ /** Optional capability carried by a runtime registration. */
27
+ export interface OneShotCompletion {
28
+ complete(input: OneShotCompletionInput): Promise<OneShotCompletionResult>
29
+ }
@@ -0,0 +1,136 @@
1
+ export interface BridgeToolSourceInfo {
2
+ origin?: string
3
+ source?: string
4
+ }
5
+
6
+ export interface BridgeTool {
7
+ name: string
8
+ sourceInfo?: BridgeToolSourceInfo
9
+ }
10
+
11
+ export interface BridgeApi {
12
+ getAllTools(): BridgeTool[]
13
+ setActiveTools(names: string[]): void
14
+ registerTool(tool: unknown): void
15
+ on(event: 'session_start', handler: (event: unknown, context: { cwd: string }) => void): void
16
+ registerCommand(
17
+ name: string,
18
+ command: {
19
+ description: string
20
+ handler: (args: string, context: BridgeCommandContext) => Promise<void>
21
+ },
22
+ ): void
23
+ }
24
+
25
+ export interface BridgeCommandContext {
26
+ modelRegistry: {
27
+ getAll(): Array<{ provider: string; id: string }>
28
+ getAvailable(): Array<{ provider: string; id: string }>
29
+ }
30
+ ui: { notify(message: string, type: 'info'): void }
31
+ }
32
+
33
+ export type BridgeBashToolFactory = (
34
+ cwd: string,
35
+ options: {
36
+ spawnHook: (input: { env?: Record<string, string>; [key: string]: unknown }) => {
37
+ env: Record<string, string>
38
+ [key: string]: unknown
39
+ }
40
+ },
41
+ ) => unknown
42
+
43
+ const BUILTIN_TOOL_NAMES = new Set(['read', 'bash', 'edit', 'write', 'grep', 'find', 'ls'])
44
+ const CATALOG_PREFIX = 'autobuild-pi-catalog:'
45
+
46
+ export function selectActiveToolNames(
47
+ tools: readonly BridgeTool[],
48
+ baseTools: readonly string[],
49
+ ): string[] {
50
+ const allowedBase = new Set(baseTools)
51
+ return [
52
+ ...new Set(
53
+ tools
54
+ .filter((tool) => {
55
+ if (BUILTIN_TOOL_NAMES.has(tool.name)) return allowedBase.has(tool.name)
56
+ // Ambient discovery is disabled by the adapter. Every remaining
57
+ // non-builtin tool came from an explicitly loaded extension. An
58
+ // empty builtin allowlist marks a tool-free one-shot invocation.
59
+ return baseTools.length > 0
60
+ })
61
+ .map((tool) => tool.name),
62
+ ),
63
+ ]
64
+ }
65
+
66
+ export function installAutobuildBridge(pi: BridgeApi, createBashTool: BridgeBashToolFactory): void {
67
+ let environment: Record<string, string> = {}
68
+ let baseTools: string[] = []
69
+
70
+ const activateTools = (): void => {
71
+ pi.setActiveTools(selectActiveToolNames(pi.getAllTools(), baseTools))
72
+ }
73
+
74
+ pi.on('session_start', (_event, context) => {
75
+ pi.registerTool(
76
+ createBashTool(context.cwd, {
77
+ spawnHook: (input) => ({
78
+ ...input,
79
+ env: { ...input.env, ...environment },
80
+ }),
81
+ }),
82
+ )
83
+ activateTools()
84
+ })
85
+
86
+ pi.registerCommand('autobuild-configure', {
87
+ description: 'Configure an Autobuild-managed headless session',
88
+ handler: async (args) => {
89
+ const payload = decode(args)
90
+ if (!isRecord(payload)) throw new Error('invalid Autobuild bridge configuration')
91
+ environment = stringRecord(payload.environment)
92
+ baseTools = stringArray(payload.tools)
93
+ activateTools()
94
+ },
95
+ })
96
+
97
+ pi.registerCommand('autobuild-models', {
98
+ description: 'Return the local Pi model catalog to Autobuild',
99
+ handler: async (args, context) => {
100
+ const payload = decode(args)
101
+ if (!isRecord(payload) || typeof payload.nonce !== 'string' || payload.nonce.length < 8) {
102
+ throw new Error('invalid Autobuild model-catalog nonce')
103
+ }
104
+ const models = payload.availableOnly
105
+ ? context.modelRegistry.getAvailable()
106
+ : context.modelRegistry.getAll()
107
+ const entries = models.map((model) => ({ provider: model.provider, id: model.id }))
108
+ context.ui.notify(`${CATALOG_PREFIX}${payload.nonce}:${encode(entries)}`, 'info')
109
+ },
110
+ })
111
+ }
112
+
113
+ function decode(value: string): unknown {
114
+ return JSON.parse(Buffer.from(value, 'base64url').toString('utf8'))
115
+ }
116
+
117
+ function encode(value: unknown): string {
118
+ return Buffer.from(JSON.stringify(value), 'utf8').toString('base64url')
119
+ }
120
+
121
+ function stringArray(value: unknown): string[] {
122
+ return Array.isArray(value) && value.every((entry) => typeof entry === 'string') ? value : []
123
+ }
124
+
125
+ function stringRecord(value: unknown): Record<string, string> {
126
+ if (!isRecord(value)) return {}
127
+ return Object.fromEntries(
128
+ Object.entries(value).filter(
129
+ (entry): entry is [string, string] => typeof entry[1] === 'string',
130
+ ),
131
+ )
132
+ }
133
+
134
+ function isRecord(value: unknown): value is Record<string, unknown> {
135
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
136
+ }
@@ -0,0 +1,6 @@
1
+ import { createBashTool } from '@earendil-works/pi-coding-agent'
2
+ import { installAutobuildBridge } from './pi-bridge-core.ts'
3
+
4
+ export default function autobuildBridge(pi) {
5
+ installAutobuildBridge(pi, createBashTool)
6
+ }