@meistrari/agent-core 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +24 -3
  2. package/bin/supervisor.ts +116 -0
  3. package/package.json +42 -3
  4. package/scripts/build-supervisor-executable.ts +32 -0
  5. package/src/agents/agent-error-serializer.ts +23 -0
  6. package/src/agents/agent-event-stream.ts +81 -0
  7. package/src/agents/agent-id.ts +17 -0
  8. package/src/agents/agent-operation.ts +11 -0
  9. package/src/agents/agent-provider.ts +37 -0
  10. package/src/agents/agent-run.ts +32 -0
  11. package/src/agents/agent-runtime-error.ts +161 -0
  12. package/src/agents/agent-session-events.ts +32 -0
  13. package/src/agents/agent-tool-runner.ts +83 -0
  14. package/src/agents/agent-tool.ts +111 -0
  15. package/src/agents/author-context.ts +36 -0
  16. package/src/agents/claude/claude-command-mapper.ts +234 -0
  17. package/src/agents/claude/claude-event-mapper.ts +736 -0
  18. package/src/agents/claude/claude-provider.ts +191 -0
  19. package/src/agents/claude/claude-run.ts +464 -0
  20. package/src/agents/claude/claude-tool-mapper.ts +186 -0
  21. package/src/agents/claude/index.ts +1 -0
  22. package/src/agents/codex/codex-auth.ts +34 -0
  23. package/src/agents/codex/codex-command-mapper.ts +78 -0
  24. package/src/agents/codex/codex-event-mapper.ts +708 -0
  25. package/src/agents/codex/codex-json-rpc-client.ts +326 -0
  26. package/src/agents/codex/codex-protocol.ts +36 -0
  27. package/src/agents/codex/codex-provider.ts +1050 -0
  28. package/src/agents/codex/codex-run.ts +404 -0
  29. package/src/agents/codex/codex-skill-catalog.ts +158 -0
  30. package/src/agents/codex/codex-skill-roots.ts +19 -0
  31. package/src/agents/codex/codex-tool-mapper.ts +55 -0
  32. package/src/agents/codex/codex.errors.ts +68 -0
  33. package/src/agents/codex/generated/meta.gen.ts +606 -0
  34. package/src/agents/codex/generated/namespaces.gen.ts +311 -0
  35. package/src/agents/codex/generated/schema.gen.ts +34883 -0
  36. package/src/agents/codex/index.ts +2 -0
  37. package/src/agents/index.ts +14 -0
  38. package/src/agents/input-attachment-preparation.ts +86 -0
  39. package/src/agents/input-attachment.errors.ts +16 -0
  40. package/src/agents/instructions.ts +8 -0
  41. package/src/agents/materialized-input-attachment.ts +26 -0
  42. package/src/agents/message-id.ts +23 -0
  43. package/src/agents/normalize.ts +8 -0
  44. package/src/agents/sandbox-environment.ts +1 -0
  45. package/src/agents/tools/ping.tool.ts +13 -0
  46. package/src/agents/user-input-request.ts +470 -0
  47. package/src/provenance.gen.ts +3 -3
  48. package/src/supervisor/agent-provider-factory.ts +189 -0
  49. package/src/supervisor/bootstrap-binder.ts +125 -0
  50. package/src/supervisor/config.ts +49 -0
  51. package/src/supervisor/control-authority-verifier.ts +135 -0
  52. package/src/supervisor/create-supervisor-runtime.ts +25 -0
  53. package/src/supervisor/errors.ts +24 -0
  54. package/src/supervisor/index.ts +34 -0
  55. package/src/supervisor/persistence/json.ts +21 -0
  56. package/src/supervisor/persistence/state-discovery.ts +56 -0
  57. package/src/supervisor/persistence/supervisor-store.ts +364 -0
  58. package/src/supervisor/ports/index.ts +109 -0
  59. package/src/supervisor/provider-factory.ts +37 -0
  60. package/src/supervisor/resident.ts +143 -0
  61. package/src/supervisor/rpc-client.ts +120 -0
  62. package/src/supervisor/runtime-handler.ts +309 -0
  63. package/src/supervisor/websocket-server.ts +434 -0
  64. package/src/supervisor-protocol/bootstrap.ts +1 -1
  65. package/src/template-onboarding.ts +47 -0
  66. package/src/testing/es256-test-keys.ts +73 -0
  67. package/src/testing/in-memory-runtime-control-plane.ts +205 -0
  68. package/src/testing/index.ts +6 -0
  69. package/src/testing/loopback-supervisor-connection.ts +71 -0
  70. package/src/testing/scripted-provider.ts +64 -0
  71. package/src/worker-runtime-client/command-pump.ts +132 -0
  72. package/src/worker-runtime-client/connection-attempt.ts +340 -0
  73. package/src/worker-runtime-client/control-authority-signer.ts +100 -0
  74. package/src/worker-runtime-client/e2b-supervisor-connection.ts +102 -0
  75. package/src/worker-runtime-client/frame-processor.ts +178 -0
  76. package/src/worker-runtime-client/index.ts +27 -0
  77. package/src/worker-runtime-client/lease-reconciler.ts +14 -0
  78. package/src/worker-runtime-client/ports.ts +137 -0
  79. package/src/worker-runtime-client/postgres-notification-listener.ts +91 -0
  80. package/src/worker-runtime-client/rpc-dispatcher.ts +27 -0
  81. package/src/worker-runtime-client/rpc-request-manager.ts +141 -0
  82. package/src/worker-runtime-client/sandbox-connection-runtime.ts +300 -0
  83. package/src/worker-runtime-client/token-crypto.ts +46 -0
