-
Notifications
You must be signed in to change notification settings - Fork 0
D4: Add bounded production agent adapters #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
09f347d
Add bounded production agent adapters
mchwang 8e9f77a
Harden adapter capture settlement
mchwang 44d1304
Pin adapter output to dedicated tmpfs
mchwang cb6f3fb
Close remaining adapter lifecycle races
mchwang 0453c5d
Validate pinned output identities
mchwang 485521d
Make cleanup and decode settlement retryable
mchwang a4322e8
Abort and await bounded adapter capture
mchwang 963ca16
Retain adapter cleanup ownership
mchwang ef40f26
Allow loaded Docker cleanup observation
mchwang 97ca736
Secure deferred output acknowledgement
mchwang 1fed991
Retain colliding cleanup recovery
mchwang ae99db9
Preserve adapter setup ownership
mchwang ee57917
Bound cancellation with monotonic deadlines
mchwang fe8c11f
Carry invocation ownership through settlement
mchwang 82dbc83
Guard active profile and close deadline
mchwang bee7c64
Apply adapter timeout across setup
mchwang 0988f12
Bound adapter cleanup and final stderr
mchwang 1f7cf36
Keep decoder settlement timers alive
mchwang 4c1dc2e
Retain recovery profile ownership
mchwang 8b963a4
Revalidate container at launch boundary
mchwang bf65dec
Validate profile capability before cleanup
mchwang 27059e3
Preserve cleanup cancellation reasons
mchwang a5dfaba
Update cleanup cancellation regression
mchwang 10c8d52
Authenticate profiles at disposal boundary
mchwang d9539fb
Skip cleanup for unauthenticated profiles and stop on late close
mchwang cd1fc31
Reject duplicate attempts without touching the active container
mchwang 691df93
Keep D4 Docker suites out of the parallel CI run
mchwang dd0af27
Retain captured output in fixed-size blocks
mchwang 8bc00da
Keep killed creates unsettled across cleanups and publish strict UTF-8
mchwang 6c757e1
Treat an absent container as settled once the create window passes
mchwang a7a6eb9
Fail closed on a trailing incomplete UTF-8 character unless truncated
mchwang d309dd7
Release only the profile when rejecting an invocation before creation
mchwang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import type { InvocationHandle } from '../contract.ts'; | ||
| import { createContainerProfile, ProfileCreationCleanupError } from '../container/profile.ts'; | ||
| import { createVendorNetwork, removeVendorNetwork, VendorNetworkCreationCleanupError, | ||
| type VendorNetwork } from '../network/network.ts'; | ||
| import { createClaudeCommand, createPhasePolicy } from '../policy.ts'; | ||
| import { retainNetworkCleanup, retainSetupCleanup, startProfileInvocation } from './supervisor.ts'; | ||
| import { createAdapterInvocationBudget, type AgentAdapterOptions, type AgentAdapterRequest } from './types.ts'; | ||
|
|
||
| export function parseClaudeOutput(raw: Buffer): { text: string; providerFailed: boolean } { | ||
| const envelope = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(raw)) as | ||
| { result?: unknown; is_error?: unknown }; | ||
| if (typeof envelope.result !== 'string' || typeof envelope.is_error !== 'boolean') | ||
| throw new Error('Claude returned a malformed output envelope.'); | ||
| return Object.freeze({ text: envelope.result, providerFailed: envelope.is_error }); | ||
| } | ||
|
|
||
| export function startClaudeInvocation(request: AgentAdapterRequest, | ||
| oauthToken: string, options: AgentAdapterOptions = {}): InvocationHandle { | ||
| if (!oauthToken || oauthToken.includes('\0')) throw new Error('Claude OAuth token is malformed.'); | ||
| const policy = createPhasePolicy(request.invocation); | ||
| const remaining = createAdapterInvocationBudget(request.invocation, options.timeoutMs); | ||
| let network: VendorNetwork; | ||
| try { network = createVendorNetwork(request.invocation, request.imageId, Math.min(60_000, remaining())); } | ||
| catch (error) { | ||
| if (error instanceof VendorNetworkCreationCleanupError) | ||
| return retainSetupCleanup(request.invocation, error.retryCleanup, error.startupError, error, | ||
| 'network creation cleanup'); | ||
| throw error; | ||
| } | ||
| try { | ||
| const profile = createContainerProfile({ ...request, policy, network, | ||
| command: createClaudeCommand(policy, request.prompt), claudeToken: oauthToken, | ||
| timeoutMs: Math.min(60_000, remaining()) }); | ||
| return startProfileInvocation(profile, { ...options, secrets: { CLAUDE_CODE_OAUTH_TOKEN: oauthToken }, | ||
| invocationBudget: remaining, | ||
| decode: (_profile, raw) => parseClaudeOutput(raw) }); | ||
| } catch (error) { | ||
| if (error instanceof ProfileCreationCleanupError) { | ||
| const retryCleanup = (networkTimeoutMs = 30_000) => { | ||
| const failures: unknown[] = []; | ||
| try { error.retryCleanup(); } catch (cleanupError) { failures.push(cleanupError); } | ||
| try { removeVendorNetwork(network, networkTimeoutMs); } catch (cleanupError) { failures.push(cleanupError); } | ||
| if (failures.length) throw new AggregateError(failures, 'Adapter setup cleanup did not settle.'); | ||
| }; | ||
| try { retryCleanup(Math.min(30_000, remaining())); } | ||
| catch (cleanupError) { return retainSetupCleanup(request.invocation, () => retryCleanup(), | ||
| error.startupError, cleanupError, 'profile and network cleanup'); } | ||
| throw error.startupError; | ||
| } | ||
| try { removeVendorNetwork(network, Math.min(30_000, remaining())); } | ||
| catch (cleanupError) { return retainNetworkCleanup(request.invocation, network, error, cleanupError); } | ||
| throw error; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import type { InvocationHandle } from '../contract.ts'; | ||
| import { createContainerProfile, ProfileCreationCleanupError } from '../container/profile.ts'; | ||
| import { createVendorNetwork, removeVendorNetwork, VendorNetworkCreationCleanupError, | ||
| type VendorNetwork } from '../network/network.ts'; | ||
| import { createCodexCommand, createPhasePolicy } from '../policy.ts'; | ||
| import { readBoundedContainerFile, retainNetworkCleanup, retainSetupCleanup, | ||
| startProfileInvocation } from './supervisor.ts'; | ||
| import { createAdapterInvocationBudget, type AgentAdapterOptions, type AgentAdapterRequest } from './types.ts'; | ||
|
|
||
| export const CODEX_OUTPUT_FILE = '/run/codeboost-output/final.txt'; | ||
|
|
||
| export async function readCodexOutput(container: string, maximumBytes: number, timeoutMs = 30_000, | ||
| signal?: AbortSignal) { | ||
| const output = await readBoundedContainerFile(container, CODEX_OUTPUT_FILE, maximumBytes, timeoutMs, signal); | ||
| const text = new TextDecoder('utf-8', { fatal: true }).decode(output); | ||
| return Object.freeze({ text, additionalBytes: output.length }); | ||
| } | ||
|
|
||
| export function startCodexInvocation(request: AgentAdapterRequest, | ||
| authFile: string, options: AgentAdapterOptions = {}): InvocationHandle { | ||
| if (!authFile || authFile.includes('\0')) throw new Error('Codex auth path is malformed.'); | ||
| const policy = createPhasePolicy(request.invocation); | ||
| const remaining = createAdapterInvocationBudget(request.invocation, options.timeoutMs); | ||
| let network: VendorNetwork; | ||
| try { network = createVendorNetwork(request.invocation, request.imageId, Math.min(60_000, remaining())); } | ||
| catch (error) { | ||
| if (error instanceof VendorNetworkCreationCleanupError) | ||
| return retainSetupCleanup(request.invocation, error.retryCleanup, error.startupError, error, | ||
| 'network creation cleanup'); | ||
| throw error; | ||
| } | ||
| try { | ||
| const profile = createContainerProfile({ ...request, policy, network, | ||
| command: createCodexCommand(policy, request.prompt), codexAuthFile: authFile, deferredOutput: true, | ||
| timeoutMs: Math.min(60_000, remaining()) }); | ||
| return startProfileInvocation(profile, { ...options, | ||
| invocationBudget: remaining, | ||
| decode: (current, _raw, maximum, timeoutMs, signal) => | ||
| readCodexOutput(current.name, maximum, timeoutMs, signal) }); | ||
| } catch (error) { | ||
| if (error instanceof ProfileCreationCleanupError) { | ||
| const retryCleanup = (networkTimeoutMs = 30_000) => { | ||
| const failures: unknown[] = []; | ||
| try { error.retryCleanup(); } catch (cleanupError) { failures.push(cleanupError); } | ||
| try { removeVendorNetwork(network, networkTimeoutMs); } catch (cleanupError) { failures.push(cleanupError); } | ||
| if (failures.length) throw new AggregateError(failures, 'Adapter setup cleanup did not settle.'); | ||
| }; | ||
| try { retryCleanup(Math.min(30_000, remaining())); } | ||
| catch (cleanupError) { return retainSetupCleanup(request.invocation, () => retryCleanup(), | ||
| error.startupError, cleanupError, 'profile and network cleanup'); } | ||
| throw error.startupError; | ||
| } | ||
| try { removeVendorNetwork(network, Math.min(30_000, remaining())); } | ||
| catch (cleanupError) { return retainNetworkCleanup(request.invocation, network, error, cleanupError); } | ||
| throw error; | ||
|
mchwang marked this conversation as resolved.
|
||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.