@tangle-network/agent-runtime 0.259.1 → 0.261.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/dist/{activation-Cus2oZaT.d.ts → activation-BpJ2CmXx.d.ts} +2 -2
- package/dist/{activation-Btw2npV2.js → activation-BwrMmguq.js} +3 -3
- package/dist/{activation-Btw2npV2.js.map → activation-BwrMmguq.js.map} +1 -1
- package/dist/agent.d.ts +2 -2
- package/dist/agent.js +2 -2
- package/dist/{bundle-DXrBPEFw.js → bundle-CvLGRVO-.js} +2 -2
- package/dist/{bundle-DXrBPEFw.js.map → bundle-CvLGRVO-.js.map} +1 -1
- package/dist/candidate-execution/index.js +5 -4
- package/dist/candidate-execution/index.js.map +1 -1
- package/dist/{coordination-driver-BmtrkNPu.js → coordination-driver-LmDxHvAO.js} +77 -11
- package/dist/coordination-driver-LmDxHvAO.js.map +1 -0
- package/dist/{delegate-DJgmZZKH.js → delegate-rkhJf5w7.js} +2 -2
- package/dist/{delegate-DJgmZZKH.js.map → delegate-rkhJf5w7.js.map} +1 -1
- package/dist/durable.d.ts +285 -57
- package/dist/durable.js +637 -108
- package/dist/durable.js.map +1 -1
- package/dist/{graph-BKRvWzHx.js → graph-Bn3wSH8-.js} +4 -3
- package/dist/graph-Bn3wSH8-.js.map +1 -0
- package/dist/{improvement-cycle-D0Os0Hg_.js → improvement-cycle-DYiwfjWY.js} +8 -7
- package/dist/improvement-cycle-DYiwfjWY.js.map +1 -0
- package/dist/{index-sGOO254Q.d.ts → index-tubViIQJ.d.ts} +122 -12
- package/dist/index.d.ts +6 -6
- package/dist/index.js +9 -9
- package/dist/intelligence.d.ts +3 -3
- package/dist/intelligence.js +7 -6
- package/dist/intelligence.js.map +1 -1
- package/dist/kernel.d.ts +4 -4
- package/dist/kernel.js +8 -8
- package/dist/{loop-runner-bin-DAU4rNIL.d.ts → loop-runner-bin-CL4W2pu1.d.ts} +3 -3
- package/dist/{loop-runner-bin-pRlDgm1i.js → loop-runner-bin-Dxqxf91b.js} +3 -3
- package/dist/{loop-runner-bin-pRlDgm1i.js.map → loop-runner-bin-Dxqxf91b.js.map} +1 -1
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +3 -3
- package/dist/mcp/index.d.ts +5 -5
- package/dist/mcp/index.js +4 -4
- package/dist/{openai-tools-BsqD1_Zr.d.ts → openai-tools-BlZHd0Nz.d.ts} +2 -2
- package/dist/{prepare-RuaAclli.js → prepare-oHUQdC5-.js} +22 -452
- package/dist/prepare-oHUQdC5-.js.map +1 -0
- package/dist/profile-D3eXNBQV.js +438 -0
- package/dist/profile-D3eXNBQV.js.map +1 -0
- package/dist/profiles.d.ts +2 -2
- package/dist/{protected-model-port-BFhSgMAy.js → protected-model-port-DqF0qDU6.js} +4 -3
- package/dist/{protected-model-port-BFhSgMAy.js.map → protected-model-port-DqF0qDU6.js.map} +1 -1
- package/dist/{provision-supervisor-B9tf4soF.js → provision-supervisor-By5oGk3t.js} +3 -3
- package/dist/{provision-supervisor-B9tf4soF.js.map → provision-supervisor-By5oGk3t.js.map} +1 -1
- package/dist/{runtime-DKiucqGX.js → runtime-BbQmK7qe.js} +218 -20
- package/dist/runtime-BbQmK7qe.js.map +1 -0
- package/dist/{server-DIsiQ6mu.js → server-CHlpLE22.js} +3 -3
- package/dist/{server-DIsiQ6mu.js.map → server-CHlpLE22.js.map} +1 -1
- package/dist/{stream-agent-turn-DRTFLphI.d.ts → stream-agent-turn-DTgxWXbR.d.ts} +2 -2
- package/dist/{structural-rollout-BIBxgW64.js → structural-rollout-Dby60zHe.js} +2 -2
- package/dist/{structural-rollout-BIBxgW64.js.map → structural-rollout-Dby60zHe.js.map} +1 -1
- package/dist/{substrate-BToFBpfh.d.ts → substrate-CxwdhZri.d.ts} +2 -2
- package/dist/{supervise-DsQjhLN-.js → supervise-1QzYAyS2.js} +78 -7
- package/dist/supervise-1QzYAyS2.js.map +1 -0
- package/dist/{supervisor-CJyzUjc7.js → supervisor-yavsoQQo.js} +84 -9
- package/dist/{supervisor-CJyzUjc7.js.map → supervisor-yavsoQQo.js.map} +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +14 -14
- package/dist/tui/index.d.ts +1 -1
- package/dist/tui/index.js +1 -1
- package/dist/{types-ChewVlpE.d.ts → types-DNCNRu0E.d.ts} +24 -2
- package/dist/{workspace-archive-DQGFwvM6.js → workspace-archive-C9lgf77y.js} +11 -11
- package/dist/workspace-archive-C9lgf77y.js.map +1 -0
- package/package.json +4 -4
- package/dist/coordination-driver-BmtrkNPu.js.map +0 -1
- package/dist/graph-BKRvWzHx.js.map +0 -1
- package/dist/improvement-cycle-D0Os0Hg_.js.map +0 -1
- package/dist/prepare-RuaAclli.js.map +0 -1
- package/dist/runtime-DKiucqGX.js.map +0 -1
- package/dist/supervise-DsQjhLN-.js.map +0 -1
- package/dist/workspace-archive-DQGFwvM6.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["canonicalCandidateBytes","canonicalCandidateBytes","fsConstants"],"sources":["../../src/candidate-execution/builder.ts","../../src/candidate-execution/claim-file-store.ts","../../src/candidate-execution/dispose.ts","../../src/candidate-execution/protected-model-grant.ts","../../src/candidate-execution/recover.ts","../../src/candidate-execution/workspace-tree.ts"],"sourcesContent":["import type { CodeSurface } from '@tangle-network/agent-eval/campaign'\nimport { verifyCodeSurface } from '@tangle-network/agent-eval/campaign'\nimport type {\n AgentCandidateCodeDisabled,\n AgentCandidateCodeNoOp,\n AgentCandidateExecution,\n AgentCandidateGitHubRepository,\n AgentCandidateKnowledge,\n AgentCandidateMemoryPolicy,\n AgentCandidateProfile,\n AgentProfile,\n AgentProfileDiff,\n} from '@tangle-network/agent-interface'\nimport { type AgentCandidateBundleInput, sealAgentCandidateBundle } from './bundle'\nimport { embeddedCandidateArtifact } from './digest'\nimport {\n applyExactAgentProfileDiff,\n freezeGenericAgentCandidateProfile,\n parseExactAgentProfile,\n parseExactAgentProfileDiff,\n parseExactCandidateProfile,\n} from './profile'\n\n/** A complete profile that can be frozen without losing behavior. */\nexport type AgentCandidateProfileSource =\n | {\n kind: 'profile'\n profile: AgentProfile\n }\n | {\n kind: 'profile-diffs'\n base: AgentProfile\n /** Applied in order before the resulting profile is frozen into the bundle. */\n diffs: readonly AgentProfileDiff[]\n }\n | {\n kind: 'candidate-profile'\n /** Already converted to the closed, secret-free candidate profile contract. */\n profile: AgentCandidateProfile\n }\n\n/** The only accepted path from an agent-eval code candidate to executable bytes. */\nexport interface AgentCandidateCodeSurfaceSource {\n kind: 'code-surface'\n surface: CodeSurface\n repository: AgentCandidateGitHubRepository\n /** Optional parent directory used to resolve a relative `surface.worktreeRef`. */\n worktreeDir?: string\n}\n\n/** Explicit control/no-op code or one finalized CodeSurface whose bytes must still verify. */\nexport type AgentCandidateCodeSource =\n | AgentCandidateCodeDisabled\n | AgentCandidateCodeNoOp\n | AgentCandidateCodeSurfaceSource\n\n/** Complete measured surfaces and execution policy compiled into one candidate bundle. */\nexport interface BuildAgentCandidateBundleInput {\n profile: AgentCandidateProfileSource\n code: AgentCandidateCodeSource\n execution: AgentCandidateExecution\n knowledge?: AgentCandidateKnowledge\n memory: AgentCandidateMemoryPolicy\n}\n\n/**\n * Compile one measured profile/code candidate into the immutable execution\n * contract. Code bytes are re-read and verified by agent-eval before they are\n * embedded. The returned bundle is schema-validated, canonically digested, and\n * deeply immutable; call `verifyAgentCandidateBundle` at the execution boundary\n * to re-read external memory, repository, and workspace artifacts.\n */\nexport function buildAgentCandidateBundle(\n input: BuildAgentCandidateBundleInput,\n): ReturnType<typeof sealAgentCandidateBundle> {\n const bundle: AgentCandidateBundleInput = {\n kind: 'agent-candidate-bundle',\n digestAlgorithm: 'rfc8785-sha256',\n profile: compileCandidateProfile(input.profile),\n code: compileCandidateCode(input.code),\n execution: input.execution,\n ...(input.knowledge !== undefined ? { knowledge: input.knowledge } : {}),\n memory: input.memory,\n }\n return sealAgentCandidateBundle(bundle)\n}\n\nfunction compileCandidateProfile(source: AgentCandidateProfileSource): AgentCandidateProfile {\n if (source.kind === 'candidate-profile') {\n return parseExactCandidateProfile(source.profile)\n }\n\n if (source.kind === 'profile') {\n return freezeGenericAgentCandidateProfile(source.profile)\n }\n\n if (source.kind !== 'profile-diffs') {\n throw new Error(\n `unsupported candidate profile source: ${String((source as { kind?: unknown }).kind)}`,\n )\n }\n if (source.diffs.length === 0) {\n throw new Error('profile-diffs source requires at least one AgentProfileDiff')\n }\n let profile = parseExactAgentProfile(source.base, 'base profile')\n for (const [index, inputDiff] of source.diffs.entries()) {\n const diff = parseExactAgentProfileDiff(inputDiff, `profile diff ${index}`)\n profile = applyExactAgentProfileDiff(profile, diff, `profile diff ${index}`)\n }\n return freezeGenericAgentCandidateProfile(profile)\n}\n\nfunction compileCandidateCode(source: AgentCandidateCodeSource): AgentCandidateBundleInput['code'] {\n if (source.kind === 'disabled' || source.kind === 'no-op') return source\n if (source.kind !== 'code-surface') {\n throw new Error(\n `unsupported candidate code source: ${String((source as { kind?: unknown }).kind)}`,\n )\n }\n\n const verified = verifyCodeSurface(source.surface, source.worktreeDir)\n const patch = embeddedCandidateArtifact(verified.patchBytes)\n if (\n patch.sha256 !== source.surface.patch.sha256 ||\n patch.byteLength !== source.surface.patch.byteLength\n ) {\n throw new Error('verified CodeSurface patch bytes do not match its content identity')\n }\n return {\n kind: 'git-patch',\n repository: source.repository,\n baseCommit: source.surface.baseCommit,\n baseTree: source.surface.baseTree,\n candidateTree: source.surface.candidateTree,\n patch: { format: 'git-diff-binary', artifact: patch },\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport { linkSync } from 'node:fs'\nimport { mkdir, open, unlink } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { Sha256Digest } from '@tangle-network/agent-interface'\n\nimport {\n type AgentCandidateExecutionAttemptRecord,\n type AgentCandidateExecutionAttemptRef,\n type AgentCandidateExecutionClaim,\n type AgentCandidateExecutionClaimResult,\n type AgentCandidateExecutionClaimStore,\n type AgentCandidateExecutionFinishResult,\n type AgentCandidateExecutionLease,\n type AgentCandidateExecutionPhase,\n type AgentCandidateExecutionPhaseResult,\n type AgentCandidateExecutionRecoveryEvidence,\n type AgentCandidateExecutionStageResult,\n type AgentCandidateExecutionTerminalRecord,\n type AgentCandidateExecutionTerminalResult,\n type AgentCandidateRetryRejection,\n candidateClaimFileInternals,\n} from './claim'\nimport {\n assertRecoveryMatchesStaged,\n assertTerminalAllowedInPhase,\n assertTerminalMatchesClaim,\n assertTerminalMatchesStaged,\n recoveredTerminalRecord,\n rejectedFinish,\n rejectedStage,\n requireStagedTerminal,\n sealTerminalDigest,\n terminalRecord,\n} from './claim-terminal'\nimport { canonicalCandidateBytes } from './digest'\n\nconst {\n assertExpiredLease,\n assertLease,\n assertSameSlot,\n assertUnexpiredLease,\n attemptRecord,\n claimSlot,\n leaseDigest,\n newLease,\n readClaim,\n readClaimIfPresent,\n readTerminal,\n readTerminalIfPresent,\n readTransitionIfPresent,\n rejectedExistingClaim,\n rejectedRetry,\n retryRejection,\n sealAttemptRef,\n sealClaim,\n sealLease,\n} = candidateClaimFileInternals\n\nexport interface FileAgentCandidateExecutionClaimStoreOptions {\n /** Evaluator-owned directory shared by every process allowed to execute candidates. */\n directory: string\n /** Testable evaluator clock; defaults to `Date.now`. */\n now?: () => number\n}\n\n/** Cross-process lifecycle implemented as fsynced, create-if-absent records. */\nexport class FileAgentCandidateExecutionClaimStore implements AgentCandidateExecutionClaimStore {\n private readonly directory: string\n private readonly now: () => number\n\n constructor(options: FileAgentCandidateExecutionClaimStoreOptions) {\n if (options.directory.length === 0) {\n throw new Error('candidate execution claim directory must not be empty')\n }\n this.directory = options.directory\n this.now = options.now ?? Date.now\n }\n\n async tryClaim(\n requested: AgentCandidateExecutionClaim,\n ): Promise<AgentCandidateExecutionClaimResult> {\n const claim = sealClaim(requested)\n await mkdir(this.directory, { recursive: true })\n const claimPath = this.claimPath(claim)\n const existing = await readClaimIfPresent(claimPath)\n if (existing) {\n assertSameSlot(existing.claim, claim, claimPath)\n return rejectedExistingClaim(existing.claim, claim)\n }\n assertUnexpiredLease(claim.leaseExpiresAtMs, this.now())\n const retryFailure = await this.retryFailure(claim)\n if (retryFailure) return rejectedRetry(claim, retryFailure)\n\n const lease = newLease(claim)\n const acquired = await writeRecordIfAbsent(this.directory, claimPath, {\n ...claim,\n phase: 'claimed',\n leaseDigest: leaseDigest(lease),\n })\n if (acquired) return Object.freeze({ acquired: true, claim, lease })\n\n const winner = await readClaim(claimPath)\n assertSameSlot(winner.claim, claim, claimPath)\n return rejectedExistingClaim(winner.claim, claim)\n }\n\n async getAttempt(\n requestedAttempt: AgentCandidateExecutionAttemptRef,\n ): Promise<AgentCandidateExecutionAttemptRecord | undefined> {\n return await this.storedAttempt(sealAttemptRef(requestedAttempt))\n }\n\n async markCandidateMayRun(\n requestedLease: AgentCandidateExecutionLease,\n ): Promise<AgentCandidateExecutionPhaseResult> {\n const lease = sealLease(requestedLease)\n const claimPath = this.claimPath(lease)\n const stored = await readClaim(claimPath)\n assertSameSlot(stored.claim, lease, claimPath)\n assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease)\n const state = await this.transitionState(stored.claim)\n if (state.phase === 'candidate-may-run') {\n return Object.freeze({ marked: false, phase: 'candidate-may-run' })\n }\n if (state.staged) {\n throw new Error('candidate execution terminal was staged before candidate-may-run phase')\n }\n assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now())\n const marked = await writeRecordIfAbsent(\n this.directory,\n this.transitionPath(stored.claim, 1),\n {\n kind: 'candidate-execution-phase',\n executionId: stored.claim.executionId,\n attempt: stored.claim.attempt,\n executionPlanDigest: stored.claim.executionPlanDigest,\n phase: 'candidate-may-run',\n },\n this.ownerPublication(stored.claim),\n )\n if (marked) return Object.freeze({ marked: true, phase: 'candidate-may-run' })\n const winner = await this.transitionState(stored.claim)\n if (winner.phase === 'candidate-may-run') {\n return Object.freeze({ marked: false, phase: 'candidate-may-run' })\n }\n throw new Error('candidate execution terminal was staged before candidate-may-run phase')\n }\n\n async stageTerminal(\n requestedLease: AgentCandidateExecutionLease,\n result: AgentCandidateExecutionTerminalResult,\n ): Promise<AgentCandidateExecutionStageResult> {\n const lease = sealLease(requestedLease)\n const claimPath = this.claimPath(lease)\n const stored = await readClaim(claimPath)\n assertSameSlot(stored.claim, lease, claimPath)\n assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease)\n const terminal = terminalRecord(stored.claim, result)\n const state = await this.transitionState(stored.claim)\n if (state.staged) return rejectedStage(state.staged, terminal)\n assertTerminalAllowedInPhase(state.phase, terminal)\n assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now())\n const transition = state.phase === 'claimed' ? 1 : 2\n const staged = await writeRecordIfAbsent(\n this.directory,\n this.transitionPath(stored.claim, transition),\n {\n kind: 'candidate-execution-pending-terminal',\n terminal,\n },\n this.ownerPublication(stored.claim),\n )\n if (staged) return Object.freeze({ staged: true, terminal })\n const winner = await this.transitionState(stored.claim)\n if (!winner.staged) {\n throw new Error('candidate execution phase transition won while staging terminal')\n }\n return rejectedStage(winner.staged, terminal)\n }\n\n async finish(\n requestedLease: AgentCandidateExecutionLease,\n requestedTerminalDigest: Sha256Digest,\n ): Promise<AgentCandidateExecutionFinishResult> {\n const lease = sealLease(requestedLease)\n const terminalDigest = sealTerminalDigest(requestedTerminalDigest)\n const claimPath = this.claimPath(lease)\n const stored = await readClaim(claimPath)\n assertSameSlot(stored.claim, lease, claimPath)\n assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease)\n const state = await this.transitionState(stored.claim)\n const staged = requireStagedTerminal(state.staged, terminalDigest)\n const terminalPath = this.terminalPath(lease)\n const existing = await readTerminalIfPresent(terminalPath)\n if (existing) {\n assertTerminalMatchesClaim(existing, stored.claim, terminalPath)\n assertTerminalMatchesStaged(existing, staged, terminalPath)\n return rejectedFinish(existing, terminalDigest)\n }\n assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now())\n const finished = await writeRecordIfAbsent(\n this.directory,\n terminalPath,\n {\n terminal: staged,\n },\n this.ownerPublication(stored.claim),\n )\n if (finished) return Object.freeze({ finished: true, terminal: staged })\n const winner = await readTerminal(terminalPath)\n assertSameSlot(winner, lease, terminalPath)\n assertTerminalMatchesClaim(winner, stored.claim, terminalPath)\n assertTerminalMatchesStaged(winner, staged, terminalPath)\n return rejectedFinish(winner, terminalDigest)\n }\n\n async recoverExpired(\n requestedAttempt: AgentCandidateExecutionAttemptRef,\n evidence: AgentCandidateExecutionRecoveryEvidence,\n ): Promise<AgentCandidateExecutionFinishResult> {\n const attempt = sealAttemptRef(requestedAttempt)\n const record = await this.storedAttempt(attempt)\n if (!record) throw new Error('candidate execution recovery does not name an acquired attempt')\n const recovered = recoveredTerminalRecord(record.claim, record.phase, evidence)\n if (record.staged) assertRecoveryMatchesStaged(record.staged, recovered)\n const requestedDigest = record.staged?.terminalDigest ?? recovered.terminalDigest\n if (record.terminal) return rejectedFinish(record.terminal, requestedDigest)\n assertExpiredLease(record.claim.leaseExpiresAtMs, this.now())\n\n let staged = record.staged\n if (!staged) {\n const transition = record.phase === 'claimed' ? 1 : 2\n const didStage = await writeRecordIfAbsent(\n this.directory,\n this.transitionPath(record.claim, transition),\n {\n kind: 'candidate-execution-pending-terminal',\n terminal: recovered,\n },\n )\n if (didStage) {\n staged = recovered\n } else {\n const winner = await this.transitionState(record.claim)\n if (!winner.staged) {\n throw new Error(\n 'candidate execution recovery lost terminal staging to a phase transition',\n )\n }\n assertRecoveryMatchesStaged(winner.staged, recovered)\n staged = winner.staged\n }\n }\n\n const terminalPath = this.terminalPath(attempt)\n const didFinish = await writeRecordIfAbsent(this.directory, terminalPath, {\n terminal: staged,\n })\n if (didFinish) return Object.freeze({ finished: true, terminal: staged })\n const winner = await readTerminal(terminalPath)\n assertSameSlot(winner, attempt, terminalPath)\n assertTerminalMatchesClaim(winner, record.claim, terminalPath)\n assertTerminalMatchesStaged(winner, staged, terminalPath)\n return rejectedFinish(winner, staged.terminalDigest)\n }\n\n private async storedAttempt(\n claim: Pick<AgentCandidateExecutionClaim, 'executionId' | 'attempt'>,\n ): Promise<AgentCandidateExecutionAttemptRecord | undefined> {\n const claimPath = this.claimPath(claim)\n const stored = await readClaimIfPresent(claimPath)\n if (!stored) return undefined\n assertSameSlot(stored.claim, claim, claimPath)\n const terminalPath = this.terminalPath(claim)\n const terminal = await readTerminalIfPresent(terminalPath)\n // Terminal publication follows its immutable staged outbox, so read that prerequisite later.\n const state = await this.transitionState(stored.claim)\n if (terminal) {\n assertTerminalMatchesClaim(terminal, stored.claim, terminalPath)\n if (!state.staged) {\n throw new Error(\n `candidate execution terminal record at ${terminalPath} has no staged outbox`,\n )\n }\n assertTerminalMatchesStaged(terminal, state.staged, terminalPath)\n }\n return attemptRecord(stored.claim, state.phase, state.staged, terminal)\n }\n\n private async transitionState(claim: AgentCandidateExecutionClaim): Promise<{\n phase: AgentCandidateExecutionPhase\n staged?: AgentCandidateExecutionTerminalRecord\n }> {\n const firstPath = this.transitionPath(claim, 1)\n const first = await readTransitionIfPresent(firstPath, claim)\n if (!first) return { phase: 'claimed' }\n if (first.kind === 'pending') return { phase: 'claimed', staged: first.terminal }\n const secondPath = this.transitionPath(claim, 2)\n const second = await readTransitionIfPresent(secondPath, claim)\n if (!second) return { phase: 'candidate-may-run' }\n if (second.kind !== 'pending') {\n throw new Error(`candidate execution transition at ${secondPath} repeats the phase marker`)\n }\n return { phase: 'candidate-may-run', staged: second.terminal }\n }\n\n private async retryFailure(\n claim: AgentCandidateExecutionClaim,\n ): Promise<AgentCandidateRetryRejection | undefined> {\n if (claim.attempt === 1) return undefined\n return retryRejection(\n claim,\n await this.storedAttempt({\n executionId: claim.executionId,\n attempt: claim.attempt - 1,\n }),\n )\n }\n\n private ownerPublication(claim: AgentCandidateExecutionClaim): {\n authorizePublish: () => true\n } {\n return {\n authorizePublish: () => {\n assertUnexpiredLease(claim.leaseExpiresAtMs, this.now())\n return true\n },\n }\n }\n\n private claimPath(claim: Pick<AgentCandidateExecutionClaim, 'executionId' | 'attempt'>): string {\n return join(this.directory, `${claimSlot(claim)}.claim.json`)\n }\n\n private terminalPath(\n claim: Pick<AgentCandidateExecutionClaim, 'executionId' | 'attempt'>,\n ): string {\n return join(this.directory, `${claimSlot(claim)}.terminal.json`)\n }\n\n private transitionPath(\n claim: Pick<AgentCandidateExecutionClaim, 'executionId' | 'attempt'>,\n ordinal: 1 | 2,\n ): string {\n return join(this.directory, `${claimSlot(claim)}.transition-${ordinal}.json`)\n }\n}\n\n/**\n * Candidate records keep this async writer because lease authorization must run after the\n * asynchronous temp-file fsync and immediately before synchronous link publication. The\n * supervisor durability helper is synchronous and has no lease hook, so combining them would\n * either move authorization earlier or add an async race to the claim boundary.\n */\nasync function writeRecordIfAbsent(\n directory: string,\n destination: string,\n record: object,\n options: {\n /** Synchronous authorization checked after temp fsync, immediately before atomic publication. */\n authorizePublish?: () => true\n } = {},\n): Promise<boolean> {\n const temporaryPath = join(directory, `.candidate-execution-${process.pid}-${randomUUID()}.tmp`)\n const handle = await open(temporaryPath, 'wx', 0o600)\n try {\n await handle.writeFile(\n Buffer.concat([Buffer.from(canonicalCandidateBytes(record)), Buffer.from('\\n')]),\n )\n await handle.sync()\n } finally {\n await handle.close()\n }\n\n let written = false\n try {\n if (options.authorizePublish && options.authorizePublish() !== true) {\n throw new Error('candidate execution record publication was not authorized')\n }\n // Authorization and the atomic filesystem call are one synchronous critical\n // section so the event loop cannot advance an injected lease clock between them.\n linkSync(temporaryPath, destination)\n written = true\n await syncDirectory(directory)\n } catch (error) {\n if (!isNodeError(error, 'EEXIST')) throw error\n } finally {\n await unlink(temporaryPath).catch((error: unknown) => {\n if (!isNodeError(error, 'ENOENT')) throw error\n })\n }\n return written\n}\n\nasync function syncDirectory(directory: string): Promise<void> {\n const handle = await open(directory, 'r')\n try {\n await handle.sync()\n } finally {\n await handle.close()\n }\n}\n\nfunction isNodeError(error: unknown, code: string): boolean {\n return (\n error !== null &&\n typeof error === 'object' &&\n 'code' in error &&\n (error as { code?: unknown }).code === code\n )\n}\n","import { candidateCleanupDeadline, withinCandidateCleanupDeadline } from './cleanup'\nimport { sealAgentCandidateModelSettlement } from './model-settlement'\nimport {\n assertPreparedCandidateIntegrity,\n beginPreparedCandidateDisposal,\n consumePreparedCandidateExecution,\n} from './prepared-state'\nimport type { PreparedAgentCandidateExecution } from './types'\n\nexport interface DisposePreparedAgentCandidateOptions {\n cleanupTimeoutMs?: number\n}\n\n/** Revoke reservations held by a prepared candidate that will not be executed. */\nexport async function disposePreparedAgentCandidateExecution(\n prepared: PreparedAgentCandidateExecution,\n options: DisposePreparedAgentCandidateOptions = {},\n): Promise<{ disposed: true }> {\n const initialState = assertPreparedCandidateIntegrity(prepared)\n const cleanupTimeoutMs = options.cleanupTimeoutMs ?? initialState.cleanupTimeoutMs\n if (cleanupTimeoutMs > initialState.cleanupTimeoutMs) {\n throw new Error('disposal cleanup timeout exceeds the frozen preparation bound')\n }\n const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs)\n const state = beginPreparedCandidateDisposal(prepared)\n const cleanup: Array<Promise<unknown>> = []\n\n if (state.memory.mode === 'isolated') {\n const reservation = state.memoryReservation\n if (!reservation) throw new Error('isolated memory reservation is missing')\n cleanup.push(\n withinCandidateCleanupDeadline(\n async () => {\n const closed = await state.ports.memory.close({\n executionId: state.executionId,\n preparationId: reservation.preparationId,\n accessDigest: reservation.accessDigest,\n effectiveNamespace: reservation.effectiveNamespace,\n reason: 'abandoned',\n })\n if (closed.closed !== true || Object.keys(closed).some((key) => key !== 'closed')) {\n throw new Error('abandoned isolated memory access did not acknowledge closure')\n }\n },\n cleanupDeadlineAtMs,\n 'isolated memory disposal',\n ),\n )\n }\n\n cleanup.push(\n withinCandidateCleanupDeadline(\n async () => {\n const settlement = sealAgentCandidateModelSettlement(\n await state.ports.models.settleGrant({\n executionId: state.executionId,\n preparationId: state.preparationId,\n grantDigest: state.modelReservation.digest,\n resolved: state.resolvedModel,\n reason: 'abandoned',\n }),\n {\n preparationId: state.preparationId,\n grantDigest: state.modelReservation.digest,\n model: state.resolvedModel.model,\n },\n )\n if (settlement.usage.modelCalls !== 0) {\n throw new Error('unexecuted model reservation unexpectedly contains calls')\n }\n },\n cleanupDeadlineAtMs,\n 'model reservation disposal',\n ),\n )\n\n const results = await Promise.allSettled(cleanup)\n const failures = results\n .filter((result): result is PromiseRejectedResult => result.status === 'rejected')\n .map((result) => result.reason)\n if (failures.length > 0) {\n consumePreparedCandidateExecution(prepared, 'disposal-failed')\n throw new AggregateError(failures, 'candidate preparation disposal failed')\n }\n\n consumePreparedCandidateExecution(prepared, 'disposed')\n return Object.freeze({ disposed: true as const })\n}\n","import type { AgentCandidateResolvedModel } from '@tangle-network/agent-interface'\n\nimport type {\n AgentCandidateModelGrantActivateInput,\n AgentCandidateModelGrantReserveInput,\n AgentCandidateModelGrantSettleInput,\n} from './protected-model-port'\nimport type {\n AgentCandidateModelPort,\n AgentCandidateProtectedModelActivation,\n AgentCandidateProtectedModelReservation,\n AgentCandidateProtectedModelSettlement,\n} from './types'\n\n/** Reservation fields supplied by a caller before Runtime resolves the model. */\nexport type AgentCandidateModelGrantRunReservationInput = Omit<\n AgentCandidateModelGrantReserveInput,\n 'resolved'\n>\n\n/** Values available only while one protected model grant is active. */\nexport interface ProtectedAgentCandidateModelGrantContext {\n readonly activation: AgentCandidateProtectedModelActivation\n readonly reservation: AgentCandidateProtectedModelReservation\n readonly resolved: AgentCandidateResolvedModel\n}\n\n/** Inputs for one protected grant scoped to one bounded caller unit. */\nexport interface RunProtectedAgentCandidateModelGrantOptions<TResult> {\n /** Runtime port that validates and settles the evaluator-owned grant. */\n readonly port: AgentCandidateModelPort\n /** Provider-neutral model request resolved before any grant is reserved. */\n readonly resolve: Parameters<AgentCandidateModelPort['resolve']>[0]\n /** One bounded unit's immutable identity, attempt, expiry, and limits. */\n readonly reserve: AgentCandidateModelGrantRunReservationInput\n /** Must be no later than the reservation expiry. */\n readonly deadlineAtMs: AgentCandidateModelGrantActivateInput['deadlineAtMs']\n /** Execute exactly one bounded unit while the activated environment is valid. */\n readonly execute: (context: ProtectedAgentCandidateModelGrantContext) => Promise<TResult>\n}\n\n/** Result and sealed settlement returned after one protected grant closes. */\nexport interface RunProtectedAgentCandidateModelGrantResult<TResult> {\n readonly value: TResult\n readonly resolved: AgentCandidateResolvedModel\n readonly reservation: AgentCandidateProtectedModelReservation\n readonly settlement: AgentCandidateProtectedModelSettlement\n}\n\nconst SETTLEMENT_RETRY_INITIAL_DELAY_MS = 25\nconst SETTLEMENT_RETRY_MAX_DELAY_MS = 1_000\n\n/**\n * Run one bounded unit under a protected model grant.\n *\n * Runtime owns the grant lifecycle; callers own the unit boundary and any\n * durable scheduling or accounting around it. A reserved grant is settled\n * after activation failure or callback failure, and the callback error is\n * preserved when settlement also fails.\n */\nexport async function runProtectedAgentCandidateModelGrant<TResult>(\n options: RunProtectedAgentCandidateModelGrantOptions<TResult>,\n): Promise<RunProtectedAgentCandidateModelGrantResult<TResult>> {\n if (typeof options.execute !== 'function') {\n throw new TypeError('protected model grant execute callback is required')\n }\n\n const resolved = await options.port.resolve(options.resolve)\n const reservation = await options.port.reserveGrant({\n ...options.reserve,\n resolved,\n })\n\n let value!: TResult\n let executionError: unknown\n let executionFailed = false\n let activated = false\n let reason: AgentCandidateModelGrantSettleInput['reason'] = 'preparation-failed'\n\n try {\n const activation = await options.port.activateGrant({\n executionId: options.reserve.executionId,\n preparationId: options.reserve.preparationId,\n grantDigest: reservation.digest,\n resolved,\n deadlineAtMs: options.deadlineAtMs,\n })\n activated = true\n value = await options.execute({ activation, reservation, resolved })\n reason = 'completed'\n } catch (error) {\n executionError = error\n executionFailed = true\n if (activated) reason = 'failed'\n }\n\n let settlement: AgentCandidateProtectedModelSettlement\n try {\n settlement = await settleProtectedModelGrant(options, {\n executionId: options.reserve.executionId,\n preparationId: options.reserve.preparationId,\n grantDigest: reservation.digest,\n resolved,\n reason,\n })\n } catch (settlementError) {\n if (executionFailed) {\n throw new AggregateError(\n [executionError, settlementError],\n 'protected model execution failed and its grant did not settle',\n )\n }\n throw settlementError\n }\n\n if (executionFailed) throw executionError\n return { value, resolved, reservation, settlement }\n}\n\nasync function settleProtectedModelGrant<TResult>(\n options: RunProtectedAgentCandidateModelGrantOptions<TResult>,\n input: AgentCandidateModelGrantSettleInput,\n): Promise<AgentCandidateProtectedModelSettlement> {\n let delayMs = SETTLEMENT_RETRY_INITIAL_DELAY_MS\n for (;;) {\n try {\n return await options.port.settleGrant(input)\n } catch (error) {\n if (!isCandidateGrantDrainingError(error)) throw error\n const remainingMs = options.deadlineAtMs - Date.now()\n if (remainingMs <= 0) throw error\n await waitForSettlementRetry(Math.min(delayMs, remainingMs))\n if (Date.now() >= options.deadlineAtMs) throw error\n delayMs = Math.min(delayMs * 2, SETTLEMENT_RETRY_MAX_DELAY_MS)\n }\n }\n}\n\nfunction waitForSettlementRetry(delayMs: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, delayMs))\n}\n\n/** Only the gateway's explicit draining state is retryable; auth and ledger errors fail closed. */\nfunction isCandidateGrantDrainingError(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) return false\n const candidate = error as { code?: unknown; message?: unknown }\n if (candidate.code === 'candidate_grant_draining') return true\n return (\n typeof candidate.message === 'string' && /\\bcandidate_grant_draining\\b/u.test(candidate.message)\n )\n}\n","import type { TraceStore } from '@tangle-network/agent-eval'\nimport type {\n AgentCandidateExecutionPlanMaterial,\n AgentCandidateMaterializationReceipt,\n} from '@tangle-network/agent-interface'\nimport {\n agentCandidateExecutionPlanMaterialSchema,\n agentCandidateMaterializationReceiptSchema,\n} from '@tangle-network/agent-interface'\n\nimport { readVerifiedArtifact } from './artifacts'\nimport type {\n AgentCandidateExecutionAttemptRef,\n AgentCandidateExecutionClaim,\n AgentCandidateExecutionClaimStore,\n AgentCandidateExecutionFinishResult,\n} from './claim'\nimport {\n candidateCleanupDeadline,\n candidateCleanupTimeout,\n withinCandidateCleanupDeadline,\n} from './cleanup'\nimport { canonicalCandidateBytes } from './digest'\nimport { sealAgentCandidateExecutorStopAcknowledgement } from './executor-capture'\nimport { sealAgentCandidateModelSettlement } from './model-settlement'\nimport { persistCandidateModelSettlementEvidence } from './outcome-evidence'\nimport { persistCandidateOutputArtifact } from './output-artifacts'\nimport { RecoveryAgentCandidateTraceStore } from './protected-trace-store'\nimport type {\n AgentCandidateExecutionPorts,\n AgentCandidateExecutorPort,\n AgentCandidateOutputArtifactPort,\n} from './types'\n\nexport interface RecoverExpiredAgentCandidateOptions {\n attempt: AgentCandidateExecutionAttemptRef\n claimStore: AgentCandidateExecutionClaimStore\n executor: AgentCandidateExecutorPort\n traceStore: TraceStore\n ports: Pick<AgentCandidateExecutionPorts, 'models' | 'memory'>\n outputArtifacts: AgentCandidateOutputArtifactPort\n cleanupTimeoutMs?: number\n /** Evaluator clock; must be the same clock used by the claim store. */\n now?: () => number\n}\n\n/** Close an expired crashed attempt from persisted non-secret handles, then record failure. */\nexport async function recoverExpiredAgentCandidateExecution(\n options: RecoverExpiredAgentCandidateOptions,\n): Promise<AgentCandidateExecutionFinishResult> {\n const record = await options.claimStore.getAttempt(options.attempt)\n if (!record) throw new Error('candidate execution recovery attempt is missing')\n if (record.terminal) {\n return Object.freeze({ finished: false, terminal: record.terminal, exactReplay: true })\n }\n const cleanupTimeoutMs = candidateCleanupTimeout(\n options.cleanupTimeoutMs ?? record.claim.cleanup.cleanupTimeoutMs,\n )\n if (cleanupTimeoutMs > record.claim.cleanup.cleanupTimeoutMs) {\n throw new Error('recovery cleanup timeout exceeds the frozen preparation bound')\n }\n\n const now = options.now ?? Date.now\n if (now() < record.claim.leaseExpiresAtMs) {\n throw new Error('candidate execution lease has not expired')\n }\n\n const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs)\n const recoveryTraceStore = new RecoveryAgentCandidateTraceStore(options.traceStore)\n const preparation = withinCandidateCleanupDeadline(\n () => readRecoveryPreparation(record.claim, options.outputArtifacts),\n cleanupDeadlineAtMs,\n 'expired candidate preparation evidence read',\n )\n const processClosure = withinCandidateCleanupDeadline(\n async (cleanupSignal) => {\n const stopped = await options.executor.stop(\n {\n executionId: record.claim.executionId,\n executionPlanDigest: record.claim.executionPlanDigest,\n },\n {\n traceStore: recoveryTraceStore,\n reason: 'failed',\n signal: cleanupSignal,\n deadlineAtMs: cleanupDeadlineAtMs,\n },\n )\n sealAgentCandidateExecutorStopAcknowledgement(stopped)\n if (options.executor.dispose) {\n const disposed = await options.executor.dispose(\n {\n executionId: record.claim.executionId,\n executionPlanDigest: record.claim.executionPlanDigest,\n },\n { signal: cleanupSignal },\n )\n if (disposed.disposed !== true || Object.keys(disposed).some((key) => key !== 'disposed')) {\n throw new Error('expired candidate executor did not acknowledge resource disposal')\n }\n }\n return { stopped: true as const }\n },\n cleanupDeadlineAtMs,\n 'expired candidate process termination',\n )\n\n const modelClosure = withinCandidateCleanupDeadline(\n async () =>\n sealAgentCandidateModelSettlement(\n await options.ports.models.settleGrant({\n executionId: record.claim.executionId,\n preparationId: record.claim.cleanup.preparationId,\n grantDigest: record.claim.cleanup.modelGrantDigest,\n resolved: record.claim.cleanup.resolvedModel,\n reason: 'failed',\n }),\n {\n preparationId: record.claim.cleanup.preparationId,\n grantDigest: record.claim.cleanup.modelGrantDigest,\n model: record.claim.cleanup.resolvedModel.model,\n },\n ),\n cleanupDeadlineAtMs,\n 'expired candidate model settlement',\n )\n\n const memoryClosure = record.claim.cleanup.memory\n ? withinCandidateCleanupDeadline(\n async () => {\n const memory = record.claim.cleanup.memory\n if (!memory) throw new Error('expired candidate memory handle is missing')\n const closed = await options.ports.memory.close({\n executionId: record.claim.executionId,\n preparationId: record.claim.cleanup.preparationId,\n accessDigest: memory.accessDigest,\n effectiveNamespace: memory.effectiveNamespace,\n reason: 'failed',\n })\n if (closed.closed !== true || Object.keys(closed).some((key) => key !== 'closed')) {\n throw new Error('expired candidate memory did not acknowledge closure')\n }\n return { closed: true as const }\n },\n cleanupDeadlineAtMs,\n 'expired candidate memory closure',\n )\n : undefined\n\n const closureOutcomes = Promise.allSettled([\n modelClosure,\n memoryClosure ?? Promise.resolve(undefined),\n ] as const)\n const [preparationOutcome, processOutcome] = await Promise.allSettled([\n preparation,\n processClosure,\n ] as const)\n const failures: unknown[] = [preparationOutcome, processOutcome]\n .filter((outcome): outcome is PromiseRejectedResult => outcome.status === 'rejected')\n .map((outcome) => outcome.reason)\n const [modelOutcome, memoryOutcome] = await closureOutcomes\n for (const outcome of [modelOutcome, memoryOutcome]) {\n if (outcome.status === 'rejected') failures.push(outcome.reason)\n }\n if (failures.length > 0) {\n throw new AggregateError(failures, 'expired candidate cleanup could not be proven')\n }\n\n if (modelOutcome.status !== 'fulfilled') {\n throw new Error('expired candidate model settlement was not recovered')\n }\n const model = modelOutcome.value\n const modelSettlement = await withinCandidateCleanupDeadline(\n () =>\n persistCandidateModelSettlementEvidence(\n {\n executionId: record.claim.executionId,\n executionPlanDigest: record.claim.executionPlanDigest,\n resolvedModel: record.claim.cleanup.resolvedModel,\n },\n model,\n options.outputArtifacts,\n ),\n cleanupDeadlineAtMs,\n 'expired candidate model-settlement persistence',\n )\n const failureClass =\n record.phase === 'claimed' && model.usage.modelCalls === 0\n ? 'pre-model-infrastructure'\n : 'unknown'\n const failureEvidence = await withinCandidateCleanupDeadline(\n () =>\n persistCandidateOutputArtifact(options.outputArtifacts, {\n executionId: record.claim.executionId,\n purpose: 'failure-evidence',\n bytes: canonicalCandidateBytes({\n kind: 'agent-candidate-recovery-failure',\n executionId: record.claim.executionId,\n attempt: record.claim.attempt,\n bundleDigest: record.claim.bundleDigest,\n executionPlanDigest: record.claim.executionPlanDigest,\n materializationReceiptDigest:\n record.claim.preparationEvidence.materializationReceipt.sha256,\n failureClass,\n processStopped: true,\n modelSettlementDigest: modelSettlement.digest,\n memoryClosed: record.claim.cleanup.memory !== undefined,\n }),\n }),\n cleanupDeadlineAtMs,\n 'expired candidate failure-evidence persistence',\n )\n const memory = record.claim.cleanup.memory\n return await options.claimStore.recoverExpired(options.attempt, {\n failureClass,\n usage: model.usage,\n modelSettlement: modelSettlement.artifact,\n failureEvidence,\n process: {\n stopped: true,\n executionPlanDigest: record.claim.executionPlanDigest,\n },\n model: {\n closed: true,\n preparationId: record.claim.cleanup.preparationId,\n grantDigest: record.claim.cleanup.modelGrantDigest,\n },\n ...(memory\n ? {\n memory: {\n closed: true,\n preparationId: record.claim.cleanup.preparationId,\n accessDigest: memory.accessDigest,\n effectiveNamespace: memory.effectiveNamespace,\n },\n }\n : {}),\n })\n}\n\nasync function readRecoveryPreparation(\n claim: AgentCandidateExecutionClaim,\n artifacts: AgentCandidateOutputArtifactPort,\n): Promise<{\n plan: AgentCandidateExecutionPlanMaterial\n receipt: AgentCandidateMaterializationReceipt\n}> {\n const [planBytes, receiptBytes] = await Promise.all([\n readVerifiedArtifact(claim.preparationEvidence.executionPlan, artifacts),\n readVerifiedArtifact(claim.preparationEvidence.materializationReceipt, artifacts),\n ])\n const plan = agentCandidateExecutionPlanMaterialSchema.parse(\n parseCanonicalBytes(planBytes, 'plan'),\n )\n if (claim.preparationEvidence.executionPlan.sha256 !== claim.executionPlanDigest) {\n throw new Error('recovery execution plan artifact does not match the claimed digest')\n }\n const receiptMaterial = parseCanonicalBytes(receiptBytes, 'materialization receipt')\n const receipt = agentCandidateMaterializationReceiptSchema.parse({\n ...receiptMaterial,\n digest: claim.preparationEvidence.materializationReceipt.sha256,\n })\n if (\n receipt.bundleDigest !== claim.bundleDigest ||\n receipt.executionPlan.digest !== claim.executionPlanDigest ||\n plan.runCell.bundleDigest !== claim.bundleDigest ||\n plan.executionId !== claim.executionId\n ) {\n throw new Error('recovery preparation evidence does not match the durable claim')\n }\n return { plan, receipt }\n}\n\nfunction parseCanonicalBytes(bytes: Uint8Array, label: string): Record<string, unknown> {\n let parsed: unknown\n try {\n parsed = JSON.parse(Buffer.from(bytes).toString('utf8'))\n } catch (error) {\n throw new Error(`candidate recovery ${label} is not JSON`, { cause: error })\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`candidate recovery ${label} must be an object`)\n }\n if (!Buffer.from(canonicalCandidateBytes(parsed)).equals(Buffer.from(bytes))) {\n throw new Error(`candidate recovery ${label} bytes are not canonical`)\n }\n return parsed as Record<string, unknown>\n}\n","/**\n * A content digest over a DIRECTORY TREE, and the per-file seed that fills one.\n *\n * The question this answers is \"is this workspace the same workspace\" — the question every launch\n * and every close of a branchable run has to answer, about a tree that a live process is still\n * writing to. That is a different question from the one\n * {@link scanMaterializedWorkspaceManifest} answers, and the difference decides the policies here:\n *\n * - A candidate manifest describes ONE EXACT regular-file workspace. It refuses a symbolic link\n * outright, because `AgentCandidateWorkspaceManifestMaterial` has no representation for one, and\n * it refuses an entry that disappears, because a signed manifest describes a settled tree.\n * - A tree descriptor describes what a RUN PRODUCED. A python venv writes\n * `bin/python -> /usr/bin/python3` as a matter of course, and a work directory can lose an entry\n * between two directory reads. Refusing on either aborts the close, and a run that never wrote\n * its digest can never be branched from.\n *\n * So the two policies are the caller's, one for links and one for vanishing entries, and both\n * default to `'refuse'` — the strict reading, correct for an INPUT seed, where a link that leaves\n * the tree means the seed describes bytes it does not contain. `'exclude'` is the reading a close\n * needs: the entry is never followed, it is recorded, and the exclusion itself is hashed, so two\n * trees that differ only in an excluded entry cannot share a digest.\n *\n * NOTHING is held in memory. A file reaches the tree digest as its own streamed sha-256 plus its\n * length, so the largest file in the tree does not decide whether the tree can be described:\n * `readFile` refuses anything above 2 GiB with `ERR_FS_FILE_TOO_LARGE`, and a single multi-gigabyte\n * artifact is exactly what a run leaves behind.\n *\n * @module\n * @experimental\n */\n\nimport { createHash } from 'node:crypto'\nimport { constants as fsConstants } from 'node:fs'\nimport { cp, lstat, open, readdir, readlink, realpath } from 'node:fs/promises'\nimport { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'\nimport type { Sha256Digest } from '@tangle-network/agent-interface'\n\n/**\n * `'tree-v1'` records a file's exact permission bits. `'portable-tree-v1'` records the two modes\n * Git stores — `755` when any execute bit is set, `644` otherwise, and `755` for a directory — so a\n * digest survives a checkout whose umask differs. Both stamp the algorithm into the digest, so one\n * tree cannot produce the same digest under both.\n */\nexport type WorkspaceTreeAlgorithm = 'tree-v1' | 'portable-tree-v1'\n\n/** What a walk does with an entry it refuses to describe: fail, or record and continue. */\nexport type WorkspaceTreeEntryPolicy = 'refuse' | 'exclude'\n\n/** Why one entry contributed its name instead of its content. */\nexport type WorkspaceTreeExclusionReason =\n | 'absolute-symlink'\n | 'symlink-escapes-tree'\n | 'symlink-resolves-outside-tree'\n | 'unresolved-symlink'\n | 'entry-disappeared'\n\n/** One entry the walk recorded but did not describe. Reported rather than dropped: a digest that\n * silently omitted an entry would be a different digest with no way to tell why. */\nexport interface WorkspaceTreeExclusion {\n /** Tree-relative path, `/`-separated. */\n readonly path: string\n readonly reason: WorkspaceTreeExclusionReason\n /** The link target exactly as it was written, for a symbolic-link exclusion. */\n readonly target?: string\n}\n\nexport interface WorkspaceTreeDescriptor {\n readonly algorithm: WorkspaceTreeAlgorithm\n readonly digest: Sha256Digest\n readonly files: number\n readonly directories: number\n /** Links kept INSIDE the tree. An excluded link is counted in `excluded`, never here. */\n readonly symlinks: number\n /** Total described regular-file bytes. */\n readonly bytes: number\n readonly excluded: ReadonlyArray<WorkspaceTreeExclusion>\n}\n\nexport interface DescribeWorkspaceTreeOptions {\n /** Default `'tree-v1'`. */\n readonly algorithm?: WorkspaceTreeAlgorithm\n /**\n * A symbolic link that is absolute, leaves the tree lexically, resolves outside it, or does not\n * resolve at all. `'refuse'` (default) is correct for an input seed. `'exclude'` is correct for a\n * close-time walk over a tree a run wrote. The link is NEVER followed in either policy: an\n * escaping link must not contribute bytes from outside the tree to a content address.\n */\n readonly onEscapingLink?: WorkspaceTreeEntryPolicy\n /**\n * An entry that vanished between the directory read that named it and the walk that reached it.\n * `'refuse'` (default) is correct for a settled tree; `'exclude'` is correct for a workspace a\n * live process still writes to.\n */\n readonly onMissingEntry?: WorkspaceTreeEntryPolicy\n}\n\nexport interface SeedWorkspaceTreeInput {\n /** The tree to copy FROM. Described, then copied entry by entry. */\n readonly source: string\n /** The tree to copy INTO. Must exist, and must not lie inside `source`. */\n readonly destination: string\n /** Default `'tree-v1'`; decides only the digest this returns, never the bytes it writes. */\n readonly algorithm?: WorkspaceTreeAlgorithm\n}\n\nconst TREE_ALGORITHMS: ReadonlySet<string> = new Set<WorkspaceTreeAlgorithm>([\n 'tree-v1',\n 'portable-tree-v1',\n])\n\nconst ENTRY_POLICIES: ReadonlySet<string> = new Set<WorkspaceTreeEntryPolicy>(['refuse', 'exclude'])\n\n/**\n * Describe one directory tree by content, streaming every file.\n *\n * The digest covers, for every entry in sorted order: its kind, its tree-relative path, its mode\n * under the selected algorithm, and then — for a file, its length and its own sha-256; for a kept\n * link, its target; for an excluded entry, the reason and the target. A file's content therefore\n * never enters the tree hash directly, which is what removes any in-memory size ceiling: the tree\n * hash consumes 32 bytes per file however large the file is.\n *\n * A hard-linked regular file is described like any other regular file. Its content is what the\n * digest is about, and a package manager that links a store into `node_modules` is ordinary\n * content, not a reason to refuse a workspace.\n */\nexport async function describeWorkspaceTree(\n directory: string,\n options: DescribeWorkspaceTreeOptions = {},\n): Promise<WorkspaceTreeDescriptor> {\n const algorithm = options.algorithm ?? 'tree-v1'\n const onEscapingLink = options.onEscapingLink ?? 'refuse'\n const onMissingEntry = options.onMissingEntry ?? 'refuse'\n if (!TREE_ALGORITHMS.has(algorithm)) {\n throw new Error(`unsupported workspace tree algorithm: ${algorithm}`)\n }\n for (const [name, policy] of [\n ['onEscapingLink', onEscapingLink],\n ['onMissingEntry', onMissingEntry],\n ] as const) {\n if (!ENTRY_POLICIES.has(policy)) {\n throw new Error(`unsupported workspace tree ${name} policy: ${policy}`)\n }\n }\n\n const root = resolve(directory)\n const rootStats = await lstatIfPresent(root)\n if (rootStats === null || !rootStats.isDirectory() || rootStats.isSymbolicLink()) {\n throw new Error(`workspace tree root must be a real directory: ${directory}`)\n }\n // Resolve the root once. Every physical containment test below compares against the RESOLVED\n // root, so a symlinked prefix — which is what macOS hands out for a temp directory — does not\n // make every entry look like it escapes.\n const physicalRoot = await realpath(root)\n\n const hash = createHash('sha256')\n // The algorithm is part of the digest. Without this, a tree whose modes already are 755/644\n // produces one digest under both algorithms, and two different questions share one answer.\n hash.update(`workspace-tree\\0${algorithm}\\0`)\n const counts = { files: 0, directories: 0, symlinks: 0, bytes: 0 }\n const excluded: WorkspaceTreeExclusion[] = []\n\n const exclude = (path: string, reason: WorkspaceTreeExclusionReason, target?: string): void => {\n const name = treePath(root, path)\n excluded.push(\n Object.freeze(target === undefined ? { path: name, reason } : { path: name, reason, target }),\n )\n // The reason is hashed with the entry. Two trees that differ only in WHY an entry was excluded\n // are different trees, and a refusal must not become a way to hide a difference.\n hash.update(`excluded\\0${name}\\0${reason}\\0${target ?? ''}\\0`)\n }\n\n /** True when the caller asked to continue past a vanished entry, and it was not the root. */\n const recordMissing = (path: string): boolean => {\n if (onMissingEntry === 'refuse' || path === root) return false\n exclude(path, 'entry-disappeared')\n return true\n }\n\n const visit = async (path: string): Promise<void> => {\n const info = await lstatIfPresent(path)\n if (info === null) {\n if (recordMissing(path)) return\n throw new Error(`workspace tree entry disappeared during the walk: ${treePath(root, path)}`)\n }\n const name = treePath(root, path)\n const mode = entryMode(info.mode, algorithm, info.isDirectory(), info.isSymbolicLink())\n\n if (info.isDirectory()) {\n let entries: string[]\n try {\n entries = await readdir(path)\n } catch (error) {\n if (isMissing(error) && recordMissing(path)) return\n throw error\n }\n counts.directories += 1\n hash.update(`directory\\0${name}\\0${mode}\\0`)\n for (const entry of entries.sort()) await visit(join(path, entry))\n return\n }\n\n if (info.isSymbolicLink()) {\n await visitSymbolicLink(path, name, mode)\n return\n }\n\n if (!info.isFile()) {\n throw new Error(`workspace tree contains an unsupported entry: ${name}`)\n }\n const read = await digestFile(path)\n if (read === null) {\n if (recordMissing(path)) return\n throw new Error(`workspace tree entry disappeared during the walk: ${name}`)\n }\n counts.files += 1\n counts.bytes += read.byteLength\n hash.update(`file\\0${name}\\0${mode}\\0${read.byteLength}\\0${read.sha256}\\0`)\n }\n\n const visitSymbolicLink = async (path: string, name: string, mode: string): Promise<void> => {\n let target: string\n try {\n target = await readlink(path)\n } catch (error) {\n if (isMissing(error) && recordMissing(path)) return\n throw error\n }\n const refuse = (reason: WorkspaceTreeExclusionReason): void => {\n if (onEscapingLink === 'refuse') {\n throw new Error(\n `workspace tree contains a link that ${reasonText(reason)}: ${name} -> ${target}`,\n )\n }\n exclude(path, reason, target)\n }\n if (isAbsolute(target)) return refuse('absolute-symlink')\n if (!isWithin(root, resolve(dirname(path), target))) return refuse('symlink-escapes-tree')\n let physicalTarget: string\n try {\n physicalTarget = await realpath(path)\n } catch {\n // A dangling link is a fact about the tree, not a reason to make the run unrecoverable.\n return refuse('unresolved-symlink')\n }\n if (!isWithin(physicalRoot, physicalTarget)) return refuse('symlink-resolves-outside-tree')\n counts.symlinks += 1\n hash.update(`symlink\\0${name}\\0${mode}\\0${target}\\0`)\n }\n\n await visit(root)\n return Object.freeze({\n algorithm,\n digest: `sha256:${hash.digest('hex')}` as Sha256Digest,\n files: counts.files,\n directories: counts.directories,\n symlinks: counts.symlinks,\n bytes: counts.bytes,\n excluded: Object.freeze(excluded),\n })\n}\n\n/**\n * Seed a workspace from a directory, one entry at a time, and return the digest of what was\n * seeded.\n *\n * Nothing is packed: `cp` walks and copies file by file, so a multi-gigabyte seed costs one file\n * handle rather than one archive in memory. Existing destination entries are never overwritten —\n * a seed that could replace a file the workspace already holds would make the resulting tree\n * depend on the order two seeds ran in.\n *\n * Links are copied verbatim, exactly as they were written. Following them would copy bytes from\n * outside the seed into the workspace, which is the same rule {@link describeWorkspaceTree}\n * applies to the digest.\n */\nexport async function seedWorkspaceTree(\n input: SeedWorkspaceTreeInput,\n): Promise<WorkspaceTreeDescriptor> {\n const source = resolve(input.source)\n const destination = resolve(input.destination)\n if (source === destination || isWithin(source, destination)) {\n throw new Error('workspace seed must not contain its destination')\n }\n const destinationStats = await lstatIfPresent(destination)\n if (destinationStats === null || !destinationStats.isDirectory()) {\n throw new Error(\n `workspace seed destination must be an existing directory: ${input.destination}`,\n )\n }\n // Described BEFORE anything is copied, so a seed this refuses leaves the destination untouched.\n const descriptor = await describeWorkspaceTree(source, {\n ...(input.algorithm === undefined ? {} : { algorithm: input.algorithm }),\n })\n for (const name of (await readdir(source)).sort()) {\n if ((await lstatIfPresent(join(destination, name))) !== null) {\n throw new Error(`workspace seed would overwrite an existing entry: ${name}`)\n }\n await cp(join(source, name), join(destination, name), {\n recursive: true,\n force: false,\n errorOnExist: true,\n verbatimSymlinks: true,\n })\n }\n return descriptor\n}\n\n/** Stream one regular file into its own digest, or `null` when it vanished mid-read. */\nasync function digestFile(\n path: string,\n): Promise<{ sha256: Sha256Digest; byteLength: number } | null> {\n let descriptor: Awaited<ReturnType<typeof open>>\n try {\n descriptor = await open(\n path,\n fsConstants.O_RDONLY |\n (typeof fsConstants.O_NOFOLLOW === 'number' ? fsConstants.O_NOFOLLOW : 0),\n )\n } catch (error) {\n if (isMissing(error)) return null\n throw error\n }\n try {\n const hash = createHash('sha256')\n const buffer = Buffer.allocUnsafe(1024 * 1024)\n let total = 0\n while (true) {\n const { bytesRead } = await descriptor.read(buffer, 0, buffer.byteLength, null)\n if (bytesRead === 0) break\n total += bytesRead\n hash.update(buffer.subarray(0, bytesRead))\n }\n // The length comes from the READ, never from `lstat`, so a file that grew between the two is\n // described by the bytes that reached the digest rather than refused for ordinary churn.\n return { sha256: `sha256:${hash.digest('hex')}` as Sha256Digest, byteLength: total }\n } finally {\n await descriptor.close()\n }\n}\n\n/** Git records exactly two file modes; a portable tree records the same two, plus 755 for a\n * directory and 777 for a link, which is what every filesystem reports for one. */\nfunction entryMode(\n mode: number | bigint,\n algorithm: WorkspaceTreeAlgorithm,\n isDirectory: boolean,\n isSymbolicLink: boolean,\n): string {\n const bits = Number(mode)\n if (algorithm !== 'portable-tree-v1') return String(bits & 0o777)\n if (isDirectory) return '755'\n if (isSymbolicLink) return '777'\n return (bits & 0o111) !== 0 ? '755' : '644'\n}\n\nfunction reasonText(reason: WorkspaceTreeExclusionReason): string {\n switch (reason) {\n case 'absolute-symlink':\n return 'is absolute'\n case 'symlink-escapes-tree':\n return 'escapes its tree'\n case 'symlink-resolves-outside-tree':\n return 'resolves outside its tree'\n default:\n return 'does not resolve'\n }\n}\n\nfunction treePath(root: string, path: string): string {\n return relative(root, path).split(sep).join('/') || '.'\n}\n\nfunction isWithin(root: string, path: string): boolean {\n return path === root || path.startsWith(`${root}${sep}`)\n}\n\nfunction isMissing(error: unknown): boolean {\n return (error as { code?: unknown } | null)?.code === 'ENOENT'\n}\n\nasync function lstatIfPresent(path: string): Promise<Awaited<ReturnType<typeof lstat>> | null> {\n try {\n return await lstat(path)\n } catch (error) {\n if (isMissing(error)) return null\n throw error\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["canonicalCandidateBytes","canonicalCandidateBytes","fsConstants"],"sources":["../../src/candidate-execution/builder.ts","../../src/candidate-execution/claim-file-store.ts","../../src/candidate-execution/dispose.ts","../../src/candidate-execution/protected-model-grant.ts","../../src/candidate-execution/recover.ts","../../src/candidate-execution/workspace-tree.ts"],"sourcesContent":["import type { CodeSurface } from '@tangle-network/agent-eval/campaign'\nimport { verifyCodeSurface } from '@tangle-network/agent-eval/campaign'\nimport type {\n AgentCandidateCodeDisabled,\n AgentCandidateCodeNoOp,\n AgentCandidateExecution,\n AgentCandidateGitHubRepository,\n AgentCandidateKnowledge,\n AgentCandidateMemoryPolicy,\n AgentCandidateProfile,\n AgentProfile,\n AgentProfileDiff,\n} from '@tangle-network/agent-interface'\nimport { type AgentCandidateBundleInput, sealAgentCandidateBundle } from './bundle'\nimport { embeddedCandidateArtifact } from './digest'\nimport {\n applyExactAgentProfileDiff,\n freezeGenericAgentCandidateProfile,\n parseExactAgentProfile,\n parseExactAgentProfileDiff,\n parseExactCandidateProfile,\n} from './profile'\n\n/** A complete profile that can be frozen without losing behavior. */\nexport type AgentCandidateProfileSource =\n | {\n kind: 'profile'\n profile: AgentProfile\n }\n | {\n kind: 'profile-diffs'\n base: AgentProfile\n /** Applied in order before the resulting profile is frozen into the bundle. */\n diffs: readonly AgentProfileDiff[]\n }\n | {\n kind: 'candidate-profile'\n /** Already converted to the closed, secret-free candidate profile contract. */\n profile: AgentCandidateProfile\n }\n\n/** The only accepted path from an agent-eval code candidate to executable bytes. */\nexport interface AgentCandidateCodeSurfaceSource {\n kind: 'code-surface'\n surface: CodeSurface\n repository: AgentCandidateGitHubRepository\n /** Optional parent directory used to resolve a relative `surface.worktreeRef`. */\n worktreeDir?: string\n}\n\n/** Explicit control/no-op code or one finalized CodeSurface whose bytes must still verify. */\nexport type AgentCandidateCodeSource =\n | AgentCandidateCodeDisabled\n | AgentCandidateCodeNoOp\n | AgentCandidateCodeSurfaceSource\n\n/** Complete measured surfaces and execution policy compiled into one candidate bundle. */\nexport interface BuildAgentCandidateBundleInput {\n profile: AgentCandidateProfileSource\n code: AgentCandidateCodeSource\n execution: AgentCandidateExecution\n knowledge?: AgentCandidateKnowledge\n memory: AgentCandidateMemoryPolicy\n}\n\n/**\n * Compile one measured profile/code candidate into the immutable execution\n * contract. Code bytes are re-read and verified by agent-eval before they are\n * embedded. The returned bundle is schema-validated, canonically digested, and\n * deeply immutable; call `verifyAgentCandidateBundle` at the execution boundary\n * to re-read external memory, repository, and workspace artifacts.\n */\nexport function buildAgentCandidateBundle(\n input: BuildAgentCandidateBundleInput,\n): ReturnType<typeof sealAgentCandidateBundle> {\n const bundle: AgentCandidateBundleInput = {\n kind: 'agent-candidate-bundle',\n digestAlgorithm: 'rfc8785-sha256',\n profile: compileCandidateProfile(input.profile),\n code: compileCandidateCode(input.code),\n execution: input.execution,\n ...(input.knowledge !== undefined ? { knowledge: input.knowledge } : {}),\n memory: input.memory,\n }\n return sealAgentCandidateBundle(bundle)\n}\n\nfunction compileCandidateProfile(source: AgentCandidateProfileSource): AgentCandidateProfile {\n if (source.kind === 'candidate-profile') {\n return parseExactCandidateProfile(source.profile)\n }\n\n if (source.kind === 'profile') {\n return freezeGenericAgentCandidateProfile(source.profile)\n }\n\n if (source.kind !== 'profile-diffs') {\n throw new Error(\n `unsupported candidate profile source: ${String((source as { kind?: unknown }).kind)}`,\n )\n }\n if (source.diffs.length === 0) {\n throw new Error('profile-diffs source requires at least one AgentProfileDiff')\n }\n let profile = parseExactAgentProfile(source.base, 'base profile')\n for (const [index, inputDiff] of source.diffs.entries()) {\n const diff = parseExactAgentProfileDiff(inputDiff, `profile diff ${index}`)\n profile = applyExactAgentProfileDiff(profile, diff, `profile diff ${index}`)\n }\n return freezeGenericAgentCandidateProfile(profile)\n}\n\nfunction compileCandidateCode(source: AgentCandidateCodeSource): AgentCandidateBundleInput['code'] {\n if (source.kind === 'disabled' || source.kind === 'no-op') return source\n if (source.kind !== 'code-surface') {\n throw new Error(\n `unsupported candidate code source: ${String((source as { kind?: unknown }).kind)}`,\n )\n }\n\n const verified = verifyCodeSurface(source.surface, source.worktreeDir)\n const patch = embeddedCandidateArtifact(verified.patchBytes)\n if (\n patch.sha256 !== source.surface.patch.sha256 ||\n patch.byteLength !== source.surface.patch.byteLength\n ) {\n throw new Error('verified CodeSurface patch bytes do not match its content identity')\n }\n return {\n kind: 'git-patch',\n repository: source.repository,\n baseCommit: source.surface.baseCommit,\n baseTree: source.surface.baseTree,\n candidateTree: source.surface.candidateTree,\n patch: { format: 'git-diff-binary', artifact: patch },\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport { linkSync } from 'node:fs'\nimport { mkdir, open, unlink } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { Sha256Digest } from '@tangle-network/agent-interface'\n\nimport {\n type AgentCandidateExecutionAttemptRecord,\n type AgentCandidateExecutionAttemptRef,\n type AgentCandidateExecutionClaim,\n type AgentCandidateExecutionClaimResult,\n type AgentCandidateExecutionClaimStore,\n type AgentCandidateExecutionFinishResult,\n type AgentCandidateExecutionLease,\n type AgentCandidateExecutionPhase,\n type AgentCandidateExecutionPhaseResult,\n type AgentCandidateExecutionRecoveryEvidence,\n type AgentCandidateExecutionStageResult,\n type AgentCandidateExecutionTerminalRecord,\n type AgentCandidateExecutionTerminalResult,\n type AgentCandidateRetryRejection,\n candidateClaimFileInternals,\n} from './claim'\nimport {\n assertRecoveryMatchesStaged,\n assertTerminalAllowedInPhase,\n assertTerminalMatchesClaim,\n assertTerminalMatchesStaged,\n recoveredTerminalRecord,\n rejectedFinish,\n rejectedStage,\n requireStagedTerminal,\n sealTerminalDigest,\n terminalRecord,\n} from './claim-terminal'\nimport { canonicalCandidateBytes } from './digest'\n\nconst {\n assertExpiredLease,\n assertLease,\n assertSameSlot,\n assertUnexpiredLease,\n attemptRecord,\n claimSlot,\n leaseDigest,\n newLease,\n readClaim,\n readClaimIfPresent,\n readTerminal,\n readTerminalIfPresent,\n readTransitionIfPresent,\n rejectedExistingClaim,\n rejectedRetry,\n retryRejection,\n sealAttemptRef,\n sealClaim,\n sealLease,\n} = candidateClaimFileInternals\n\nexport interface FileAgentCandidateExecutionClaimStoreOptions {\n /** Evaluator-owned directory shared by every process allowed to execute candidates. */\n directory: string\n /** Testable evaluator clock; defaults to `Date.now`. */\n now?: () => number\n}\n\n/** Cross-process lifecycle implemented as fsynced, create-if-absent records. */\nexport class FileAgentCandidateExecutionClaimStore implements AgentCandidateExecutionClaimStore {\n private readonly directory: string\n private readonly now: () => number\n\n constructor(options: FileAgentCandidateExecutionClaimStoreOptions) {\n if (options.directory.length === 0) {\n throw new Error('candidate execution claim directory must not be empty')\n }\n this.directory = options.directory\n this.now = options.now ?? Date.now\n }\n\n async tryClaim(\n requested: AgentCandidateExecutionClaim,\n ): Promise<AgentCandidateExecutionClaimResult> {\n const claim = sealClaim(requested)\n await mkdir(this.directory, { recursive: true })\n const claimPath = this.claimPath(claim)\n const existing = await readClaimIfPresent(claimPath)\n if (existing) {\n assertSameSlot(existing.claim, claim, claimPath)\n return rejectedExistingClaim(existing.claim, claim)\n }\n assertUnexpiredLease(claim.leaseExpiresAtMs, this.now())\n const retryFailure = await this.retryFailure(claim)\n if (retryFailure) return rejectedRetry(claim, retryFailure)\n\n const lease = newLease(claim)\n const acquired = await writeRecordIfAbsent(this.directory, claimPath, {\n ...claim,\n phase: 'claimed',\n leaseDigest: leaseDigest(lease),\n })\n if (acquired) return Object.freeze({ acquired: true, claim, lease })\n\n const winner = await readClaim(claimPath)\n assertSameSlot(winner.claim, claim, claimPath)\n return rejectedExistingClaim(winner.claim, claim)\n }\n\n async getAttempt(\n requestedAttempt: AgentCandidateExecutionAttemptRef,\n ): Promise<AgentCandidateExecutionAttemptRecord | undefined> {\n return await this.storedAttempt(sealAttemptRef(requestedAttempt))\n }\n\n async markCandidateMayRun(\n requestedLease: AgentCandidateExecutionLease,\n ): Promise<AgentCandidateExecutionPhaseResult> {\n const lease = sealLease(requestedLease)\n const claimPath = this.claimPath(lease)\n const stored = await readClaim(claimPath)\n assertSameSlot(stored.claim, lease, claimPath)\n assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease)\n const state = await this.transitionState(stored.claim)\n if (state.phase === 'candidate-may-run') {\n return Object.freeze({ marked: false, phase: 'candidate-may-run' })\n }\n if (state.staged) {\n throw new Error('candidate execution terminal was staged before candidate-may-run phase')\n }\n assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now())\n const marked = await writeRecordIfAbsent(\n this.directory,\n this.transitionPath(stored.claim, 1),\n {\n kind: 'candidate-execution-phase',\n executionId: stored.claim.executionId,\n attempt: stored.claim.attempt,\n executionPlanDigest: stored.claim.executionPlanDigest,\n phase: 'candidate-may-run',\n },\n this.ownerPublication(stored.claim),\n )\n if (marked) return Object.freeze({ marked: true, phase: 'candidate-may-run' })\n const winner = await this.transitionState(stored.claim)\n if (winner.phase === 'candidate-may-run') {\n return Object.freeze({ marked: false, phase: 'candidate-may-run' })\n }\n throw new Error('candidate execution terminal was staged before candidate-may-run phase')\n }\n\n async stageTerminal(\n requestedLease: AgentCandidateExecutionLease,\n result: AgentCandidateExecutionTerminalResult,\n ): Promise<AgentCandidateExecutionStageResult> {\n const lease = sealLease(requestedLease)\n const claimPath = this.claimPath(lease)\n const stored = await readClaim(claimPath)\n assertSameSlot(stored.claim, lease, claimPath)\n assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease)\n const terminal = terminalRecord(stored.claim, result)\n const state = await this.transitionState(stored.claim)\n if (state.staged) return rejectedStage(state.staged, terminal)\n assertTerminalAllowedInPhase(state.phase, terminal)\n assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now())\n const transition = state.phase === 'claimed' ? 1 : 2\n const staged = await writeRecordIfAbsent(\n this.directory,\n this.transitionPath(stored.claim, transition),\n {\n kind: 'candidate-execution-pending-terminal',\n terminal,\n },\n this.ownerPublication(stored.claim),\n )\n if (staged) return Object.freeze({ staged: true, terminal })\n const winner = await this.transitionState(stored.claim)\n if (!winner.staged) {\n throw new Error('candidate execution phase transition won while staging terminal')\n }\n return rejectedStage(winner.staged, terminal)\n }\n\n async finish(\n requestedLease: AgentCandidateExecutionLease,\n requestedTerminalDigest: Sha256Digest,\n ): Promise<AgentCandidateExecutionFinishResult> {\n const lease = sealLease(requestedLease)\n const terminalDigest = sealTerminalDigest(requestedTerminalDigest)\n const claimPath = this.claimPath(lease)\n const stored = await readClaim(claimPath)\n assertSameSlot(stored.claim, lease, claimPath)\n assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease)\n const state = await this.transitionState(stored.claim)\n const staged = requireStagedTerminal(state.staged, terminalDigest)\n const terminalPath = this.terminalPath(lease)\n const existing = await readTerminalIfPresent(terminalPath)\n if (existing) {\n assertTerminalMatchesClaim(existing, stored.claim, terminalPath)\n assertTerminalMatchesStaged(existing, staged, terminalPath)\n return rejectedFinish(existing, terminalDigest)\n }\n assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now())\n const finished = await writeRecordIfAbsent(\n this.directory,\n terminalPath,\n {\n terminal: staged,\n },\n this.ownerPublication(stored.claim),\n )\n if (finished) return Object.freeze({ finished: true, terminal: staged })\n const winner = await readTerminal(terminalPath)\n assertSameSlot(winner, lease, terminalPath)\n assertTerminalMatchesClaim(winner, stored.claim, terminalPath)\n assertTerminalMatchesStaged(winner, staged, terminalPath)\n return rejectedFinish(winner, terminalDigest)\n }\n\n async recoverExpired(\n requestedAttempt: AgentCandidateExecutionAttemptRef,\n evidence: AgentCandidateExecutionRecoveryEvidence,\n ): Promise<AgentCandidateExecutionFinishResult> {\n const attempt = sealAttemptRef(requestedAttempt)\n const record = await this.storedAttempt(attempt)\n if (!record) throw new Error('candidate execution recovery does not name an acquired attempt')\n const recovered = recoveredTerminalRecord(record.claim, record.phase, evidence)\n if (record.staged) assertRecoveryMatchesStaged(record.staged, recovered)\n const requestedDigest = record.staged?.terminalDigest ?? recovered.terminalDigest\n if (record.terminal) return rejectedFinish(record.terminal, requestedDigest)\n assertExpiredLease(record.claim.leaseExpiresAtMs, this.now())\n\n let staged = record.staged\n if (!staged) {\n const transition = record.phase === 'claimed' ? 1 : 2\n const didStage = await writeRecordIfAbsent(\n this.directory,\n this.transitionPath(record.claim, transition),\n {\n kind: 'candidate-execution-pending-terminal',\n terminal: recovered,\n },\n )\n if (didStage) {\n staged = recovered\n } else {\n const winner = await this.transitionState(record.claim)\n if (!winner.staged) {\n throw new Error(\n 'candidate execution recovery lost terminal staging to a phase transition',\n )\n }\n assertRecoveryMatchesStaged(winner.staged, recovered)\n staged = winner.staged\n }\n }\n\n const terminalPath = this.terminalPath(attempt)\n const didFinish = await writeRecordIfAbsent(this.directory, terminalPath, {\n terminal: staged,\n })\n if (didFinish) return Object.freeze({ finished: true, terminal: staged })\n const winner = await readTerminal(terminalPath)\n assertSameSlot(winner, attempt, terminalPath)\n assertTerminalMatchesClaim(winner, record.claim, terminalPath)\n assertTerminalMatchesStaged(winner, staged, terminalPath)\n return rejectedFinish(winner, staged.terminalDigest)\n }\n\n private async storedAttempt(\n claim: Pick<AgentCandidateExecutionClaim, 'executionId' | 'attempt'>,\n ): Promise<AgentCandidateExecutionAttemptRecord | undefined> {\n const claimPath = this.claimPath(claim)\n const stored = await readClaimIfPresent(claimPath)\n if (!stored) return undefined\n assertSameSlot(stored.claim, claim, claimPath)\n const terminalPath = this.terminalPath(claim)\n const terminal = await readTerminalIfPresent(terminalPath)\n // Terminal publication follows its immutable staged outbox, so read that prerequisite later.\n const state = await this.transitionState(stored.claim)\n if (terminal) {\n assertTerminalMatchesClaim(terminal, stored.claim, terminalPath)\n if (!state.staged) {\n throw new Error(\n `candidate execution terminal record at ${terminalPath} has no staged outbox`,\n )\n }\n assertTerminalMatchesStaged(terminal, state.staged, terminalPath)\n }\n return attemptRecord(stored.claim, state.phase, state.staged, terminal)\n }\n\n private async transitionState(claim: AgentCandidateExecutionClaim): Promise<{\n phase: AgentCandidateExecutionPhase\n staged?: AgentCandidateExecutionTerminalRecord\n }> {\n const firstPath = this.transitionPath(claim, 1)\n const first = await readTransitionIfPresent(firstPath, claim)\n if (!first) return { phase: 'claimed' }\n if (first.kind === 'pending') return { phase: 'claimed', staged: first.terminal }\n const secondPath = this.transitionPath(claim, 2)\n const second = await readTransitionIfPresent(secondPath, claim)\n if (!second) return { phase: 'candidate-may-run' }\n if (second.kind !== 'pending') {\n throw new Error(`candidate execution transition at ${secondPath} repeats the phase marker`)\n }\n return { phase: 'candidate-may-run', staged: second.terminal }\n }\n\n private async retryFailure(\n claim: AgentCandidateExecutionClaim,\n ): Promise<AgentCandidateRetryRejection | undefined> {\n if (claim.attempt === 1) return undefined\n return retryRejection(\n claim,\n await this.storedAttempt({\n executionId: claim.executionId,\n attempt: claim.attempt - 1,\n }),\n )\n }\n\n private ownerPublication(claim: AgentCandidateExecutionClaim): {\n authorizePublish: () => true\n } {\n return {\n authorizePublish: () => {\n assertUnexpiredLease(claim.leaseExpiresAtMs, this.now())\n return true\n },\n }\n }\n\n private claimPath(claim: Pick<AgentCandidateExecutionClaim, 'executionId' | 'attempt'>): string {\n return join(this.directory, `${claimSlot(claim)}.claim.json`)\n }\n\n private terminalPath(\n claim: Pick<AgentCandidateExecutionClaim, 'executionId' | 'attempt'>,\n ): string {\n return join(this.directory, `${claimSlot(claim)}.terminal.json`)\n }\n\n private transitionPath(\n claim: Pick<AgentCandidateExecutionClaim, 'executionId' | 'attempt'>,\n ordinal: 1 | 2,\n ): string {\n return join(this.directory, `${claimSlot(claim)}.transition-${ordinal}.json`)\n }\n}\n\n/**\n * Candidate records keep this async writer because lease authorization must run after the\n * asynchronous temp-file fsync and immediately before synchronous link publication. The\n * supervisor durability helper is synchronous and has no lease hook, so combining them would\n * either move authorization earlier or add an async race to the claim boundary.\n */\nasync function writeRecordIfAbsent(\n directory: string,\n destination: string,\n record: object,\n options: {\n /** Synchronous authorization checked after temp fsync, immediately before atomic publication. */\n authorizePublish?: () => true\n } = {},\n): Promise<boolean> {\n const temporaryPath = join(directory, `.candidate-execution-${process.pid}-${randomUUID()}.tmp`)\n const handle = await open(temporaryPath, 'wx', 0o600)\n try {\n await handle.writeFile(\n Buffer.concat([Buffer.from(canonicalCandidateBytes(record)), Buffer.from('\\n')]),\n )\n await handle.sync()\n } finally {\n await handle.close()\n }\n\n let written = false\n try {\n if (options.authorizePublish && options.authorizePublish() !== true) {\n throw new Error('candidate execution record publication was not authorized')\n }\n // Authorization and the atomic filesystem call are one synchronous critical\n // section so the event loop cannot advance an injected lease clock between them.\n linkSync(temporaryPath, destination)\n written = true\n await syncDirectory(directory)\n } catch (error) {\n if (!isNodeError(error, 'EEXIST')) throw error\n } finally {\n await unlink(temporaryPath).catch((error: unknown) => {\n if (!isNodeError(error, 'ENOENT')) throw error\n })\n }\n return written\n}\n\nasync function syncDirectory(directory: string): Promise<void> {\n const handle = await open(directory, 'r')\n try {\n await handle.sync()\n } finally {\n await handle.close()\n }\n}\n\nfunction isNodeError(error: unknown, code: string): boolean {\n return (\n error !== null &&\n typeof error === 'object' &&\n 'code' in error &&\n (error as { code?: unknown }).code === code\n )\n}\n","import { candidateCleanupDeadline, withinCandidateCleanupDeadline } from './cleanup'\nimport { sealAgentCandidateModelSettlement } from './model-settlement'\nimport {\n assertPreparedCandidateIntegrity,\n beginPreparedCandidateDisposal,\n consumePreparedCandidateExecution,\n} from './prepared-state'\nimport type { PreparedAgentCandidateExecution } from './types'\n\nexport interface DisposePreparedAgentCandidateOptions {\n cleanupTimeoutMs?: number\n}\n\n/** Revoke reservations held by a prepared candidate that will not be executed. */\nexport async function disposePreparedAgentCandidateExecution(\n prepared: PreparedAgentCandidateExecution,\n options: DisposePreparedAgentCandidateOptions = {},\n): Promise<{ disposed: true }> {\n const initialState = assertPreparedCandidateIntegrity(prepared)\n const cleanupTimeoutMs = options.cleanupTimeoutMs ?? initialState.cleanupTimeoutMs\n if (cleanupTimeoutMs > initialState.cleanupTimeoutMs) {\n throw new Error('disposal cleanup timeout exceeds the frozen preparation bound')\n }\n const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs)\n const state = beginPreparedCandidateDisposal(prepared)\n const cleanup: Array<Promise<unknown>> = []\n\n if (state.memory.mode === 'isolated') {\n const reservation = state.memoryReservation\n if (!reservation) throw new Error('isolated memory reservation is missing')\n cleanup.push(\n withinCandidateCleanupDeadline(\n async () => {\n const closed = await state.ports.memory.close({\n executionId: state.executionId,\n preparationId: reservation.preparationId,\n accessDigest: reservation.accessDigest,\n effectiveNamespace: reservation.effectiveNamespace,\n reason: 'abandoned',\n })\n if (closed.closed !== true || Object.keys(closed).some((key) => key !== 'closed')) {\n throw new Error('abandoned isolated memory access did not acknowledge closure')\n }\n },\n cleanupDeadlineAtMs,\n 'isolated memory disposal',\n ),\n )\n }\n\n cleanup.push(\n withinCandidateCleanupDeadline(\n async () => {\n const settlement = sealAgentCandidateModelSettlement(\n await state.ports.models.settleGrant({\n executionId: state.executionId,\n preparationId: state.preparationId,\n grantDigest: state.modelReservation.digest,\n resolved: state.resolvedModel,\n reason: 'abandoned',\n }),\n {\n preparationId: state.preparationId,\n grantDigest: state.modelReservation.digest,\n model: state.resolvedModel.model,\n },\n )\n if (settlement.usage.modelCalls !== 0) {\n throw new Error('unexecuted model reservation unexpectedly contains calls')\n }\n },\n cleanupDeadlineAtMs,\n 'model reservation disposal',\n ),\n )\n\n const results = await Promise.allSettled(cleanup)\n const failures = results\n .filter((result): result is PromiseRejectedResult => result.status === 'rejected')\n .map((result) => result.reason)\n if (failures.length > 0) {\n consumePreparedCandidateExecution(prepared, 'disposal-failed')\n throw new AggregateError(failures, 'candidate preparation disposal failed')\n }\n\n consumePreparedCandidateExecution(prepared, 'disposed')\n return Object.freeze({ disposed: true as const })\n}\n","import type { AgentCandidateResolvedModel } from '@tangle-network/agent-interface'\n\nimport type {\n AgentCandidateModelGrantActivateInput,\n AgentCandidateModelGrantReserveInput,\n AgentCandidateModelGrantSettleInput,\n} from './protected-model-port'\nimport type {\n AgentCandidateModelPort,\n AgentCandidateProtectedModelActivation,\n AgentCandidateProtectedModelReservation,\n AgentCandidateProtectedModelSettlement,\n} from './types'\n\n/** Reservation fields supplied by a caller before Runtime resolves the model. */\nexport type AgentCandidateModelGrantRunReservationInput = Omit<\n AgentCandidateModelGrantReserveInput,\n 'resolved'\n>\n\n/** Values available only while one protected model grant is active. */\nexport interface ProtectedAgentCandidateModelGrantContext {\n readonly activation: AgentCandidateProtectedModelActivation\n readonly reservation: AgentCandidateProtectedModelReservation\n readonly resolved: AgentCandidateResolvedModel\n}\n\n/** Inputs for one protected grant scoped to one bounded caller unit. */\nexport interface RunProtectedAgentCandidateModelGrantOptions<TResult> {\n /** Runtime port that validates and settles the evaluator-owned grant. */\n readonly port: AgentCandidateModelPort\n /** Provider-neutral model request resolved before any grant is reserved. */\n readonly resolve: Parameters<AgentCandidateModelPort['resolve']>[0]\n /** One bounded unit's immutable identity, attempt, expiry, and limits. */\n readonly reserve: AgentCandidateModelGrantRunReservationInput\n /** Must be no later than the reservation expiry. */\n readonly deadlineAtMs: AgentCandidateModelGrantActivateInput['deadlineAtMs']\n /** Execute exactly one bounded unit while the activated environment is valid. */\n readonly execute: (context: ProtectedAgentCandidateModelGrantContext) => Promise<TResult>\n}\n\n/** Result and sealed settlement returned after one protected grant closes. */\nexport interface RunProtectedAgentCandidateModelGrantResult<TResult> {\n readonly value: TResult\n readonly resolved: AgentCandidateResolvedModel\n readonly reservation: AgentCandidateProtectedModelReservation\n readonly settlement: AgentCandidateProtectedModelSettlement\n}\n\nconst SETTLEMENT_RETRY_INITIAL_DELAY_MS = 25\nconst SETTLEMENT_RETRY_MAX_DELAY_MS = 1_000\n\n/**\n * Run one bounded unit under a protected model grant.\n *\n * Runtime owns the grant lifecycle; callers own the unit boundary and any\n * durable scheduling or accounting around it. A reserved grant is settled\n * after activation failure or callback failure, and the callback error is\n * preserved when settlement also fails.\n */\nexport async function runProtectedAgentCandidateModelGrant<TResult>(\n options: RunProtectedAgentCandidateModelGrantOptions<TResult>,\n): Promise<RunProtectedAgentCandidateModelGrantResult<TResult>> {\n if (typeof options.execute !== 'function') {\n throw new TypeError('protected model grant execute callback is required')\n }\n\n const resolved = await options.port.resolve(options.resolve)\n const reservation = await options.port.reserveGrant({\n ...options.reserve,\n resolved,\n })\n\n let value!: TResult\n let executionError: unknown\n let executionFailed = false\n let activated = false\n let reason: AgentCandidateModelGrantSettleInput['reason'] = 'preparation-failed'\n\n try {\n const activation = await options.port.activateGrant({\n executionId: options.reserve.executionId,\n preparationId: options.reserve.preparationId,\n grantDigest: reservation.digest,\n resolved,\n deadlineAtMs: options.deadlineAtMs,\n })\n activated = true\n value = await options.execute({ activation, reservation, resolved })\n reason = 'completed'\n } catch (error) {\n executionError = error\n executionFailed = true\n if (activated) reason = 'failed'\n }\n\n let settlement: AgentCandidateProtectedModelSettlement\n try {\n settlement = await settleProtectedModelGrant(options, {\n executionId: options.reserve.executionId,\n preparationId: options.reserve.preparationId,\n grantDigest: reservation.digest,\n resolved,\n reason,\n })\n } catch (settlementError) {\n if (executionFailed) {\n throw new AggregateError(\n [executionError, settlementError],\n 'protected model execution failed and its grant did not settle',\n )\n }\n throw settlementError\n }\n\n if (executionFailed) throw executionError\n return { value, resolved, reservation, settlement }\n}\n\nasync function settleProtectedModelGrant<TResult>(\n options: RunProtectedAgentCandidateModelGrantOptions<TResult>,\n input: AgentCandidateModelGrantSettleInput,\n): Promise<AgentCandidateProtectedModelSettlement> {\n let delayMs = SETTLEMENT_RETRY_INITIAL_DELAY_MS\n for (;;) {\n try {\n return await options.port.settleGrant(input)\n } catch (error) {\n if (!isCandidateGrantDrainingError(error)) throw error\n const remainingMs = options.deadlineAtMs - Date.now()\n if (remainingMs <= 0) throw error\n await waitForSettlementRetry(Math.min(delayMs, remainingMs))\n if (Date.now() >= options.deadlineAtMs) throw error\n delayMs = Math.min(delayMs * 2, SETTLEMENT_RETRY_MAX_DELAY_MS)\n }\n }\n}\n\nfunction waitForSettlementRetry(delayMs: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, delayMs))\n}\n\n/** Only the gateway's explicit draining state is retryable; auth and ledger errors fail closed. */\nfunction isCandidateGrantDrainingError(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) return false\n const candidate = error as { code?: unknown; message?: unknown }\n if (candidate.code === 'candidate_grant_draining') return true\n return (\n typeof candidate.message === 'string' && /\\bcandidate_grant_draining\\b/u.test(candidate.message)\n )\n}\n","import type { TraceStore } from '@tangle-network/agent-eval'\nimport type {\n AgentCandidateExecutionPlanMaterial,\n AgentCandidateMaterializationReceipt,\n} from '@tangle-network/agent-interface'\nimport {\n agentCandidateExecutionPlanMaterialSchema,\n agentCandidateMaterializationReceiptSchema,\n} from '@tangle-network/agent-interface'\n\nimport { readVerifiedArtifact } from './artifacts'\nimport type {\n AgentCandidateExecutionAttemptRef,\n AgentCandidateExecutionClaim,\n AgentCandidateExecutionClaimStore,\n AgentCandidateExecutionFinishResult,\n} from './claim'\nimport {\n candidateCleanupDeadline,\n candidateCleanupTimeout,\n withinCandidateCleanupDeadline,\n} from './cleanup'\nimport { canonicalCandidateBytes } from './digest'\nimport { sealAgentCandidateExecutorStopAcknowledgement } from './executor-capture'\nimport { sealAgentCandidateModelSettlement } from './model-settlement'\nimport { persistCandidateModelSettlementEvidence } from './outcome-evidence'\nimport { persistCandidateOutputArtifact } from './output-artifacts'\nimport { RecoveryAgentCandidateTraceStore } from './protected-trace-store'\nimport type {\n AgentCandidateExecutionPorts,\n AgentCandidateExecutorPort,\n AgentCandidateOutputArtifactPort,\n} from './types'\n\nexport interface RecoverExpiredAgentCandidateOptions {\n attempt: AgentCandidateExecutionAttemptRef\n claimStore: AgentCandidateExecutionClaimStore\n executor: AgentCandidateExecutorPort\n traceStore: TraceStore\n ports: Pick<AgentCandidateExecutionPorts, 'models' | 'memory'>\n outputArtifacts: AgentCandidateOutputArtifactPort\n cleanupTimeoutMs?: number\n /** Evaluator clock; must be the same clock used by the claim store. */\n now?: () => number\n}\n\n/** Close an expired crashed attempt from persisted non-secret handles, then record failure. */\nexport async function recoverExpiredAgentCandidateExecution(\n options: RecoverExpiredAgentCandidateOptions,\n): Promise<AgentCandidateExecutionFinishResult> {\n const record = await options.claimStore.getAttempt(options.attempt)\n if (!record) throw new Error('candidate execution recovery attempt is missing')\n if (record.terminal) {\n return Object.freeze({ finished: false, terminal: record.terminal, exactReplay: true })\n }\n const cleanupTimeoutMs = candidateCleanupTimeout(\n options.cleanupTimeoutMs ?? record.claim.cleanup.cleanupTimeoutMs,\n )\n if (cleanupTimeoutMs > record.claim.cleanup.cleanupTimeoutMs) {\n throw new Error('recovery cleanup timeout exceeds the frozen preparation bound')\n }\n\n const now = options.now ?? Date.now\n if (now() < record.claim.leaseExpiresAtMs) {\n throw new Error('candidate execution lease has not expired')\n }\n\n const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs)\n const recoveryTraceStore = new RecoveryAgentCandidateTraceStore(options.traceStore)\n const preparation = withinCandidateCleanupDeadline(\n () => readRecoveryPreparation(record.claim, options.outputArtifacts),\n cleanupDeadlineAtMs,\n 'expired candidate preparation evidence read',\n )\n const processClosure = withinCandidateCleanupDeadline(\n async (cleanupSignal) => {\n const stopped = await options.executor.stop(\n {\n executionId: record.claim.executionId,\n executionPlanDigest: record.claim.executionPlanDigest,\n },\n {\n traceStore: recoveryTraceStore,\n reason: 'failed',\n signal: cleanupSignal,\n deadlineAtMs: cleanupDeadlineAtMs,\n },\n )\n sealAgentCandidateExecutorStopAcknowledgement(stopped)\n if (options.executor.dispose) {\n const disposed = await options.executor.dispose(\n {\n executionId: record.claim.executionId,\n executionPlanDigest: record.claim.executionPlanDigest,\n },\n { signal: cleanupSignal },\n )\n if (disposed.disposed !== true || Object.keys(disposed).some((key) => key !== 'disposed')) {\n throw new Error('expired candidate executor did not acknowledge resource disposal')\n }\n }\n return { stopped: true as const }\n },\n cleanupDeadlineAtMs,\n 'expired candidate process termination',\n )\n\n const modelClosure = withinCandidateCleanupDeadline(\n async () =>\n sealAgentCandidateModelSettlement(\n await options.ports.models.settleGrant({\n executionId: record.claim.executionId,\n preparationId: record.claim.cleanup.preparationId,\n grantDigest: record.claim.cleanup.modelGrantDigest,\n resolved: record.claim.cleanup.resolvedModel,\n reason: 'failed',\n }),\n {\n preparationId: record.claim.cleanup.preparationId,\n grantDigest: record.claim.cleanup.modelGrantDigest,\n model: record.claim.cleanup.resolvedModel.model,\n },\n ),\n cleanupDeadlineAtMs,\n 'expired candidate model settlement',\n )\n\n const memoryClosure = record.claim.cleanup.memory\n ? withinCandidateCleanupDeadline(\n async () => {\n const memory = record.claim.cleanup.memory\n if (!memory) throw new Error('expired candidate memory handle is missing')\n const closed = await options.ports.memory.close({\n executionId: record.claim.executionId,\n preparationId: record.claim.cleanup.preparationId,\n accessDigest: memory.accessDigest,\n effectiveNamespace: memory.effectiveNamespace,\n reason: 'failed',\n })\n if (closed.closed !== true || Object.keys(closed).some((key) => key !== 'closed')) {\n throw new Error('expired candidate memory did not acknowledge closure')\n }\n return { closed: true as const }\n },\n cleanupDeadlineAtMs,\n 'expired candidate memory closure',\n )\n : undefined\n\n const closureOutcomes = Promise.allSettled([\n modelClosure,\n memoryClosure ?? Promise.resolve(undefined),\n ] as const)\n const [preparationOutcome, processOutcome] = await Promise.allSettled([\n preparation,\n processClosure,\n ] as const)\n const failures: unknown[] = [preparationOutcome, processOutcome]\n .filter((outcome): outcome is PromiseRejectedResult => outcome.status === 'rejected')\n .map((outcome) => outcome.reason)\n const [modelOutcome, memoryOutcome] = await closureOutcomes\n for (const outcome of [modelOutcome, memoryOutcome]) {\n if (outcome.status === 'rejected') failures.push(outcome.reason)\n }\n if (failures.length > 0) {\n throw new AggregateError(failures, 'expired candidate cleanup could not be proven')\n }\n\n if (modelOutcome.status !== 'fulfilled') {\n throw new Error('expired candidate model settlement was not recovered')\n }\n const model = modelOutcome.value\n const modelSettlement = await withinCandidateCleanupDeadline(\n () =>\n persistCandidateModelSettlementEvidence(\n {\n executionId: record.claim.executionId,\n executionPlanDigest: record.claim.executionPlanDigest,\n resolvedModel: record.claim.cleanup.resolvedModel,\n },\n model,\n options.outputArtifacts,\n ),\n cleanupDeadlineAtMs,\n 'expired candidate model-settlement persistence',\n )\n const failureClass =\n record.phase === 'claimed' && model.usage.modelCalls === 0\n ? 'pre-model-infrastructure'\n : 'unknown'\n const failureEvidence = await withinCandidateCleanupDeadline(\n () =>\n persistCandidateOutputArtifact(options.outputArtifacts, {\n executionId: record.claim.executionId,\n purpose: 'failure-evidence',\n bytes: canonicalCandidateBytes({\n kind: 'agent-candidate-recovery-failure',\n executionId: record.claim.executionId,\n attempt: record.claim.attempt,\n bundleDigest: record.claim.bundleDigest,\n executionPlanDigest: record.claim.executionPlanDigest,\n materializationReceiptDigest:\n record.claim.preparationEvidence.materializationReceipt.sha256,\n failureClass,\n processStopped: true,\n modelSettlementDigest: modelSettlement.digest,\n memoryClosed: record.claim.cleanup.memory !== undefined,\n }),\n }),\n cleanupDeadlineAtMs,\n 'expired candidate failure-evidence persistence',\n )\n const memory = record.claim.cleanup.memory\n return await options.claimStore.recoverExpired(options.attempt, {\n failureClass,\n usage: model.usage,\n modelSettlement: modelSettlement.artifact,\n failureEvidence,\n process: {\n stopped: true,\n executionPlanDigest: record.claim.executionPlanDigest,\n },\n model: {\n closed: true,\n preparationId: record.claim.cleanup.preparationId,\n grantDigest: record.claim.cleanup.modelGrantDigest,\n },\n ...(memory\n ? {\n memory: {\n closed: true,\n preparationId: record.claim.cleanup.preparationId,\n accessDigest: memory.accessDigest,\n effectiveNamespace: memory.effectiveNamespace,\n },\n }\n : {}),\n })\n}\n\nasync function readRecoveryPreparation(\n claim: AgentCandidateExecutionClaim,\n artifacts: AgentCandidateOutputArtifactPort,\n): Promise<{\n plan: AgentCandidateExecutionPlanMaterial\n receipt: AgentCandidateMaterializationReceipt\n}> {\n const [planBytes, receiptBytes] = await Promise.all([\n readVerifiedArtifact(claim.preparationEvidence.executionPlan, artifacts),\n readVerifiedArtifact(claim.preparationEvidence.materializationReceipt, artifacts),\n ])\n const plan = agentCandidateExecutionPlanMaterialSchema.parse(\n parseCanonicalBytes(planBytes, 'plan'),\n )\n if (claim.preparationEvidence.executionPlan.sha256 !== claim.executionPlanDigest) {\n throw new Error('recovery execution plan artifact does not match the claimed digest')\n }\n const receiptMaterial = parseCanonicalBytes(receiptBytes, 'materialization receipt')\n const receipt = agentCandidateMaterializationReceiptSchema.parse({\n ...receiptMaterial,\n digest: claim.preparationEvidence.materializationReceipt.sha256,\n })\n if (\n receipt.bundleDigest !== claim.bundleDigest ||\n receipt.executionPlan.digest !== claim.executionPlanDigest ||\n plan.runCell.bundleDigest !== claim.bundleDigest ||\n plan.executionId !== claim.executionId\n ) {\n throw new Error('recovery preparation evidence does not match the durable claim')\n }\n return { plan, receipt }\n}\n\nfunction parseCanonicalBytes(bytes: Uint8Array, label: string): Record<string, unknown> {\n let parsed: unknown\n try {\n parsed = JSON.parse(Buffer.from(bytes).toString('utf8'))\n } catch (error) {\n throw new Error(`candidate recovery ${label} is not JSON`, { cause: error })\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`candidate recovery ${label} must be an object`)\n }\n if (!Buffer.from(canonicalCandidateBytes(parsed)).equals(Buffer.from(bytes))) {\n throw new Error(`candidate recovery ${label} bytes are not canonical`)\n }\n return parsed as Record<string, unknown>\n}\n","/**\n * A content digest over a DIRECTORY TREE, and the per-file seed that fills one.\n *\n * The question this answers is \"is this workspace the same workspace\" — the question every launch\n * and every close of a branchable run has to answer, about a tree that a live process is still\n * writing to. That is a different question from the one\n * {@link scanMaterializedWorkspaceManifest} answers, and the difference decides the policies here:\n *\n * - A candidate manifest describes ONE EXACT regular-file workspace. It refuses a symbolic link\n * outright, because `AgentCandidateWorkspaceManifestMaterial` has no representation for one, and\n * it refuses an entry that disappears, because a signed manifest describes a settled tree.\n * - A tree descriptor describes what a RUN PRODUCED. A python venv writes\n * `bin/python -> /usr/bin/python3` as a matter of course, and a work directory can lose an entry\n * between two directory reads. Refusing on either aborts the close, and a run that never wrote\n * its digest can never be branched from.\n *\n * So the two policies are the caller's, one for links and one for vanishing entries, and both\n * default to `'refuse'` — the strict reading, correct for an INPUT seed, where a link that leaves\n * the tree means the seed describes bytes it does not contain. `'exclude'` is the reading a close\n * needs: the entry is never followed, it is recorded, and the exclusion itself is hashed, so two\n * trees that differ only in an excluded entry cannot share a digest.\n *\n * NOTHING is held in memory. A file reaches the tree digest as its own streamed sha-256 plus its\n * length, so the largest file in the tree does not decide whether the tree can be described:\n * `readFile` refuses anything above 2 GiB with `ERR_FS_FILE_TOO_LARGE`, and a single multi-gigabyte\n * artifact is exactly what a run leaves behind.\n *\n * @module\n * @experimental\n */\n\nimport { createHash } from 'node:crypto'\nimport { constants as fsConstants } from 'node:fs'\nimport { cp, lstat, open, readdir, readlink, realpath } from 'node:fs/promises'\nimport { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'\nimport type { Sha256Digest } from '@tangle-network/agent-interface'\n\n/**\n * `'tree-v1'` records a file's exact permission bits. `'portable-tree-v1'` records the two modes\n * Git stores — `755` when any execute bit is set, `644` otherwise, and `755` for a directory — so a\n * digest survives a checkout whose umask differs. Both stamp the algorithm into the digest, so one\n * tree cannot produce the same digest under both.\n */\nexport type WorkspaceTreeAlgorithm = 'tree-v1' | 'portable-tree-v1'\n\n/** What a walk does with an entry it refuses to describe: fail, or record and continue. */\nexport type WorkspaceTreeEntryPolicy = 'refuse' | 'exclude'\n\n/** Why one entry contributed its name instead of its content. */\nexport type WorkspaceTreeExclusionReason =\n | 'absolute-symlink'\n | 'symlink-escapes-tree'\n | 'symlink-resolves-outside-tree'\n | 'unresolved-symlink'\n | 'entry-disappeared'\n\n/** One entry the walk recorded but did not describe. Reported rather than dropped: a digest that\n * silently omitted an entry would be a different digest with no way to tell why. */\nexport interface WorkspaceTreeExclusion {\n /** Tree-relative path, `/`-separated. */\n readonly path: string\n readonly reason: WorkspaceTreeExclusionReason\n /** The link target exactly as it was written, for a symbolic-link exclusion. */\n readonly target?: string\n}\n\nexport interface WorkspaceTreeDescriptor {\n readonly algorithm: WorkspaceTreeAlgorithm\n readonly digest: Sha256Digest\n readonly files: number\n readonly directories: number\n /** Links kept INSIDE the tree. An excluded link is counted in `excluded`, never here. */\n readonly symlinks: number\n /** Total described regular-file bytes. */\n readonly bytes: number\n readonly excluded: ReadonlyArray<WorkspaceTreeExclusion>\n}\n\nexport interface DescribeWorkspaceTreeOptions {\n /** Default `'tree-v1'`. */\n readonly algorithm?: WorkspaceTreeAlgorithm\n /**\n * A symbolic link that is absolute, leaves the tree lexically, resolves outside it, or does not\n * resolve at all. `'refuse'` (default) is correct for an input seed. `'exclude'` is correct for a\n * close-time walk over a tree a run wrote. The link is NEVER followed in either policy: an\n * escaping link must not contribute bytes from outside the tree to a content address.\n */\n readonly onEscapingLink?: WorkspaceTreeEntryPolicy\n /**\n * An entry that vanished between the directory read that named it and the walk that reached it.\n * `'refuse'` (default) is correct for a settled tree; `'exclude'` is correct for a workspace a\n * live process still writes to.\n */\n readonly onMissingEntry?: WorkspaceTreeEntryPolicy\n}\n\nexport interface SeedWorkspaceTreeInput {\n /** The tree to copy FROM. Described, then copied entry by entry. */\n readonly source: string\n /** The tree to copy INTO. Must exist, and must not lie inside `source`. */\n readonly destination: string\n /** Default `'tree-v1'`; decides only the digest this returns, never the bytes it writes. */\n readonly algorithm?: WorkspaceTreeAlgorithm\n}\n\nconst TREE_ALGORITHMS: ReadonlySet<string> = new Set<WorkspaceTreeAlgorithm>([\n 'tree-v1',\n 'portable-tree-v1',\n])\n\nconst ENTRY_POLICIES: ReadonlySet<string> = new Set<WorkspaceTreeEntryPolicy>(['refuse', 'exclude'])\n\n/**\n * Describe one directory tree by content, streaming every file.\n *\n * The digest covers, for every entry in sorted order: its kind, its tree-relative path, its mode\n * under the selected algorithm, and then — for a file, its length and its own sha-256; for a kept\n * link, its target; for an excluded entry, the reason and the target. A file's content therefore\n * never enters the tree hash directly, which is what removes any in-memory size ceiling: the tree\n * hash consumes 32 bytes per file however large the file is.\n *\n * A hard-linked regular file is described like any other regular file. Its content is what the\n * digest is about, and a package manager that links a store into `node_modules` is ordinary\n * content, not a reason to refuse a workspace.\n */\nexport async function describeWorkspaceTree(\n directory: string,\n options: DescribeWorkspaceTreeOptions = {},\n): Promise<WorkspaceTreeDescriptor> {\n const algorithm = options.algorithm ?? 'tree-v1'\n const onEscapingLink = options.onEscapingLink ?? 'refuse'\n const onMissingEntry = options.onMissingEntry ?? 'refuse'\n if (!TREE_ALGORITHMS.has(algorithm)) {\n throw new Error(`unsupported workspace tree algorithm: ${algorithm}`)\n }\n for (const [name, policy] of [\n ['onEscapingLink', onEscapingLink],\n ['onMissingEntry', onMissingEntry],\n ] as const) {\n if (!ENTRY_POLICIES.has(policy)) {\n throw new Error(`unsupported workspace tree ${name} policy: ${policy}`)\n }\n }\n\n const root = resolve(directory)\n const rootStats = await lstatIfPresent(root)\n if (rootStats === null || !rootStats.isDirectory() || rootStats.isSymbolicLink()) {\n throw new Error(`workspace tree root must be a real directory: ${directory}`)\n }\n // Resolve the root once. Every physical containment test below compares against the RESOLVED\n // root, so a symlinked prefix — which is what macOS hands out for a temp directory — does not\n // make every entry look like it escapes.\n const physicalRoot = await realpath(root)\n\n const hash = createHash('sha256')\n // The algorithm is part of the digest. Without this, a tree whose modes already are 755/644\n // produces one digest under both algorithms, and two different questions share one answer.\n hash.update(`workspace-tree\\0${algorithm}\\0`)\n const counts = { files: 0, directories: 0, symlinks: 0, bytes: 0 }\n const excluded: WorkspaceTreeExclusion[] = []\n\n const exclude = (path: string, reason: WorkspaceTreeExclusionReason, target?: string): void => {\n const name = treePath(root, path)\n excluded.push(\n Object.freeze(target === undefined ? { path: name, reason } : { path: name, reason, target }),\n )\n // The reason is hashed with the entry. Two trees that differ only in WHY an entry was excluded\n // are different trees, and a refusal must not become a way to hide a difference.\n hash.update(`excluded\\0${name}\\0${reason}\\0${target ?? ''}\\0`)\n }\n\n /** True when the caller asked to continue past a vanished entry, and it was not the root. */\n const recordMissing = (path: string): boolean => {\n if (onMissingEntry === 'refuse' || path === root) return false\n exclude(path, 'entry-disappeared')\n return true\n }\n\n const visit = async (path: string): Promise<void> => {\n const info = await lstatIfPresent(path)\n if (info === null) {\n if (recordMissing(path)) return\n throw new Error(`workspace tree entry disappeared during the walk: ${treePath(root, path)}`)\n }\n const name = treePath(root, path)\n const mode = entryMode(info.mode, algorithm, info.isDirectory(), info.isSymbolicLink())\n\n if (info.isDirectory()) {\n let entries: string[]\n try {\n entries = await readdir(path)\n } catch (error) {\n if (isMissing(error) && recordMissing(path)) return\n throw error\n }\n counts.directories += 1\n hash.update(`directory\\0${name}\\0${mode}\\0`)\n for (const entry of entries.sort()) await visit(join(path, entry))\n return\n }\n\n if (info.isSymbolicLink()) {\n await visitSymbolicLink(path, name, mode)\n return\n }\n\n if (!info.isFile()) {\n throw new Error(`workspace tree contains an unsupported entry: ${name}`)\n }\n const read = await digestFile(path)\n if (read === null) {\n if (recordMissing(path)) return\n throw new Error(`workspace tree entry disappeared during the walk: ${name}`)\n }\n counts.files += 1\n counts.bytes += read.byteLength\n hash.update(`file\\0${name}\\0${mode}\\0${read.byteLength}\\0${read.sha256}\\0`)\n }\n\n const visitSymbolicLink = async (path: string, name: string, mode: string): Promise<void> => {\n let target: string\n try {\n target = await readlink(path)\n } catch (error) {\n if (isMissing(error) && recordMissing(path)) return\n throw error\n }\n const refuse = (reason: WorkspaceTreeExclusionReason): void => {\n if (onEscapingLink === 'refuse') {\n throw new Error(\n `workspace tree contains a link that ${reasonText(reason)}: ${name} -> ${target}`,\n )\n }\n exclude(path, reason, target)\n }\n if (isAbsolute(target)) return refuse('absolute-symlink')\n if (!isWithin(root, resolve(dirname(path), target))) return refuse('symlink-escapes-tree')\n let physicalTarget: string\n try {\n physicalTarget = await realpath(path)\n } catch {\n // A dangling link is a fact about the tree, not a reason to make the run unrecoverable.\n return refuse('unresolved-symlink')\n }\n if (!isWithin(physicalRoot, physicalTarget)) return refuse('symlink-resolves-outside-tree')\n counts.symlinks += 1\n hash.update(`symlink\\0${name}\\0${mode}\\0${target}\\0`)\n }\n\n await visit(root)\n return Object.freeze({\n algorithm,\n digest: `sha256:${hash.digest('hex')}` as Sha256Digest,\n files: counts.files,\n directories: counts.directories,\n symlinks: counts.symlinks,\n bytes: counts.bytes,\n excluded: Object.freeze(excluded),\n })\n}\n\n/**\n * Seed a workspace from a directory, one entry at a time, and return the digest of what was\n * seeded.\n *\n * Nothing is packed: `cp` walks and copies file by file, so a multi-gigabyte seed costs one file\n * handle rather than one archive in memory. Existing destination entries are never overwritten —\n * a seed that could replace a file the workspace already holds would make the resulting tree\n * depend on the order two seeds ran in.\n *\n * Links are copied verbatim, exactly as they were written. Following them would copy bytes from\n * outside the seed into the workspace, which is the same rule {@link describeWorkspaceTree}\n * applies to the digest.\n */\nexport async function seedWorkspaceTree(\n input: SeedWorkspaceTreeInput,\n): Promise<WorkspaceTreeDescriptor> {\n const source = resolve(input.source)\n const destination = resolve(input.destination)\n if (source === destination || isWithin(source, destination)) {\n throw new Error('workspace seed must not contain its destination')\n }\n const destinationStats = await lstatIfPresent(destination)\n if (destinationStats === null || !destinationStats.isDirectory()) {\n throw new Error(\n `workspace seed destination must be an existing directory: ${input.destination}`,\n )\n }\n // Described BEFORE anything is copied, so a seed this refuses leaves the destination untouched.\n const descriptor = await describeWorkspaceTree(source, {\n ...(input.algorithm === undefined ? {} : { algorithm: input.algorithm }),\n })\n for (const name of (await readdir(source)).sort()) {\n if ((await lstatIfPresent(join(destination, name))) !== null) {\n throw new Error(`workspace seed would overwrite an existing entry: ${name}`)\n }\n await cp(join(source, name), join(destination, name), {\n recursive: true,\n force: false,\n errorOnExist: true,\n verbatimSymlinks: true,\n })\n }\n return descriptor\n}\n\n/** Stream one regular file into its own digest, or `null` when it vanished mid-read. */\nasync function digestFile(\n path: string,\n): Promise<{ sha256: Sha256Digest; byteLength: number } | null> {\n let descriptor: Awaited<ReturnType<typeof open>>\n try {\n descriptor = await open(\n path,\n fsConstants.O_RDONLY |\n (typeof fsConstants.O_NOFOLLOW === 'number' ? fsConstants.O_NOFOLLOW : 0),\n )\n } catch (error) {\n if (isMissing(error)) return null\n throw error\n }\n try {\n const hash = createHash('sha256')\n const buffer = Buffer.allocUnsafe(1024 * 1024)\n let total = 0\n while (true) {\n const { bytesRead } = await descriptor.read(buffer, 0, buffer.byteLength, null)\n if (bytesRead === 0) break\n total += bytesRead\n hash.update(buffer.subarray(0, bytesRead))\n }\n // The length comes from the READ, never from `lstat`, so a file that grew between the two is\n // described by the bytes that reached the digest rather than refused for ordinary churn.\n return { sha256: `sha256:${hash.digest('hex')}` as Sha256Digest, byteLength: total }\n } finally {\n await descriptor.close()\n }\n}\n\n/** Git records exactly two file modes; a portable tree records the same two, plus 755 for a\n * directory and 777 for a link, which is what every filesystem reports for one. */\nfunction entryMode(\n mode: number | bigint,\n algorithm: WorkspaceTreeAlgorithm,\n isDirectory: boolean,\n isSymbolicLink: boolean,\n): string {\n const bits = Number(mode)\n if (algorithm !== 'portable-tree-v1') return String(bits & 0o777)\n if (isDirectory) return '755'\n if (isSymbolicLink) return '777'\n return (bits & 0o111) !== 0 ? '755' : '644'\n}\n\nfunction reasonText(reason: WorkspaceTreeExclusionReason): string {\n switch (reason) {\n case 'absolute-symlink':\n return 'is absolute'\n case 'symlink-escapes-tree':\n return 'escapes its tree'\n case 'symlink-resolves-outside-tree':\n return 'resolves outside its tree'\n default:\n return 'does not resolve'\n }\n}\n\nfunction treePath(root: string, path: string): string {\n return relative(root, path).split(sep).join('/') || '.'\n}\n\nfunction isWithin(root: string, path: string): boolean {\n return path === root || path.startsWith(`${root}${sep}`)\n}\n\nfunction isMissing(error: unknown): boolean {\n return (error as { code?: unknown } | null)?.code === 'ENOENT'\n}\n\nasync function lstatIfPresent(path: string): Promise<Awaited<ReturnType<typeof lstat>> | null> {\n try {\n return await lstat(path)\n } catch (error) {\n if (isMissing(error)) return null\n throw error\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as workerTraceAnalysisStore, Bi as chargedTokens, Ci as detachedFrozen, Gi as promptCacheTokenClasses, Mt as assertValidBudget, Qt as createPeerMailbox, S as meterRuntimeOwnedProviderAttempt, W as resolveSpawnResourcePaths, Zi as unmeteredSpend, Zn as resolveRedactor, _t as FileResultBlobStore, a as pickBestDelivered, bt as InMemorySpawnJournal, di as providerAttemptEvidence, dt as createExecutorRegistry, ea as addResourceSpend, ei as prepareJsonlAppend, g as withDriverExecutor, kt as parseWorkerToolTraceArtifact, na as withBudgetResources, ni as writeAllBytes, nn as isLiveNodeStatus, o as runFinalizer, qt as createInbox, r as bestDelivered, rn as isTerminalNodeStatus, rt as freeSlots, s as runTree, ti as readCommittedJsonLines, tt as errMessage, vt as FileSpawnJournal, wn as runBrainLoop, yt as InMemoryResultBlobStore, z as scopeRetainedOwnerResourceReader } from "./supervisor-yavsoQQo.js";
|
|
2
2
|
import { f as RuntimeRunStateError, m as ValidationError } from "./errors-DodWX-cb.js";
|
|
3
3
|
import { A as writeWorkerCancellation, M as writeWorkerSteerAcknowledgement, b as workerCancellationsDir, c as readWorkerCancelRequests, d as readWorkerSteerRequests, k as writeRunCancellation, l as readWorkerCancellation, o as readRunCancelRequest, r as claimWorkerSteerDelivery, s as readRunCancellation, u as readWorkerSteerAcknowledgement } from "./run-layout-vwd4SwX_.js";
|
|
4
4
|
import { agentProfileSchema, canonicalCandidateDigest } from "@tangle-network/agent-interface";
|
|
@@ -713,6 +713,33 @@ function spawnProfileArg() {
|
|
|
713
713
|
}).properties));
|
|
714
714
|
return spawnProfileArgCache;
|
|
715
715
|
}
|
|
716
|
+
/** `spawn_worker`'s `profile` argument when the run has a profiles table: one of the table's names,
|
|
717
|
+
* listed with each entry's description and whether a sealed verdict promoted it, or a profile the
|
|
718
|
+
* manager authors. Without a table the argument is the authored shape alone, byte for byte. */
|
|
719
|
+
function spawnProfileArgWithTable(profiles) {
|
|
720
|
+
const menu = [...profiles].map(([name, entry]) => {
|
|
721
|
+
return `- '${name}'${typeof entry.profile.description === "string" && entry.profile.description.length > 0 ? ` — ${entry.profile.description}` : ""} (${entry.promotion ? `promoted by sealed play ${JSON.stringify(entry.promotion.experiment.spec.id)}` : "exploratory"})`;
|
|
722
|
+
});
|
|
723
|
+
return { anyOf: [{
|
|
724
|
+
type: "string",
|
|
725
|
+
enum: [...profiles.keys()],
|
|
726
|
+
description: `The name of a profile in this run's profiles table. The runtime runs that exact profile, so do not retype it; other profile fields cannot be added to a name. Promoted entries beat their control in a sealed comparison; exploratory ones have no verdict yet.\n${menu.join("\n")}`
|
|
727
|
+
}, spawnProfileArg()] };
|
|
728
|
+
}
|
|
729
|
+
/** Why `spawn_worker` cannot use this `profile` argument against the profiles table, or
|
|
730
|
+
* `undefined` when it can. A name must be in the table. An authored profile must not take a table
|
|
731
|
+
* name: continuity and every per-name record key on `profile.name`, so one name must mean one
|
|
732
|
+
* profile. Without a table nothing is refused here, and the canonical schema answers as before. */
|
|
733
|
+
function profileTableRefusal(table, profile) {
|
|
734
|
+
if (table === void 0) return void 0;
|
|
735
|
+
if (typeof profile === "string") {
|
|
736
|
+
if (table.has(profile)) return void 0;
|
|
737
|
+
return `profile ${JSON.stringify(profile)} is not in this run's profiles table, which holds ${[...table.keys()].map((name) => JSON.stringify(name)).join(", ")}`;
|
|
738
|
+
}
|
|
739
|
+
const name = typeof profile === "object" && profile !== null ? profile.name : void 0;
|
|
740
|
+
if (typeof name !== "string" || !table.has(name)) return void 0;
|
|
741
|
+
return `profile.name ${JSON.stringify(name)} is a profiles-table entry: pass profile: ${JSON.stringify(name)} to run that exact profile, or give the profile you wrote another name`;
|
|
742
|
+
}
|
|
716
743
|
const BUDGET_FIELD = {
|
|
717
744
|
tokens: "maxTokens",
|
|
718
745
|
iterations: "maxIterations",
|
|
@@ -763,6 +790,7 @@ function createCoordinationTools(opts) {
|
|
|
763
790
|
/** Internal manager binding; its lifetime fences delivery without cancelling the worker scope. */
|
|
764
791
|
function createCoordinationToolsForManager(opts, lifetime) {
|
|
765
792
|
const deliverable = opts.deliverable;
|
|
793
|
+
const profileTable = opts.profiles !== void 0 && opts.profiles.size > 0 ? opts.profiles : void 0;
|
|
766
794
|
const readWorkerOutput = createWorkerOutputReader(opts.blobs);
|
|
767
795
|
const priorSubmission = deliverable === void 0 ? void 0 : opts.priorJournal?.find((record) => record.event.type === "submission");
|
|
768
796
|
let stopped = priorSubmission !== void 0;
|
|
@@ -1756,11 +1784,11 @@ function createCoordinationToolsForManager(opts, lifetime) {
|
|
|
1756
1784
|
const tools = [
|
|
1757
1785
|
{
|
|
1758
1786
|
name: "spawn_worker",
|
|
1759
|
-
description: "Start a worker the driver will drive. `profile` is the worker or another driver; `task` is what it should do. Reserves budget from the conserved pool and fails closed. Pass an optional `budget` (per-field) to give a hard sub-task more than the default — it merges over the per-worker default; the conserved pool is still the hard fence. When a max-live-workers cap is set it also fails closed (`error: \"max-live-workers\"`) while that many workers are still in flight — settle or steer one before spawning another. Pass a `key` naming the assignment to make it run-once ACROSS restarts: a key that already completed returns the finished result (`resumed: \"completed\"` — no work re-runs, nothing is spent), a key whose prior attempt failed (`down`) spawns fresh and says so (`resumed: \"retried\"`), and a key with no terminal receipt is refused (`error: \"in-doubt\"`) until its exact prior execution is recovered. A key still running is refused (`error: \"duplicate-key\"`). Returns `freeSlots`: how many MORE workers you can start right now (`null` = uncapped). While `freeSlots > 0` there is idle capacity — call this again to fill it rather than waiting; parallel workers finish the run sooner than one at a time.",
|
|
1787
|
+
description: "Start a worker the driver will drive. `profile` is the worker or another driver; `task` is what it should do. Reserves budget from the conserved pool and fails closed. Pass an optional `budget` (per-field) to give a hard sub-task more than the default — it merges over the per-worker default; the conserved pool is still the hard fence. When a max-live-workers cap is set it also fails closed (`error: \"max-live-workers\"`) while that many workers are still in flight — settle or steer one before spawning another. Pass a `key` naming the assignment to make it run-once ACROSS restarts: a key that already completed returns the finished result (`resumed: \"completed\"` — no work re-runs, nothing is spent), a key whose prior attempt failed (`down`) spawns fresh and says so (`resumed: \"retried\"`), and a key with no terminal receipt is refused (`error: \"in-doubt\"`) until its exact prior execution is recovered. A key still running is refused (`error: \"duplicate-key\"`). Returns `freeSlots`: how many MORE workers you can start right now (`null` = uncapped). While `freeSlots > 0` there is idle capacity — call this again to fill it rather than waiting; parallel workers finish the run sooner than one at a time." + (profileTable === void 0 ? "" : " This run has a profiles table: pass `profile` as one of the table's names to run that exact profile, or write a new profile under a name the table does not hold."),
|
|
1760
1788
|
inputSchema: {
|
|
1761
1789
|
type: "object",
|
|
1762
1790
|
properties: {
|
|
1763
|
-
profile: spawnProfileArg(),
|
|
1791
|
+
profile: profileTable === void 0 ? spawnProfileArg() : spawnProfileArgWithTable(profileTable),
|
|
1764
1792
|
task: { description: "The task the worker should perform." },
|
|
1765
1793
|
label: {
|
|
1766
1794
|
type: "string",
|
|
@@ -1775,6 +1803,10 @@ function createCoordinationToolsForManager(opts, lifetime) {
|
|
|
1775
1803
|
enum: ["fresh", "resume"],
|
|
1776
1804
|
description: "How this spawn continues the node's prior work. \"fresh\" (the default) starts a brand-new session. \"resume\" re-attaches to the node's most recent SETTLED worker: a NEW live worker is spawned whose session continues where that worker stopped (the backend receives the prior workerId and the resume sequence). Resume fails closed when the node has no settled prior worker (`error: \"resume-no-prior\"` — spawn it fresh first), while a prior worker of the node is still live (`error: \"resume-while-live\"` — steer_agent is the live-worker channel), and under a `key` (`error: \"resume-with-key\"` — keys are run-once, resume runs again). Omit to use the run's declared default for this profile name."
|
|
1777
1805
|
},
|
|
1806
|
+
successorOf: {
|
|
1807
|
+
type: "string",
|
|
1808
|
+
description: "The workerId of a SETTLED worker you spawned that this new worker replaces — for example one that failed, stalled, or finished with a weak result, restarted with a changed profile or task. The run record then names the predecessor on the new worker. Refused for an id you did not spawn (`error: \"successor-unknown\"`) and for a worker still live (`error: \"successor-live\"` — await_event until it settles). Put what the successor needs from its predecessor in `task`."
|
|
1809
|
+
},
|
|
1778
1810
|
budget: {
|
|
1779
1811
|
type: "object",
|
|
1780
1812
|
description: "Optional per-spawn budget that merges over the per-worker default (per field). Only set the ceilings this sub-task needs raised; the conserved pool still fences.",
|
|
@@ -1827,7 +1859,21 @@ function createCoordinationToolsForManager(opts, lifetime) {
|
|
|
1827
1859
|
live,
|
|
1828
1860
|
freeSlots: freeWorkerSlots()
|
|
1829
1861
|
}))(liveWorkerCount()));
|
|
1830
|
-
const
|
|
1862
|
+
const tableRefusal = profileTableRefusal(profileTable, a.profile);
|
|
1863
|
+
if (tableRefusal !== void 0) return {
|
|
1864
|
+
error: "invalid-profile",
|
|
1865
|
+
reason: tableRefusal,
|
|
1866
|
+
issues: [{
|
|
1867
|
+
path: "profile",
|
|
1868
|
+
message: tableRefusal
|
|
1869
|
+
}]
|
|
1870
|
+
};
|
|
1871
|
+
const tableEntry = typeof a.profile === "string" ? profileTable?.get(a.profile) : void 0;
|
|
1872
|
+
const resourcePaths = tableEntry === void 0 ? await resolveSpawnResourcePaths(a.profile, opts.spawnResourceReader ?? opts.spawnResourceRoot) : {
|
|
1873
|
+
ok: true,
|
|
1874
|
+
profile: tableEntry.profile,
|
|
1875
|
+
resolved: []
|
|
1876
|
+
};
|
|
1831
1877
|
if (!resourcePaths.ok) return {
|
|
1832
1878
|
error: "invalid-profile",
|
|
1833
1879
|
reason: `resources.${resourcePaths.at}: ${resourcePaths.reason}`,
|
|
@@ -1854,6 +1900,22 @@ function createCoordinationToolsForManager(opts, lifetime) {
|
|
|
1854
1900
|
live: liveWorkerCount(),
|
|
1855
1901
|
freeSlots: freeWorkerSlots()
|
|
1856
1902
|
});
|
|
1903
|
+
const successorOf = a.successorOf === void 0 ? void 0 : str(a.successorOf, "successorOf");
|
|
1904
|
+
if (successorOf !== void 0) {
|
|
1905
|
+
const predecessor = nodeForWorker(successorOf);
|
|
1906
|
+
if (predecessor === void 0 || predecessor.parent !== opts.scope.view.root) return {
|
|
1907
|
+
error: "successor-unknown",
|
|
1908
|
+
reason: `'${successorOf}' is not a worker this manager spawned — successorOf takes a workerId from one of your own spawn_worker results`,
|
|
1909
|
+
live: liveWorkerCount(),
|
|
1910
|
+
freeSlots: freeWorkerSlots()
|
|
1911
|
+
};
|
|
1912
|
+
if (isLiveNodeStatus(predecessor.status)) return {
|
|
1913
|
+
error: "successor-live",
|
|
1914
|
+
reason: `worker '${successorOf}' has not settled — await_event until it does, then spawn its successor (steer_agent redirects a live worker instead)`,
|
|
1915
|
+
live: liveWorkerCount(),
|
|
1916
|
+
freeSlots: freeWorkerSlots()
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1857
1919
|
const task = detachedFrozen(a.task);
|
|
1858
1920
|
const label = typeof a.label === "string" ? a.label : "worker";
|
|
1859
1921
|
if (opts.preflightSpawn) {
|
|
@@ -1887,13 +1949,15 @@ function createCoordinationToolsForManager(opts, lifetime) {
|
|
|
1887
1949
|
...key !== void 0 ? { key } : {},
|
|
1888
1950
|
continuity: continuity.continuity,
|
|
1889
1951
|
...continuity.continuity === "resume" ? { resume: continuity.resume } : {},
|
|
1952
|
+
...successorOf !== void 0 ? { successorOf } : {},
|
|
1890
1953
|
...peerMailUrl !== void 0 ? { peerMailUrl } : {}
|
|
1891
1954
|
});
|
|
1892
1955
|
const res = opts.scope.spawn(() => opts.makeWorkerAgent(profile, context), task, {
|
|
1893
1956
|
budget,
|
|
1894
1957
|
label,
|
|
1895
1958
|
assignmentId,
|
|
1896
|
-
...key !== void 0 ? { key } : {}
|
|
1959
|
+
...key !== void 0 ? { key } : {},
|
|
1960
|
+
...successorOf !== void 0 ? { successorOf } : {}
|
|
1897
1961
|
});
|
|
1898
1962
|
if (res.ok && res.prior?.state === "completed") {
|
|
1899
1963
|
const s = res.prior.settled;
|
|
@@ -1932,6 +1996,7 @@ function createCoordinationToolsForManager(opts, lifetime) {
|
|
|
1932
1996
|
...res.handle.executionBindings === void 0 ? {} : { executionBindings: res.handle.executionBindings },
|
|
1933
1997
|
continuity: continuity.continuity,
|
|
1934
1998
|
...continuity.continuity === "resume" ? { resume: continuity.resume } : {},
|
|
1999
|
+
...successorOf !== void 0 ? { successorOf } : {},
|
|
1935
2000
|
live: liveWorkerCount(),
|
|
1936
2001
|
freeSlots: freeWorkerSlots(),
|
|
1937
2002
|
...priorHistory,
|
|
@@ -3273,13 +3338,13 @@ function formatOverspend(violation) {
|
|
|
3273
3338
|
* (can't afford a worker) and usd (a usd-capped pool whose ceiling the driver's own metered
|
|
3274
3339
|
* inference has drained — `meter` debits usd, so without this a huge-token/small-usd pool would
|
|
3275
3340
|
* overspend usd up to the turn tripwire). */
|
|
3276
|
-
function poolStarved(scope, perWorker) {
|
|
3341
|
+
function poolStarved(scope, perWorker, preserveOwnerTurns = false) {
|
|
3277
3342
|
const b = scope.budget;
|
|
3278
3343
|
if (scope.view.inFlight > 0 || scope.view.waiting > 0) return false;
|
|
3279
|
-
const tokenStarved = b.tokensLeft < perWorker.maxTokens;
|
|
3344
|
+
const tokenStarved = preserveOwnerTurns ? b.tokensLeft <= 0 : b.tokensLeft < perWorker.maxTokens;
|
|
3280
3345
|
const iterationStarved = b.iterationsLeft <= 0;
|
|
3281
|
-
const usdStarved = b.usdCapped && (b.usdLeft <= 0 || perWorker.maxUsd !== void 0 && b.usdLeft < perWorker.maxUsd);
|
|
3282
|
-
const resourceStarved = Object.entries(b.resources ?? {}).some(([name, value]) => !value.known || value.remaining <= 0 || value.remaining < (perWorker.resources?.[name]?.limit ?? 0));
|
|
3346
|
+
const usdStarved = b.usdCapped && (b.usdLeft <= 0 || !preserveOwnerTurns && perWorker.maxUsd !== void 0 && b.usdLeft < perWorker.maxUsd);
|
|
3347
|
+
const resourceStarved = Object.entries(b.resources ?? {}).some(([name, value]) => !value.known || value.remaining <= 0 || !preserveOwnerTurns && value.remaining < (perWorker.resources?.[name]?.limit ?? 0));
|
|
3283
3348
|
return tokenStarved || iterationStarved || usdStarved || resourceStarved;
|
|
3284
3349
|
}
|
|
3285
3350
|
/** The absolute wall-clock deadline (when the root set one) has passed. */
|
|
@@ -3697,6 +3762,7 @@ function driverAgent(opts) {
|
|
|
3697
3762
|
...opts.preflightSpawn ? { preflightSpawn: opts.preflightSpawn } : {},
|
|
3698
3763
|
...opts.resolveSpawnProfile ? { resolveSpawnProfile: opts.resolveSpawnProfile } : {},
|
|
3699
3764
|
...opts.composeSpawnProfile ? { composeSpawnProfile: opts.composeSpawnProfile } : {},
|
|
3765
|
+
...opts.profiles ? { profiles: opts.profiles } : {},
|
|
3700
3766
|
...opts.spawnResourceRoot ? { spawnResourceRoot: opts.spawnResourceRoot } : {},
|
|
3701
3767
|
...ownerReader ? { spawnResourceReader: ownerReader } : {},
|
|
3702
3768
|
...opts.escalateQuestion ? { escalateQuestion: opts.escalateQuestion } : {},
|
|
@@ -3905,7 +3971,7 @@ function driverAgent(opts) {
|
|
|
3905
3971
|
});
|
|
3906
3972
|
},
|
|
3907
3973
|
stopBefore: () => {
|
|
3908
|
-
if (coord.isStopped() || scope.signal.aborted || poolStarved(scope, opts.perWorker) || deadlinePassed(scope, now)) return true;
|
|
3974
|
+
if (coord.isStopped() || scope.signal.aborted || poolStarved(scope, opts.perWorker, opts.preserveOwnerTurns) || deadlinePassed(scope, now)) return true;
|
|
3909
3975
|
if (!opts.stopRule || !tracker) return false;
|
|
3910
3976
|
const decision = progressStop(tracker, opts.stopRule, coord, scope, now, opts.stallAfterMs);
|
|
3911
3977
|
if (!decision.stop) return false;
|
|
@@ -4036,4 +4102,4 @@ function safeJson(v) {
|
|
|
4036
4102
|
//#endregion
|
|
4037
4103
|
export { downMessageRefusalReasons as A, ANALYST_DEFINITION_BOUNDS as C, coordinationVerbNames as D, canonicalFindingEvent as E, defaultToolDetectors as F, watchTrace as I, parseAuthoredAnalystDefinition as M, questionEscalationTargets as N, createCoordinationTools as O, createEventBus as P, watchRunCancellation as S, analystToolGroupNames as T, renderAnytimeTable as _, allOf as a, FileCoordinationLog as b, createProgressTracker as c, progressStop as d, sampleFromSettled as f, plateauLength as g, bestSoFar as h, finalizeBestDelivered as i, normalizeAnalyzeOnSettle as j, createCoordinationToolsForManager as k, noProgressFor as l, areaUnderCurve as m, createSteerAcknowledger as n, allWorkersStalled as o, anytimeReport as p, driverAgent as r, anyOf as s, createCancelAcknowledger as t, plateau as u, createFileRunContext as v, DEFAULT_AWAIT_EVENT_TIMEOUT_MS as w, applyRunCancellation as x, createInMemoryRunContext as y };
|
|
4038
4104
|
|
|
4039
|
-
//# sourceMappingURL=coordination-driver-
|
|
4105
|
+
//# sourceMappingURL=coordination-driver-LmDxHvAO.js.map
|