@@ -0,0 +1,364 @@
1
+ import type { SessionBootstrapBody } from '../../supervisor-protocol/bootstrap'
2
+ import type { CommandEnvelope } from '../../supervisor-protocol/envelopes/control-plane-to-supervisor'
3
+ import type { DurableEventEnvelope } from '../../supervisor-protocol/envelopes/supervisor-to-control-plane'
4
+ import type { WireEventBody } from '../../supervisor-protocol/event-body'
5
+ import type { SupervisorAgentRunSnapshot } from '../../supervisor-protocol/supervisor-agent-run-snapshot'
6
+ import { mkdirSync } from 'node:fs'
7
+ import { dirname } from 'node:path'
8
+ import { Database } from 'bun:sqlite'
9
+ import { sessionBootstrapBodySchema } from '../../supervisor-protocol/bootstrap'
10
+ import { wireCommandBodySchema } from '../../supervisor-protocol/command-body'
11
+ import { wireEventBodySchema } from '../../supervisor-protocol/event-body'
12
+ import { supervisorAgentRunSnapshotSchema } from '../../supervisor-protocol/supervisor-agent-run-snapshot'
13
+ import { SupervisorPersistenceError } from '../errors'
14
+ import { canonicalJson, sha256CanonicalJson } from './json'
15
+
16
+ const schemaVersion = 1
17
+ const bootstrapId = 'runtime'
18
+
19
+ interface BootstrapRow {
20
+ session_id: string
21
+ body_json: string
22
+ body_hash: string
23
+ connection_generation: number
24
+ runtime_connection_attempt_id: string
25
+ }
26
+
27
+ interface CommandRow {
28
+ command_id: string
29
+ seq: number
30
+ body_json: string
31
+ body_hash: string
32
+ process_status: 'pending' | 'applied' | 'failed'
33
+ }
34
+
35
+ interface EventRow {
36
+ seq: number
37
+ occurred_at: string
38
+ body_json: string
39
+ body_hash: string
40
+ }
41
+
42
+ export interface PersistedSupervisorBinding {
43
+ body: SessionBootstrapBody
44
+ bodyHash: string
45
+ connectionGeneration: number
46
+ runtimeConnectionAttemptId: string
47
+ }
48
+
49
+ export class SupervisorStore {
50
+ private closed = false
51
+
52
+ private constructor(private readonly database: Database) {}
53
+
54
+ static open(input: { sqlitePath: string }): SupervisorStore {
55
+ let store: SupervisorStore | undefined
56
+ try {
57
+ mkdirSync(dirname(input.sqlitePath), { recursive: true })
58
+ store = new SupervisorStore(new Database(input.sqlitePath, { create: true }))
59
+ store.database.exec('PRAGMA journal_mode = WAL')
60
+ store.database.exec('PRAGMA foreign_keys = ON')
61
+ store.database.exec('PRAGMA busy_timeout = 5000')
62
+ store.database.exec('PRAGMA synchronous = FULL')
63
+ const version = store.userVersion()
64
+ if (version === 0)
65
+ store.initializeSchema()
66
+ else if (version !== schemaVersion)
67
+ throw new Error(`Unsupported supervisor SQLite schema version: ${version}.`)
68
+ return store
69
+ }
70
+ catch (cause) {
71
+ store?.close()
72
+ throw new SupervisorPersistenceError({ message: 'Failed to open supervisor SQLite state.', cause })
73
+ }
74
+ }
75
+
76
+ close(): void {
77
+ if (this.closed)
78
+ return
79
+ this.closed = true
80
+ this.database.close()
81
+ }
82
+
83
+ binding(): PersistedSupervisorBinding | undefined {
84
+ const row = this.bootstrapRow()
85
+ return row ? bindingFromRow(row) : undefined
86
+ }
87
+
88
+ bind(input: {
89
+ body: SessionBootstrapBody
90
+ connectionGeneration: number
91
+ runtimeConnectionAttemptId: string
92
+ }): PersistedSupervisorBinding {
93
+ const transaction = this.database.transaction(() => {
94
+ const existing = this.bootstrapRow()
95
+ if (!existing) {
96
+ const now = new Date().toISOString()
97
+ const bodyJson = canonicalJson(input.body)
98
+ const bodyHash = sha256CanonicalJson(input.body)
99
+ this.database.query(`
100
+ INSERT INTO bootstrap (
101
+ id, session_id, body_json, body_hash, connection_generation,
102
+ runtime_connection_attempt_id, created_at, updated_at
103
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
104
+ `).run(
105
+ bootstrapId,
106
+ input.body.sessionId,
107
+ bodyJson,
108
+ bodyHash,
109
+ input.connectionGeneration,
110
+ input.runtimeConnectionAttemptId,
111
+ now,
112
+ now,
113
+ )
114
+ this.insertAttempt(input)
115
+ this.insertCommand({
116
+ kind: 'command',
117
+ commandId: input.body.initialCommand.commandId,
118
+ commandSeq: input.body.initialCommand.commandSeq,
119
+ body: input.body.initialCommand.body,
120
+ })
121
+ return {
122
+ body: input.body,
123
+ bodyHash,
124
+ connectionGeneration: input.connectionGeneration,
125
+ runtimeConnectionAttemptId: input.runtimeConnectionAttemptId,
126
+ }
127
+ }
128
+ const binding = bindingFromRow(existing)
129
+ if (binding.body.sessionId !== input.body.sessionId)
130
+ throw new Error('Supervisor is already bound to another session.')
131
+ if (input.connectionGeneration < binding.connectionGeneration)
132
+ throw new Error('Supervisor connection generation is stale.')
133
+ this.insertAttempt(input)
134
+ this.database.query(`
135
+ UPDATE bootstrap
136
+ SET connection_generation = ?, runtime_connection_attempt_id = ?, updated_at = ?
137
+ WHERE id = ?
138
+ `).run(input.connectionGeneration, input.runtimeConnectionAttemptId, new Date().toISOString(), bootstrapId)
139
+ return {
140
+ ...binding,
141
+ connectionGeneration: input.connectionGeneration,
142
+ runtimeConnectionAttemptId: input.runtimeConnectionAttemptId,
143
+ }
144
+ })
145
+ try {
146
+ return transaction()
147
+ }
148
+ catch (cause) {
149
+ throw new SupervisorPersistenceError({ message: 'Failed to persist supervisor bootstrap.', cause })
150
+ }
151
+ }
152
+
153
+ acceptConnectionAttempt(input: {
154
+ connectionGeneration: number
155
+ runtimeConnectionAttemptId: string
156
+ }): PersistedSupervisorBinding {
157
+ const binding = this.binding()
158
+ if (!binding)
159
+ throw new SupervisorPersistenceError({ message: 'Supervisor is not bound.' })
160
+ return this.bind({
161
+ body: binding.body,
162
+ connectionGeneration: input.connectionGeneration,
163
+ runtimeConnectionAttemptId: input.runtimeConnectionAttemptId,
164
+ })
165
+ }
166
+
167
+ receiveCommand(envelope: CommandEnvelope): { status: 'received', duplicate: boolean } {
168
+ const body = wireCommandBodySchema.parse(envelope.body)
169
+ const existing = this.database.query<CommandRow, [number]>(`
170
+ SELECT command_id, seq, body_json, body_hash, process_status
171
+ FROM commands WHERE seq = ?
172
+ `).get(envelope.commandSeq)
173
+ if (existing) {
174
+ if (existing.command_id !== envelope.commandId
175
+ || existing.body_hash !== sha256CanonicalJson(body)
176
+ || existing.body_json !== canonicalJson(body)) {
177
+ throw new SupervisorPersistenceError({ message: 'Command sequence conflicts with durable inbox state.' })
178
+ }
179
+ return { status: 'received', duplicate: true }
180
+ }
181
+ const maximum = this.lastReceivedCommandSeq()
182
+ if (envelope.commandSeq !== maximum + 1)
183
+ throw new SupervisorPersistenceError({ message: 'Command sequence contains a gap.' })
184
+ try {
185
+ this.insertCommand({ ...envelope, body })
186
+ return { status: 'received', duplicate: false }
187
+ }
188
+ catch (cause) {
189
+ throw new SupervisorPersistenceError({ message: 'Failed to durably receive supervisor command.', cause })
190
+ }
191
+ }
192
+
193
+ pendingCommands(): Array<CommandEnvelope & { body: ReturnType<typeof wireCommandBodySchema.parse> }> {
194
+ return this.database.query<CommandRow, []>(`
195
+ SELECT command_id, seq, body_json, body_hash, process_status
196
+ FROM commands WHERE process_status = 'pending' ORDER BY seq
197
+ `).all().map(row => ({
198
+ kind: 'command',
199
+ commandId: row.command_id,
200
+ commandSeq: row.seq,
201
+ body: commandBodyFromRow(row),
202
+ }))
203
+ }
204
+
205
+ markCommand(input: { commandSeq: number, status: 'applied' | 'failed' }): void {
206
+ this.database.query(`UPDATE commands SET process_status = ? WHERE seq = ?`).run(input.status, input.commandSeq)
207
+ }
208
+
209
+ lastReceivedCommandSeq(): number {
210
+ return this.database.query<{ maximum: number }, []>('SELECT COALESCE(MAX(seq), 0) AS maximum FROM commands').get()?.maximum ?? 0
211
+ }
212
+
213
+ appendDurableEvent(input: { occurredAt: string, body: WireEventBody }): DurableEventEnvelope {
214
+ const body = wireEventBodySchema.parse(input.body)
215
+ const bodyJson = canonicalJson(body)
216
+ const result = this.database.query(`
217
+ INSERT INTO events (occurred_at, body_json, body_hash, created_at) VALUES (?, ?, ?, ?)
218
+ `).run(input.occurredAt, bodyJson, sha256CanonicalJson(body), new Date().toISOString())
219
+ return {
220
+ kind: 'event',
221
+ delivery_semantics: 'durable',
222
+ seq: Number(result.lastInsertRowid),
223
+ occurredAt: input.occurredAt,
224
+ body,
225
+ }
226
+ }
227
+
228
+ eventsAfter(highWaterMark: number): DurableEventEnvelope[] {
229
+ return this.database.query<EventRow, [number]>(`
230
+ SELECT seq, occurred_at, body_json, body_hash FROM events WHERE seq > ? ORDER BY seq
231
+ `).all(highWaterMark).map(row => ({
232
+ kind: 'event',
233
+ delivery_semantics: 'durable',
234
+ seq: row.seq,
235
+ occurredAt: row.occurred_at,
236
+ body: eventBodyFromRow(row),
237
+ }))
238
+ }
239
+
240
+ localEventMax(): number {
241
+ return this.database.query<{ maximum: number }, []>('SELECT COALESCE(MAX(seq), 0) AS maximum FROM events').get()?.maximum ?? 0
242
+ }
243
+
244
+ agentRunSnapshot(): SupervisorAgentRunSnapshot {
245
+ const row = this.database.query<{ value: string }, []>('SELECT value FROM metadata WHERE key = \'agent_run\'').get()
246
+ return row ? supervisorAgentRunSnapshotSchema.parse(JSON.parse(row.value)) : { status: 'not_attached' }
247
+ }
248
+
249
+ setAgentRunSnapshot(snapshot: SupervisorAgentRunSnapshot): void {
250
+ const value = canonicalJson(supervisorAgentRunSnapshotSchema.parse(snapshot))
251
+ this.database.query(`
252
+ INSERT INTO metadata (key, value) VALUES ('agent_run', ?)
253
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value
254
+ `).run(value)
255
+ }
256
+
257
+ private insertAttempt(input: { connectionGeneration: number, runtimeConnectionAttemptId: string }): void {
258
+ const replay = this.database.query<{ present: number }, [number, string]>(`
259
+ SELECT 1 AS present FROM connection_attempts
260
+ WHERE connection_generation = ? AND runtime_connection_attempt_id = ?
261
+ `).get(input.connectionGeneration, input.runtimeConnectionAttemptId)
262
+ if (replay)
263
+ throw new Error('Supervisor connection attempt was already admitted.')
264
+ this.database.query('DELETE FROM connection_attempts WHERE connection_generation < ?').run(input.connectionGeneration)
265
+ this.database.query(`
266
+ INSERT INTO connection_attempts (connection_generation, runtime_connection_attempt_id, accepted_at)
267
+ VALUES (?, ?, ?)
268
+ `).run(input.connectionGeneration, input.runtimeConnectionAttemptId, new Date().toISOString())
269
+ }
270
+
271
+ private insertCommand(envelope: CommandEnvelope): void {
272
+ const body = wireCommandBodySchema.parse(envelope.body)
273
+ this.database.query(`
274
+ INSERT INTO commands (seq, command_id, body_json, body_hash, process_status, received_at)
275
+ VALUES (?, ?, ?, ?, 'pending', ?)
276
+ `).run(
277
+ envelope.commandSeq,
278
+ envelope.commandId,
279
+ canonicalJson(body),
280
+ sha256CanonicalJson(body),
281
+ new Date().toISOString(),
282
+ )
283
+ }
284
+
285
+ private bootstrapRow(): BootstrapRow | undefined {
286
+ return this.database.query<BootstrapRow, []>(`
287
+ SELECT session_id, body_json, body_hash, connection_generation, runtime_connection_attempt_id
288
+ FROM bootstrap WHERE id = 'runtime'
289
+ `).get() ?? undefined
290
+ }
291
+
292
+ private userVersion(): number {
293
+ return this.database.query<{ user_version: number }, []>('PRAGMA user_version').get()?.user_version ?? 0
294
+ }
295
+
296
+ private initializeSchema(): void {
297
+ this.database.transaction(() => {
298
+ this.database.exec(`
299
+ CREATE TABLE bootstrap (
300
+ id TEXT PRIMARY KEY CHECK (id = 'runtime'),
301
+ session_id TEXT NOT NULL CHECK (session_id <> ''),
302
+ body_json TEXT NOT NULL,
303
+ body_hash TEXT NOT NULL CHECK (length(body_hash) = 64),
304
+ connection_generation INTEGER NOT NULL CHECK (connection_generation > 0),
305
+ runtime_connection_attempt_id TEXT NOT NULL CHECK (runtime_connection_attempt_id <> ''),
306
+ created_at TEXT NOT NULL,
307
+ updated_at TEXT NOT NULL
308
+ );
309
+ CREATE TABLE connection_attempts (
310
+ connection_generation INTEGER NOT NULL CHECK (connection_generation > 0),
311
+ runtime_connection_attempt_id TEXT NOT NULL CHECK (runtime_connection_attempt_id <> ''),
312
+ accepted_at TEXT NOT NULL,
313
+ PRIMARY KEY (connection_generation, runtime_connection_attempt_id)
314
+ );
315
+ CREATE TABLE commands (
316
+ seq INTEGER PRIMARY KEY CHECK (seq > 0),
317
+ command_id TEXT NOT NULL UNIQUE CHECK (command_id <> ''),
318
+ body_json TEXT NOT NULL,
319
+ body_hash TEXT NOT NULL CHECK (length(body_hash) = 64),
320
+ process_status TEXT NOT NULL CHECK (process_status IN ('pending', 'applied', 'failed')),
321
+ received_at TEXT NOT NULL
322
+ );
323
+ CREATE TABLE events (
324
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
325
+ occurred_at TEXT NOT NULL,
326
+ body_json TEXT NOT NULL,
327
+ body_hash TEXT NOT NULL CHECK (length(body_hash) = 64),
328
+ created_at TEXT NOT NULL
329
+ );
330
+ CREATE TABLE metadata (
331
+ key TEXT PRIMARY KEY CHECK (key <> ''),
332
+ value TEXT NOT NULL
333
+ );
334
+ `)
335
+ this.database.exec(`PRAGMA user_version = ${schemaVersion}`)
336
+ })()
337
+ }
338
+ }
339
+
340
+ function commandBodyFromRow(row: CommandRow) {
341
+ const body = wireCommandBodySchema.parse(JSON.parse(row.body_json))
342
+ if (canonicalJson(body) !== row.body_json || sha256CanonicalJson(body) !== row.body_hash)
343
+ throw new SupervisorPersistenceError({ message: `Supervisor command ${row.seq} failed canonical integrity validation.` })
344
+ return body
345
+ }
346
+
347
+ function eventBodyFromRow(row: EventRow) {
348
+ const body = wireEventBodySchema.parse(JSON.parse(row.body_json))
349
+ if (canonicalJson(body) !== row.body_json || sha256CanonicalJson(body) !== row.body_hash)
350
+ throw new SupervisorPersistenceError({ message: `Supervisor event ${row.seq} failed canonical integrity validation.` })
351
+ return body
352
+ }
353
+
354
+ function bindingFromRow(row: BootstrapRow): PersistedSupervisorBinding {
355
+ const body = sessionBootstrapBodySchema.parse(JSON.parse(row.body_json))
356
+ if (canonicalJson(body) !== row.body_json || sha256CanonicalJson(body) !== row.body_hash)
357
+ throw new SupervisorPersistenceError({ message: 'Supervisor bootstrap body failed canonical integrity validation.' })
358
+ return {
359
+ body,
360
+ bodyHash: row.body_hash,
361
+ connectionGeneration: row.connection_generation,
362
+ runtimeConnectionAttemptId: row.runtime_connection_attempt_id,
363
+ }
364
+ }
@@ -0,0 +1,109 @@
1
+ import type { AgentInputBlock, AgentProviderId } from '../../protocol'
2
+ import type { EphemeralCredentials, SessionBootstrapBody } from '../../supervisor-protocol/bootstrap'
3
+ import type { WireSendPromptCommand } from '../../supervisor-protocol/command-body'
4
+ import type { ProductEvent } from '../../supervisor-protocol/product-event'
5
+ import type { SupervisorRpcClient } from '../rpc-client'
6
+
7
+ export interface ProviderCredentialsSource {
8
+ initial: () => EphemeralCredentials | undefined
9
+ refresh: (input: { reason: 'startup' | 'expired' | 'rejected', signal?: AbortSignal }) => Promise<EphemeralCredentials>
10
+ environmentFor: (provider: AgentProviderId) => Record<string, string>
11
+ }
12
+
13
+ export interface SupervisorAgentTool {
14
+ name: string
15
+ }
16
+
17
+ export type ToolRegistryFactory = (provider: AgentProviderId) => readonly SupervisorAgentTool[]
18
+
19
+ export type InstructionComposer = (input: {
20
+ provider: AgentProviderId
21
+ tools: readonly { name: string }[]
22
+ }) => string
23
+
24
+ export interface InitializedWorkspaceRepository {
25
+ repository: { id: string, fullName: string }
26
+ root: string
27
+ branch: string
28
+ sha: string
29
+ }
30
+
31
+ export interface MaterializedAgentInputAttachment {
32
+ attachmentId: string
33
+ ordinal: number
34
+ filename: string
35
+ mediaType: string
36
+ byteSize: number
37
+ sha256: string
38
+ localPath: string
39
+ }
40
+
41
+ export interface WorkspacePreparer {
42
+ initialize: (input: {
43
+ signal: AbortSignal
44
+ onStartupStage: (input: { stage: string, outcome: 'success' | 'failure', duration: number }) => void
45
+ }) => Promise<readonly InitializedWorkspaceRepository[]>
46
+ prepareTurn: (input: {
47
+ commandId: string
48
+ command: WireSendPromptCommand
49
+ signal: AbortSignal
50
+ }) => Promise<{
51
+ attachments: readonly MaterializedAgentInputAttachment[]
52
+ promptPrefixBlocks?: AgentInputBlock[]
53
+ }>
54
+ }
55
+
56
+ export interface WorkspaceSyncContext {
57
+ trigger: 'turn-ended' | 'boot-reconcile'
58
+ turnId: string
59
+ author: { name: string, email: string } | undefined
60
+ additionalUsers: Array<{ id: string, name: string, gitEmail: string }>
61
+ assistantSummary: string | undefined
62
+ commitAllowed: boolean
63
+ }
64
+
65
+ export type RepositorySyncAction
66
+ = | { kind: 'commit-completed', repository: { id: string, fullName: string }, branch: string, commitSha: string }
67
+ | { kind: 'commit-failed', repository: string, branch: string, reason: 'unknown', detail: string }
68
+ | { kind: 'push-completed', repository: { id: string, fullName: string }, branch: string, remoteHeadSha: string }
69
+ | {
70
+ kind: 'push-failed'
71
+ repository: string
72
+ branch: string
73
+ reason: 'non-fast-forward' | 'auth' | 'network' | 'rejected' | 'unknown'
74
+ detail: string
75
+ }
76
+
77
+ export interface WorkspaceSync {
78
+ sync: (context: WorkspaceSyncContext) => Promise<RepositorySyncAction[]>
79
+ fetch: () => Promise<void>
80
+ }
81
+
82
+ export interface TurnHooks {
83
+ onTurnEnded: (input: {
84
+ turnId: string
85
+ status: 'completed' | 'failed' | 'interrupted' | 'cancelled'
86
+ cwd: string
87
+ signal: AbortSignal
88
+ }) => Promise<ProductEvent[]>
89
+ }
90
+
91
+ export interface LargePayloadStore {
92
+ put: (input: {
93
+ sessionId: string
94
+ eventType: string
95
+ bytes: Uint8Array
96
+ signal: AbortSignal
97
+ }) => Promise<{ ref: string }>
98
+ }
99
+
100
+ export interface SupervisorExtensionContext {
101
+ bootstrap: SessionBootstrapBody
102
+ rpc: SupervisorRpcClient
103
+ signal: AbortSignal
104
+ }
105
+
106
+ export interface SupervisorBroker {
107
+ start: () => void | Promise<void>
108
+ stop: () => void | Promise<void>
109
+ }
@@ -0,0 +1,37 @@
1
+ import type { EphemeralCredentials, SessionBootstrapBody, SessionBootstrapGitToken } from '../supervisor-protocol/bootstrap'
2
+ import type { WireCommandBody } from '../supervisor-protocol/command-body'
3
+ import type { WireEventBody } from '../supervisor-protocol/event-body'
4
+ import type { SupervisorAgentRunSnapshot } from '../supervisor-protocol/supervisor-agent-run-snapshot'
5
+ import type { SupervisorRpcClient } from './rpc-client'
6
+
7
+ export interface SupervisorAgentRuntime {
8
+ snapshot: () => SupervisorAgentRunSnapshot
9
+ handle: (input: {
10
+ commandId: string
11
+ commandSeq: number
12
+ body: WireCommandBody
13
+ signal: AbortSignal
14
+ emit: (body: WireEventBody) => void
15
+ }) => Promise<void>
16
+ refreshSecrets?: (input: {
17
+ credentials?: EphemeralCredentials
18
+ gitToken?: SessionBootstrapGitToken
19
+ }) => void | Promise<void>
20
+ close: (input: { signal: AbortSignal }) => Promise<void>
21
+ }
22
+
23
+ export interface SupervisorProviderFactory {
24
+ create: (input: {
25
+ bootstrap: SessionBootstrapBody
26
+ credentials?: EphemeralCredentials
27
+ gitToken?: SessionBootstrapGitToken
28
+ rpc: SupervisorRpcClient
29
+ signal: AbortSignal
30
+ }) => Promise<SupervisorAgentRuntime>
31
+ }
32
+
33
+ export function createSupervisorProviderFactory(
34
+ create: SupervisorProviderFactory['create'],
35
+ ): SupervisorProviderFactory {
36
+ return { create }
37
+ }
@@ -0,0 +1,143 @@
1
+ import type { Logger } from '../logger'
2
+ import type { SessionBootstrapEnvelope } from '../supervisor-protocol/envelopes/control-plane-to-supervisor'
3
+ import type { BootstrapAdmission, SupervisorProcessBinding } from './bootstrap-binder'
4
+ import type { ResidentSupervisorConfig } from './config'
5
+ import type { VerifySupervisorControlAuthority } from './control-authority-verifier'
6
+ import type { SupervisorStore } from './persistence/supervisor-store'
7
+ import type { SupervisorProviderFactory } from './provider-factory'
8
+ import { agentCoreProvenance } from '../provenance'
9
+ import { createBootstrapBinder } from './bootstrap-binder'
10
+ import { CLEAN_EXIT_CODE, RECOVERABLE_ERROR_CODE, TERMINAL_ERROR_CODE } from './config'
11
+ import { createSupervisorControlAuthorityVerifier } from './control-authority-verifier'
12
+ import { SupervisorConfigurationError } from './errors'
13
+ import { discoverPersistedSupervisorState } from './persistence/state-discovery'
14
+ import { SupervisorRuntimeHandler } from './runtime-handler'
15
+ import { ResidentSupervisorWebSocketServer } from './websocket-server'
16
+
17
+ export interface ResidentSupervisor {
18
+ start: () => void
19
+ stop: (input?: { reason?: 'sigterm' | 'sigint' | 'fatal_error', exitCode?: number }) => Promise<void>
20
+ port: () => number | undefined
21
+ binding: () => 'unbound' | 'bound'
22
+ }
23
+
24
+ export async function createResidentSupervisor(input: {
25
+ config: ResidentSupervisorConfig
26
+ logger: Logger
27
+ providerFactory: SupervisorProviderFactory
28
+ profile: { name: string, version: number, requiredRpcMethods: readonly string[] }
29
+ admission?: BootstrapAdmission
30
+ verifyControlAuthority?: VerifySupervisorControlAuthority
31
+ onExitRequested?: (code: number) => void
32
+ onStartupStage?: (fact: {
33
+ event: 'sandbox.startup-stage'
34
+ stage: 'bootstrap-persisted' | 'bootstrap-readiness-sent'
35
+ outcome: 'success' | 'failure'
36
+ duration: number
37
+ sessionId: string
38
+ }) => void
39
+ }): Promise<ResidentSupervisor> {
40
+ const recovered = await discoverPersistedSupervisorState({ stateRoot: input.config.stateRoot })
41
+ let binding: SupervisorProcessBinding = recovered
42
+ ? { status: 'bound', store: recovered.store, sessionId: recovered.sessionId }
43
+ : { status: 'unbound' }
44
+ let handler: SupervisorRuntimeHandler | undefined
45
+ let processStartedPersisted = false
46
+ let stopped = false
47
+ let exitCode = CLEAN_EXIT_CODE
48
+
49
+ const verifyControlAuthority = input.verifyControlAuthority
50
+ ?? await createSupervisorControlAuthorityVerifier({ jwksPath: input.config.authorityJwksPath })
51
+ if (recovered) {
52
+ recovered.store.appendDurableEvent({
53
+ occurredAt: new Date().toISOString(),
54
+ body: { type: 'supervisor.started', payload: {} },
55
+ })
56
+ processStartedPersisted = true
57
+ }
58
+ const requestShutdown = (code: number) => {
59
+ exitCode = code
60
+ input.onExitRequested?.(code)
61
+ void stop({ reason: 'fatal_error', exitCode: code })
62
+ }
63
+ const activate = async (activation: { store: SupervisorStore, envelope: SessionBootstrapEnvelope }) => {
64
+ if (!processStartedPersisted) {
65
+ activation.store.appendDurableEvent({
66
+ occurredAt: new Date().toISOString(),
67
+ body: { type: 'supervisor.started', payload: {} },
68
+ })
69
+ processStartedPersisted = true
70
+ }
71
+ if (!handler) {
72
+ handler = new SupervisorRuntimeHandler({
73
+ store: activation.store,
74
+ providerFactory: input.providerFactory,
75
+ credentials: activation.envelope.credentials,
76
+ gitToken: activation.envelope.gitToken,
77
+ logger: input.logger,
78
+ onFatal: error => requestShutdown(
79
+ error instanceof SupervisorConfigurationError ? TERMINAL_ERROR_CODE : RECOVERABLE_ERROR_CODE,
80
+ ),
81
+ })
82
+ }
83
+ else {
84
+ await handler.refreshSecrets({
85
+ credentials: activation.envelope.credentials,
86
+ gitToken: activation.envelope.gitToken,
87
+ })
88
+ }
89
+ return handler
90
+ }
91
+ const bindBootstrap = createBootstrapBinder({
92
+ stateRoot: input.config.stateRoot,
93
+ getBinding: () => binding,
94
+ setBinding: value => binding = value,
95
+ admission: input.admission,
96
+ activate,
97
+ shutdown: requestShutdown,
98
+ logger: input.logger,
99
+ })
100
+ const listener = new ResidentSupervisorWebSocketServer({
101
+ config: input.config,
102
+ logger: input.logger,
103
+ health: () => ({
104
+ status: 'ok',
105
+ binding: binding.status,
106
+ profile: input.profile,
107
+ agentCore: agentCoreProvenance(),
108
+ }),
109
+ getBoundSessionId: () => binding.status === 'bound' ? binding.sessionId : undefined,
110
+ getLocalEventMax: () => binding.status === 'bound' ? binding.store.localEventMax() : 0,
111
+ verifyControlAuthority,
112
+ acceptBoundConnectionGeneration: (connection) => {
113
+ if (binding.status !== 'bound')
114
+ throw new Error('Supervisor is not bound.')
115
+ binding.store.acceptConnectionAttempt({
116
+ connectionGeneration: connection.generation,
117
+ runtimeConnectionAttemptId: connection.runtimeConnectionAttemptId,
118
+ })
119
+ },
120
+ bindBootstrap,
121
+ onStartupStage: input.onStartupStage ?? (() => undefined),
122
+ })
123
+
124
+ async function stop(options: { reason?: 'sigterm' | 'sigint' | 'fatal_error', exitCode?: number } = {}): Promise<void> {
125
+ if (stopped)
126
+ return
127
+ stopped = true
128
+ exitCode = options.exitCode ?? exitCode
129
+ await listener.stop()
130
+ await handler?.stop(options.reason)
131
+ if (binding.status === 'bound')
132
+ binding.store.close()
133
+ if (exitCode !== CLEAN_EXIT_CODE)
134
+ process.exitCode = exitCode
135
+ }
136
+
137
+ return {
138
+ start: () => listener.start(),
139
+ stop,
140
+ port: () => listener.port(),
141
+ binding: () => binding.status,
142
+ }
143
+ }