diff --git a/docs/api/index.md b/docs/api/index.md index 86ca200b..d1b23f1e 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -1776,7 +1776,7 @@ Defined in: [candidate-execution/dispose.ts:11](https://github.com/tangle-networ ### ExecutePreparedAgentCandidateOptions -Defined in: [candidate-execution/execute.ts:60](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L60) +Defined in: [candidate-execution/execute.ts:61](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L61) #### Properties @@ -1784,31 +1784,31 @@ Defined in: [candidate-execution/execute.ts:60](https://github.com/tangle-networ > **executor**: [`AgentCandidateExecutorPort`](#agentcandidateexecutorport) -Defined in: [candidate-execution/execute.ts:61](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L61) +Defined in: [candidate-execution/execute.ts:62](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L62) ##### grader > **grader**: [`AgentCandidateBenchmarkGraderPort`](#agentcandidatebenchmarkgraderport) -Defined in: [candidate-execution/execute.ts:62](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L62) +Defined in: [candidate-execution/execute.ts:63](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L63) ##### outputArtifacts > **outputArtifacts**: [`AgentCandidateOutputArtifactPort`](#agentcandidateoutputartifactport) -Defined in: [candidate-execution/execute.ts:63](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L63) +Defined in: [candidate-execution/execute.ts:64](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L64) ##### traceStore > **traceStore**: `TraceStore` -Defined in: [candidate-execution/execute.ts:64](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L64) +Defined in: [candidate-execution/execute.ts:65](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L65) ##### claimStore > **claimStore**: [`AgentCandidateExecutionClaimStore`](#agentcandidateexecutionclaimstore) -Defined in: [candidate-execution/execute.ts:66](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L66) +Defined in: [candidate-execution/execute.ts:67](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L67) Long-lived evaluator-owned store shared by every process that can run this benchmark. @@ -1816,7 +1816,7 @@ Long-lived evaluator-owned store shared by every process that can run this bench > `optional` **cleanupTimeoutMs?**: `number` -Defined in: [candidate-execution/execute.ts:68](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L68) +Defined in: [candidate-execution/execute.ts:69](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L69) Maximum time to prove process death and revoke protected access after a run ends. @@ -1824,7 +1824,7 @@ Maximum time to prove process death and revoke protected access after a run ends > `optional` **resultTimeoutMs?**: `number` -Defined in: [candidate-execution/execute.ts:70](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L70) +Defined in: [candidate-execution/execute.ts:71](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L71) Maximum time for task verification, executable grading, and receipt construction. @@ -1832,7 +1832,7 @@ Maximum time for task verification, executable grading, and receipt construction ### PrepareAgentCandidateExecutionOptions -Defined in: [candidate-execution/prepare.ts:88](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/prepare.ts#L88) +Defined in: [candidate-execution/prepare.ts:90](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/prepare.ts#L90) #### Properties @@ -1840,18 +1840,201 @@ Defined in: [candidate-execution/prepare.ts:88](https://github.com/tangle-networ > `optional` **cleanupTimeoutMs?**: `number` -Defined in: [candidate-execution/prepare.ts:89](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/prepare.ts#L89) +Defined in: [candidate-execution/prepare.ts:91](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/prepare.ts#L91) ##### resultTimeoutMs? > `optional` **resultTimeoutMs?**: `number` -Defined in: [candidate-execution/prepare.ts:91](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/prepare.ts#L91) +Defined in: [candidate-execution/prepare.ts:93](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/prepare.ts#L93) Maximum time for task verification, executable grading, and receipt construction. *** +### AgentCandidateModelGrantClient + +Defined in: [candidate-execution/protected-model-port.ts:39](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L39) + +Narrow transport contract for a service that owns scoped model credentials +and the authoritative per-call usage ledger. + +An HTTP client can bind these methods to control-plane endpoints. Keeping +transport out of the runtime prevents parent credentials, endpoint paths, +and retry policy from becoming part of the portable candidate contract. + +#### Methods + +##### reserve() + +> **reserve**(`input`): `Promise`\<[`AgentCandidateProtectedModelReservation`](#agentcandidateprotectedmodelreservation)\> + +Defined in: [candidate-execution/protected-model-port.ts:40](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L40) + +###### Parameters + +###### input + +###### executionId + +`string` + +###### preparationId + +`string` + +###### expiresAtMs + +`number` + +###### attempt + +`AgentCandidateAttemptPolicy` + +###### bundleDigest + +`` `sha256:${string}` `` + +###### resolved + +`AgentCandidateResolvedModel` + +###### limits + +[`AgentCandidateModelLimits`](#agentcandidatemodellimits) + +###### Returns + +`Promise`\<[`AgentCandidateProtectedModelReservation`](#agentcandidateprotectedmodelreservation)\> + +##### activate() + +> **activate**(`input`): `Promise`\<[`AgentCandidateProtectedModelActivation`](#agentcandidateprotectedmodelactivation)\> + +Defined in: [candidate-execution/protected-model-port.ts:41](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L41) + +###### Parameters + +###### input + +###### executionId + +`string` + +###### preparationId + +`string` + +###### grantDigest + +`` `sha256:${string}` `` + +###### resolved + +`AgentCandidateResolvedModel` + +###### deadlineAtMs + +`number` + +###### Returns + +`Promise`\<[`AgentCandidateProtectedModelActivation`](#agentcandidateprotectedmodelactivation)\> + +##### settle() + +> **settle**(`input`): `Promise`\<[`AgentCandidateProtectedModelSettlement`](#agentcandidateprotectedmodelsettlement)\> + +Defined in: [candidate-execution/protected-model-port.ts:44](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L44) + +###### Parameters + +###### input + +###### executionId + +`string` + +###### preparationId + +`string` + +###### grantDigest + +`` `sha256:${string}` `` + +###### resolved + +`AgentCandidateResolvedModel` + +###### reason + +`"completed"` \| `"failed"` \| `"timeout"` \| `"replayed"` \| `"preparation-failed"` \| `"abandoned"` + +###### Returns + +`Promise`\<[`AgentCandidateProtectedModelSettlement`](#agentcandidateprotectedmodelsettlement)\> + +*** + +### CreateProtectedAgentCandidateModelPortOptions + +Defined in: [candidate-execution/protected-model-port.ts:49](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L49) + +#### Properties + +##### client + +> **client**: [`AgentCandidateModelGrantClient`](#agentcandidatemodelgrantclient) + +Defined in: [candidate-execution/protected-model-port.ts:50](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L50) + +##### resolveModel + +> **resolveModel**: (`input`) => `Promise`\<`AgentCandidateResolvedModel`\> + +Defined in: [candidate-execution/protected-model-port.ts:52](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L52) + +Catalog/snapshot resolution stays separate from credential issuance. + +###### Parameters + +###### input + +###### requested + +`string` + +###### harness + +`HarnessType` + +###### reasoningEffort + +`ReasoningEffort` \| `undefined` + +###### Returns + +`Promise`\<`AgentCandidateResolvedModel`\> + +##### gatewayDomain + +> **gatewayDomain**: `string` + +Defined in: [candidate-execution/protected-model-port.ts:54](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L54) + +The only public DNS name candidate processes may reach for inference. + +##### activationEnvNames + +> **activationEnvNames**: readonly `string`[] + +Defined in: [candidate-execution/protected-model-port.ts:56](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L56) + +Exact environment names the activation endpoint must return, no more or fewer. + +*** + ### RecoverExpiredAgentCandidateOptions Defined in: [candidate-execution/recover.ts:23](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/recover.ts#L23) @@ -1916,7 +2099,7 @@ Evaluator clock; must be the same clock used by the claim store. ### AgentCandidateArtifactPort -Defined in: [candidate-execution/types.ts:33](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L33) +Defined in: [candidate-execution/types.ts:34](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L34) Reads one content-addressed object from the closed S3/IPFS locator set. @@ -1930,7 +2113,7 @@ Reads one content-addressed object from the closed S3/IPFS locator set. > **read**(`ref`): `Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\> -Defined in: [candidate-execution/types.ts:34](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L34) +Defined in: [candidate-execution/types.ts:35](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L35) ###### Parameters @@ -1946,7 +2129,7 @@ Defined in: [candidate-execution/types.ts:34](https://github.com/tangle-network/ ### AgentCandidateOutputArtifactPort -Defined in: [candidate-execution/types.ts:52](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L52) +Defined in: [candidate-execution/types.ts:53](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L53) Durable content-addressed evidence store controlled only by the evaluator. @@ -1960,7 +2143,7 @@ Durable content-addressed evidence store controlled only by the evaluator. > **read**(`ref`): `Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\> -Defined in: [candidate-execution/types.ts:34](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L34) +Defined in: [candidate-execution/types.ts:35](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L35) ###### Parameters @@ -1980,7 +2163,7 @@ Defined in: [candidate-execution/types.ts:34](https://github.com/tangle-network/ > **put**(`input`): `Promise`\<`AgentCandidateArtifactRef`\> -Defined in: [candidate-execution/types.ts:54](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L54) +Defined in: [candidate-execution/types.ts:55](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L55) Must be idempotent for identical bytes and return only a durable S3/IPFS locator. @@ -2014,7 +2197,7 @@ Abort must prevent durable publication when it happens before resolution. ### AgentCandidateRepositoryPort -Defined in: [candidate-execution/types.ts:64](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L64) +Defined in: [candidate-execution/types.ts:65](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L65) Resolves a declared GitHub repository to an already-present local Git object store. @@ -2024,7 +2207,7 @@ Resolves a declared GitHub repository to an already-present local Git object sto > **resolve**(`repository`): `Promise`\<`string`\> -Defined in: [candidate-execution/types.ts:65](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L65) +Defined in: [candidate-execution/types.ts:66](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L66) ###### Parameters @@ -2040,7 +2223,7 @@ Defined in: [candidate-execution/types.ts:65](https://github.com/tangle-network/ ### AgentCandidateVerificationPorts -Defined in: [candidate-execution/types.ts:68](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L68) +Defined in: [candidate-execution/types.ts:69](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L69) #### Extended by @@ -2052,19 +2235,19 @@ Defined in: [candidate-execution/types.ts:68](https://github.com/tangle-network/ > **artifacts**: [`AgentCandidateArtifactPort`](#agentcandidateartifactport) -Defined in: [candidate-execution/types.ts:69](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L69) +Defined in: [candidate-execution/types.ts:70](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L70) ##### repositories > **repositories**: [`AgentCandidateRepositoryPort`](#agentcandidaterepositoryport) -Defined in: [candidate-execution/types.ts:70](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L70) +Defined in: [candidate-execution/types.ts:71](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L71) *** ### AgentCandidateWorkspacePort -Defined in: [candidate-execution/types.ts:80](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L80) +Defined in: [candidate-execution/types.ts:81](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L81) Materializes an already-verified workspace archive. @@ -2078,7 +2261,7 @@ any archive encoding, or no-op when the exact workspace is already present. > **materialize**(`input`): `Promise`\<`void`\> -Defined in: [candidate-execution/types.ts:81](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L81) +Defined in: [candidate-execution/types.ts:82](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L82) ###### Parameters @@ -2108,7 +2291,7 @@ Defined in: [candidate-execution/types.ts:81](https://github.com/tangle-network/ ### ResolvedAgentCandidateContainer -Defined in: [candidate-execution/types.ts:89](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L89) +Defined in: [candidate-execution/types.ts:90](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L90) #### Properties @@ -2116,37 +2299,37 @@ Defined in: [candidate-execution/types.ts:89](https://github.com/tangle-network/ > **source**: `"pinned-container"` \| `"evaluator-task-container"` -Defined in: [candidate-execution/types.ts:90](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L90) +Defined in: [candidate-execution/types.ts:91](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L91) ##### image > **image**: `string` -Defined in: [candidate-execution/types.ts:91](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L91) +Defined in: [candidate-execution/types.ts:92](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L92) ##### indexDigest > **indexDigest**: `` `sha256:${string}` `` -Defined in: [candidate-execution/types.ts:92](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L92) +Defined in: [candidate-execution/types.ts:93](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L93) ##### manifestDigest > **manifestDigest**: `` `sha256:${string}` `` -Defined in: [candidate-execution/types.ts:93](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L93) +Defined in: [candidate-execution/types.ts:94](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L94) ##### platform > **platform**: `AgentCandidateOciPlatform` -Defined in: [candidate-execution/types.ts:94](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L94) +Defined in: [candidate-execution/types.ts:95](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L95) *** ### AgentCandidateContainerPort -Defined in: [candidate-execution/types.ts:97](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L97) +Defined in: [candidate-execution/types.ts:98](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L98) #### Methods @@ -2154,7 +2337,7 @@ Defined in: [candidate-execution/types.ts:97](https://github.com/tangle-network/ > **resolve**(`input`): `Promise`\<[`ResolvedAgentCandidateContainer`](#resolvedagentcandidatecontainer)\> -Defined in: [candidate-execution/types.ts:98](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L98) +Defined in: [candidate-execution/types.ts:99](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L99) ###### Parameters @@ -2176,7 +2359,7 @@ Defined in: [candidate-execution/types.ts:98](https://github.com/tangle-network/ ### AgentCandidateModelPort -Defined in: [candidate-execution/types.ts:104](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L104) +Defined in: [candidate-execution/types.ts:105](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L105) #### Methods @@ -2184,7 +2367,7 @@ Defined in: [candidate-execution/types.ts:104](https://github.com/tangle-network > **resolve**(`input`): `Promise`\<`AgentCandidateResolvedModel`\> -Defined in: [candidate-execution/types.ts:105](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L105) +Defined in: [candidate-execution/types.ts:106](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L106) ###### Parameters @@ -2210,7 +2393,7 @@ Defined in: [candidate-execution/types.ts:105](https://github.com/tangle-network > **reserveGrant**(`input`): `Promise`\<[`AgentCandidateProtectedModelReservation`](#agentcandidateprotectedmodelreservation)\> -Defined in: [candidate-execution/types.ts:115](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L115) +Defined in: [candidate-execution/types.ts:116](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L116) Reserve a stable access identity without creating a live credential. The reservation is scoped to `preparationId` and must automatically expire @@ -2256,7 +2439,7 @@ at `expiresAtMs`, even if this call returns ambiguously to the runtime. > **activateGrant**(`input`): `Promise`\<[`AgentCandidateProtectedModelActivation`](#agentcandidateprotectedmodelactivation)\> -Defined in: [candidate-execution/types.ts:125](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L125) +Defined in: [candidate-execution/types.ts:126](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L126) Create the live scoped credential only after the execution attempt is durably claimed. @@ -2292,7 +2475,7 @@ Create the live scoped credential only after the execution attempt is durably cl > **settleGrant**(`input`): `Promise`\<[`AgentCandidateProtectedModelSettlement`](#agentcandidateprotectedmodelsettlement)\> -Defined in: [candidate-execution/types.ts:138](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L138) +Defined in: [candidate-execution/types.ts:139](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L139) Atomically revoke the grant, drain in-flight calls, and return its immutable final ledger. This operation must be idempotent for the exact preparation and must also @@ -2331,7 +2514,7 @@ different preparation, even when both reservations report the same digest. ### AgentCandidateProtectedModelReservation -Defined in: [candidate-execution/types.ts:153](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L153) +Defined in: [candidate-execution/types.ts:154](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L154) #### Properties @@ -2339,19 +2522,19 @@ Defined in: [candidate-execution/types.ts:153](https://github.com/tangle-network > **preparationId**: `string` -Defined in: [candidate-execution/types.ts:154](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L154) +Defined in: [candidate-execution/types.ts:155](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L155) ##### digest > **digest**: `` `sha256:${string}` `` -Defined in: [candidate-execution/types.ts:155](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L155) +Defined in: [candidate-execution/types.ts:156](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L156) ##### expiresAtMs > **expiresAtMs**: `number` -Defined in: [candidate-execution/types.ts:157](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L157) +Defined in: [candidate-execution/types.ts:158](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L158) Evaluator service must expire and revoke this reservation at this epoch millisecond. @@ -2359,15 +2542,23 @@ Evaluator service must expire and revoke this reservation at this epoch millisec > **enforcedLimits**: [`AgentCandidateModelLimits`](#agentcandidatemodellimits) -Defined in: [candidate-execution/types.ts:159](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L159) +Defined in: [candidate-execution/types.ts:160](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L160) The gateway must stop calls before any one of these limits is exceeded. +##### network + +> **network**: `AgentCandidateModelAccessNetwork` + +Defined in: [candidate-execution/types.ts:162](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L162) + +Exact public endpoint exception; every other candidate destination stays blocked. + *** ### AgentCandidateProtectedModelActivation -Defined in: [candidate-execution/types.ts:162](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L162) +Defined in: [candidate-execution/types.ts:165](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L165) #### Properties @@ -2375,7 +2566,7 @@ Defined in: [candidate-execution/types.ts:162](https://github.com/tangle-network > **env**: `Readonly`\<`Record`\<`string`, `string`\>\> -Defined in: [candidate-execution/types.ts:164](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L164) +Defined in: [candidate-execution/types.ts:167](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L167) Injected only into the trusted executor after all pre-launch checks pass. @@ -2383,7 +2574,7 @@ Injected only into the trusted executor after all pre-launch checks pass. ### AgentCandidateProtectedModelCall -Defined in: [candidate-execution/types.ts:168](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L168) +Defined in: [candidate-execution/types.ts:171](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L171) One evaluator-gateway call in the final, revoked model-access ledger. @@ -2393,51 +2584,77 @@ One evaluator-gateway call in the final, revoked model-access ledger. > **callId**: `string` -Defined in: [candidate-execution/types.ts:169](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L169) +Defined in: [candidate-execution/types.ts:172](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L172) + +##### generationId + +> **generationId**: `string` + +Defined in: [candidate-execution/types.ts:174](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L174) + +Router-generated public response identity. ##### traceSpanId > **traceSpanId**: `string` -Defined in: [candidate-execution/types.ts:171](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L171) +Defined in: [candidate-execution/types.ts:176](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L176) + +Exact protected agent-eval LLM span produced from the router ledger. + +##### status + +> **status**: `"failed"` \| `"succeeded"` -Exact protected agent-eval LLM span produced for this call. +Defined in: [candidate-execution/types.ts:177](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L177) ##### model > **model**: `string` -Defined in: [candidate-execution/types.ts:172](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L172) +Defined in: [candidate-execution/types.ts:178](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L178) + +##### startedAtMs + +> **startedAtMs**: `number` + +Defined in: [candidate-execution/types.ts:179](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L179) + +##### endedAtMs + +> **endedAtMs**: `number` + +Defined in: [candidate-execution/types.ts:180](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L180) ##### inputTokens > **inputTokens**: `number` -Defined in: [candidate-execution/types.ts:173](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L173) +Defined in: [candidate-execution/types.ts:181](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L181) ##### outputTokens > **outputTokens**: `number` -Defined in: [candidate-execution/types.ts:174](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L174) +Defined in: [candidate-execution/types.ts:182](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L182) -##### cachedInputTokens? +##### cachedInputTokens -> `optional` **cachedInputTokens?**: `number` +> **cachedInputTokens**: `number` -Defined in: [candidate-execution/types.ts:175](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L175) +Defined in: [candidate-execution/types.ts:183](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L183) -##### reasoningTokens? +##### reasoningTokens -> `optional` **reasoningTokens?**: `number` +> **reasoningTokens**: `number` -Defined in: [candidate-execution/types.ts:176](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L176) +Defined in: [candidate-execution/types.ts:184](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L184) ##### costUsdNanos > **costUsdNanos**: `number` -Defined in: [candidate-execution/types.ts:178](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L178) +Defined in: [candidate-execution/types.ts:186](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L186) Integer billionths of one US dollar; avoids floating-point ledger drift. @@ -2445,7 +2662,7 @@ Integer billionths of one US dollar; avoids floating-point ledger drift. ### AgentCandidateProtectedModelSettlement -Defined in: [candidate-execution/types.ts:181](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L181) +Defined in: [candidate-execution/types.ts:189](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L189) #### Properties @@ -2453,31 +2670,31 @@ Defined in: [candidate-execution/types.ts:181](https://github.com/tangle-network > **preparationId**: `string` -Defined in: [candidate-execution/types.ts:182](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L182) +Defined in: [candidate-execution/types.ts:190](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L190) ##### grantDigest > **grantDigest**: `` `sha256:${string}` `` -Defined in: [candidate-execution/types.ts:183](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L183) +Defined in: [candidate-execution/types.ts:191](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L191) ##### closed > **closed**: `true` -Defined in: [candidate-execution/types.ts:184](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L184) +Defined in: [candidate-execution/types.ts:192](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L192) ##### calls > **calls**: readonly [`AgentCandidateProtectedModelCall`](#agentcandidateprotectedmodelcall)[] -Defined in: [candidate-execution/types.ts:185](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L185) +Defined in: [candidate-execution/types.ts:193](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L193) *** ### AgentCandidateMemoryResetResult -Defined in: [candidate-execution/types.ts:188](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L188) +Defined in: [candidate-execution/types.ts:196](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L196) #### Properties @@ -2485,43 +2702,43 @@ Defined in: [candidate-execution/types.ts:188](https://github.com/tangle-network > **preparationId**: `string` -Defined in: [candidate-execution/types.ts:189](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L189) +Defined in: [candidate-execution/types.ts:197](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L197) ##### accessDigest > **accessDigest**: `` `sha256:${string}` `` -Defined in: [candidate-execution/types.ts:190](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L190) +Defined in: [candidate-execution/types.ts:198](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L198) ##### expiresAtMs > **expiresAtMs**: `number` -Defined in: [candidate-execution/types.ts:191](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L191) +Defined in: [candidate-execution/types.ts:199](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L199) ##### evidence > **evidence**: `AgentCandidateCapturedArtifact` -Defined in: [candidate-execution/types.ts:192](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L192) +Defined in: [candidate-execution/types.ts:200](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L200) ##### emptyStateDigest > **emptyStateDigest**: `` `sha256:${string}` `` -Defined in: [candidate-execution/types.ts:193](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L193) +Defined in: [candidate-execution/types.ts:201](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L201) ##### beforeState > **beforeState**: `AgentCandidateWorkspaceSnapshotEvidence` -Defined in: [candidate-execution/types.ts:194](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L194) +Defined in: [candidate-execution/types.ts:202](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L202) *** ### AgentCandidateMemoryPort -Defined in: [candidate-execution/types.ts:197](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L197) +Defined in: [candidate-execution/types.ts:205](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L205) #### Methods @@ -2529,7 +2746,7 @@ Defined in: [candidate-execution/types.ts:197](https://github.com/tangle-network > **reset**(`input`): `Promise`\<[`AgentCandidateMemoryResetResult`](#agentcandidatememoryresetresult)\> -Defined in: [candidate-execution/types.ts:203](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L203) +Defined in: [candidate-execution/types.ts:211](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L211) Reset and reserve exact task memory without returning live access. The service must scope the reservation to `preparationId`, automatically @@ -2571,7 +2788,7 @@ revoke it at `expiresAtMs`, and never reuse it for another preparation. > **activate**(`input`): `Promise`\<\{ `env`: `Readonly`\<`Record`\<`string`, `string`\>\>; \}\> -Defined in: [candidate-execution/types.ts:215](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L215) +Defined in: [candidate-execution/types.ts:223](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L223) Create live scoped access only after the execution attempt is durably claimed. Activation must match the exact preparation/access pair and may not extend expiry. @@ -2608,7 +2825,7 @@ Activation must match the exact preparation/access pair and may not extend expir > **close**(`input`): `Promise`\<\{ `closed`: `true`; \}\> -Defined in: [candidate-execution/types.ts:227](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L227) +Defined in: [candidate-execution/types.ts:235](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L235) Revoke evaluator-owned access after process death or a failed preparation. Must be idempotent and concurrency-safe for the exact preparation/access @@ -2646,7 +2863,7 @@ pair and must never close a different preparation. ### AgentCandidateExecutionPorts -Defined in: [candidate-execution/types.ts:236](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L236) +Defined in: [candidate-execution/types.ts:244](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L244) #### Extends @@ -2658,7 +2875,7 @@ Defined in: [candidate-execution/types.ts:236](https://github.com/tangle-network > **artifacts**: [`AgentCandidateArtifactPort`](#agentcandidateartifactport) -Defined in: [candidate-execution/types.ts:69](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L69) +Defined in: [candidate-execution/types.ts:70](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L70) ###### Inherited from @@ -2668,7 +2885,7 @@ Defined in: [candidate-execution/types.ts:69](https://github.com/tangle-network/ > **repositories**: [`AgentCandidateRepositoryPort`](#agentcandidaterepositoryport) -Defined in: [candidate-execution/types.ts:70](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L70) +Defined in: [candidate-execution/types.ts:71](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L71) ###### Inherited from @@ -2678,31 +2895,31 @@ Defined in: [candidate-execution/types.ts:70](https://github.com/tangle-network/ > **workspaces**: [`AgentCandidateWorkspacePort`](#agentcandidateworkspaceport) -Defined in: [candidate-execution/types.ts:237](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L237) +Defined in: [candidate-execution/types.ts:245](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L245) ##### containers > **containers**: [`AgentCandidateContainerPort`](#agentcandidatecontainerport) -Defined in: [candidate-execution/types.ts:238](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L238) +Defined in: [candidate-execution/types.ts:246](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L246) ##### models > **models**: [`AgentCandidateModelPort`](#agentcandidatemodelport) -Defined in: [candidate-execution/types.ts:239](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L239) +Defined in: [candidate-execution/types.ts:247](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L247) ##### memory > **memory**: [`AgentCandidateMemoryPort`](#agentcandidatememoryport) -Defined in: [candidate-execution/types.ts:240](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L240) +Defined in: [candidate-execution/types.ts:248](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L248) *** ### AgentCandidateTaskExecution -Defined in: [candidate-execution/types.ts:243](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L243) +Defined in: [candidate-execution/types.ts:251](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L251) #### Properties @@ -2710,37 +2927,37 @@ Defined in: [candidate-execution/types.ts:243](https://github.com/tangle-network > **executionId**: `string` -Defined in: [candidate-execution/types.ts:244](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L244) +Defined in: [candidate-execution/types.ts:252](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L252) ##### benchmark > **benchmark**: `string` -Defined in: [candidate-execution/types.ts:245](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L245) +Defined in: [candidate-execution/types.ts:253](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L253) ##### benchmarkVersion > **benchmarkVersion**: `string` -Defined in: [candidate-execution/types.ts:246](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L246) +Defined in: [candidate-execution/types.ts:254](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L254) ##### taskId > **taskId**: `string` -Defined in: [candidate-execution/types.ts:247](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L247) +Defined in: [candidate-execution/types.ts:255](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L255) ##### splitDigest > **splitDigest**: `` `sha256:${string}` `` -Defined in: [candidate-execution/types.ts:248](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L248) +Defined in: [candidate-execution/types.ts:256](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L256) ##### instruction > **instruction**: `string` -Defined in: [candidate-execution/types.ts:250](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L250) +Defined in: [candidate-execution/types.ts:258](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L258) Exact agent-visible task instruction. The runtime rejects malformed Unicode. @@ -2748,7 +2965,7 @@ Exact agent-visible task instruction. The runtime rejects malformed Unicode. > **repository**: `object` -Defined in: [candidate-execution/types.ts:251](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L251) +Defined in: [candidate-execution/types.ts:259](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L259) ###### identity @@ -2770,13 +2987,13 @@ Defined in: [candidate-execution/types.ts:251](https://github.com/tangle-network > **attempt**: `AgentCandidateAttemptPolicy` -Defined in: [candidate-execution/types.ts:257](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L257) +Defined in: [candidate-execution/types.ts:265](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L265) ##### model > **model**: `object` -Defined in: [candidate-execution/types.ts:258](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L258) +Defined in: [candidate-execution/types.ts:266](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L266) ###### requested @@ -2790,7 +3007,7 @@ Defined in: [candidate-execution/types.ts:258](https://github.com/tangle-network > **executionRoots**: `object` -Defined in: [candidate-execution/types.ts:263](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L263) +Defined in: [candidate-execution/types.ts:271](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L271) Absolute paths inside the evaluator-owned execution environment. @@ -2806,7 +3023,7 @@ Absolute paths inside the evaluator-owned execution environment. > **stagingRoots**: `object` -Defined in: [candidate-execution/types.ts:268](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L268) +Defined in: [candidate-execution/types.ts:276](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L276) Host-side staging roots. These are verified but never signed as container paths. @@ -2826,25 +3043,25 @@ Host-side staging roots. These are verified but never signed as container paths. > **workspace**: `AgentCandidateWorkspaceSnapshotEvidence` -Defined in: [candidate-execution/types.ts:273](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L273) +Defined in: [candidate-execution/types.ts:281](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L281) ##### evaluatorTaskContainer? > `optional` **evaluatorTaskContainer?**: [`ResolvedAgentCandidateContainer`](#resolvedagentcandidatecontainer) -Defined in: [candidate-execution/types.ts:274](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L274) +Defined in: [candidate-execution/types.ts:282](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L282) ##### limits > **limits**: `AgentCandidateExecutionLimits` -Defined in: [candidate-execution/types.ts:275](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L275) +Defined in: [candidate-execution/types.ts:283](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L283) *** ### VerifiedAgentCandidate -Defined in: [candidate-execution/types.ts:278](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L278) +Defined in: [candidate-execution/types.ts:286](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L286) #### Properties @@ -2852,25 +3069,25 @@ Defined in: [candidate-execution/types.ts:278](https://github.com/tangle-network > `readonly` **bundle**: `AgentCandidateBundleV1` -Defined in: [candidate-execution/types.ts:279](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L279) +Defined in: [candidate-execution/types.ts:287](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L287) ##### materializedTree? > `readonly` `optional` **materializedTree?**: `string` -Defined in: [candidate-execution/types.ts:280](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L280) +Defined in: [candidate-execution/types.ts:288](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L288) ##### \[verifiedCandidateBrand\] > `readonly` **\[verifiedCandidateBrand\]**: `true` -Defined in: [candidate-execution/types.ts:281](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L281) +Defined in: [candidate-execution/types.ts:289](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L289) *** ### CanonicalCandidateDocument -Defined in: [candidate-execution/types.ts:284](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L284) +Defined in: [candidate-execution/types.ts:292](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L292) #### Type Parameters @@ -2884,13 +3101,13 @@ Defined in: [candidate-execution/types.ts:284](https://github.com/tangle-network > `readonly` **value**: `T` -Defined in: [candidate-execution/types.ts:285](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L285) +Defined in: [candidate-execution/types.ts:293](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L293) ##### bytes > `readonly` **bytes**: `Uint8Array` -Defined in: [candidate-execution/types.ts:287](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L287) +Defined in: [candidate-execution/types.ts:295](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L295) Canonical UTF-8 bytes of `value` with its top-level digest omitted. @@ -2898,13 +3115,13 @@ Canonical UTF-8 bytes of `value` with its top-level digest omitted. > `readonly` **digest**: `` `sha256:${string}` `` -Defined in: [candidate-execution/types.ts:288](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L288) +Defined in: [candidate-execution/types.ts:296](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L296) *** ### PreparedAgentCandidateLaunch -Defined in: [candidate-execution/types.ts:291](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L291) +Defined in: [candidate-execution/types.ts:299](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L299) #### Properties @@ -2912,13 +3129,13 @@ Defined in: [candidate-execution/types.ts:291](https://github.com/tangle-network > **executable**: `string` -Defined in: [candidate-execution/types.ts:292](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L292) +Defined in: [candidate-execution/types.ts:300](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L300) ##### args > **args**: readonly `string`[] -Defined in: [candidate-execution/types.ts:294](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L294) +Defined in: [candidate-execution/types.ts:302](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L302) Complete fixed argv, including profile materializer flags but excluding task delivery. @@ -2926,13 +3143,13 @@ Complete fixed argv, including profile materializer flags but excluding task del > **env**: `Readonly`\<`Record`\<`string`, `string`\>\> -Defined in: [candidate-execution/types.ts:295](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L295) +Defined in: [candidate-execution/types.ts:303](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L303) ##### flags > **flags**: readonly `string`[] -Defined in: [candidate-execution/types.ts:297](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L297) +Defined in: [candidate-execution/types.ts:305](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L305) Informational subset already present at the tail of `args`; executors must not append twice. @@ -2940,13 +3157,13 @@ Informational subset already present at the tail of `args`; executors must not a > **cwd**: `string` -Defined in: [candidate-execution/types.ts:298](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L298) +Defined in: [candidate-execution/types.ts:306](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L306) *** ### PreparedAgentCandidateInstruction -Defined in: [candidate-execution/types.ts:301](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L301) +Defined in: [candidate-execution/types.ts:309](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L309) #### Properties @@ -2954,19 +3171,19 @@ Defined in: [candidate-execution/types.ts:301](https://github.com/tangle-network > **bytes**: `Uint8Array` -Defined in: [candidate-execution/types.ts:302](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L302) +Defined in: [candidate-execution/types.ts:310](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L310) ##### delivery > **delivery**: `AgentCandidateInstructionDelivery` -Defined in: [candidate-execution/types.ts:303](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L303) +Defined in: [candidate-execution/types.ts:311](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L311) *** ### PreparedAgentCandidateTrace -Defined in: [candidate-execution/types.ts:306](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L306) +Defined in: [candidate-execution/types.ts:314](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L314) #### Properties @@ -2974,25 +3191,25 @@ Defined in: [candidate-execution/types.ts:306](https://github.com/tangle-network > **runId**: `string` -Defined in: [candidate-execution/types.ts:307](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L307) +Defined in: [candidate-execution/types.ts:315](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L315) ##### tags > **tags**: `Readonly`\<`Record`\<`string`, `string`\>\> -Defined in: [candidate-execution/types.ts:308](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L308) +Defined in: [candidate-execution/types.ts:316](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L316) ##### env > **env**: `Readonly`\<`Record`\<`string`, `string`\>\> -Defined in: [candidate-execution/types.ts:309](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L309) +Defined in: [candidate-execution/types.ts:317](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L317) *** ### PreparedAgentCandidateExecution -Defined in: [candidate-execution/types.ts:312](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L312) +Defined in: [candidate-execution/types.ts:320](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L320) #### Properties @@ -3000,19 +3217,19 @@ Defined in: [candidate-execution/types.ts:312](https://github.com/tangle-network > `readonly` **bundle**: `AgentCandidateBundleV1` -Defined in: [candidate-execution/types.ts:313](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L313) +Defined in: [candidate-execution/types.ts:321](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L321) ##### executionId > `readonly` **executionId**: `string` -Defined in: [candidate-execution/types.ts:314](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L314) +Defined in: [candidate-execution/types.ts:322](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L322) ##### roots > `readonly` **roots**: `object` -Defined in: [candidate-execution/types.ts:315](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L315) +Defined in: [candidate-execution/types.ts:323](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L323) ###### execution @@ -3046,7 +3263,7 @@ Defined in: [candidate-execution/types.ts:315](https://github.com/tangle-network > `readonly` **profilePlan**: `object` -Defined in: [candidate-execution/types.ts:326](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L326) +Defined in: [candidate-execution/types.ts:334](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L334) ###### value @@ -3064,7 +3281,7 @@ Defined in: [candidate-execution/types.ts:326](https://github.com/tangle-network > `readonly` **executionPlan**: `object` -Defined in: [candidate-execution/types.ts:331](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L331) +Defined in: [candidate-execution/types.ts:339](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L339) ###### value @@ -3078,31 +3295,31 @@ Defined in: [candidate-execution/types.ts:331](https://github.com/tangle-network > `readonly` **materializationReceipt**: [`CanonicalCandidateDocument`](#canonicalcandidatedocument)\<`AgentCandidateMaterializationReceiptV1`\> -Defined in: [candidate-execution/types.ts:335](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L335) +Defined in: [candidate-execution/types.ts:343](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L343) ##### launch > `readonly` **launch**: [`PreparedAgentCandidateLaunch`](#preparedagentcandidatelaunch) -Defined in: [candidate-execution/types.ts:336](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L336) +Defined in: [candidate-execution/types.ts:344](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L344) ##### instruction > `readonly` **instruction**: [`PreparedAgentCandidateInstruction`](#preparedagentcandidateinstruction) -Defined in: [candidate-execution/types.ts:337](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L337) +Defined in: [candidate-execution/types.ts:345](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L345) ##### resolvedModel > `readonly` **resolvedModel**: `AgentCandidateResolvedModel` -Defined in: [candidate-execution/types.ts:338](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L338) +Defined in: [candidate-execution/types.ts:346](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L346) ##### knowledge? > `readonly` `optional` **knowledge?**: `object` -Defined in: [candidate-execution/types.ts:339](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L339) +Defined in: [candidate-execution/types.ts:347](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L347) ###### snapshotId @@ -3120,25 +3337,25 @@ Defined in: [candidate-execution/types.ts:339](https://github.com/tangle-network > `readonly` **trace**: [`PreparedAgentCandidateTrace`](#preparedagentcandidatetrace) -Defined in: [candidate-execution/types.ts:344](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L344) +Defined in: [candidate-execution/types.ts:352](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L352) ##### memory > `readonly` **memory**: `AgentCandidateEffectiveMemory` -Defined in: [candidate-execution/types.ts:345](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L345) +Defined in: [candidate-execution/types.ts:353](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L353) ##### \[preparedCandidateBrand\] > `readonly` **\[preparedCandidateBrand\]**: `true` -Defined in: [candidate-execution/types.ts:346](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L346) +Defined in: [candidate-execution/types.ts:354](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L354) *** ### AgentCandidateProtectedRunCapture -Defined in: [candidate-execution/types.ts:349](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L349) +Defined in: [candidate-execution/types.ts:357](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L357) #### Properties @@ -3146,19 +3363,19 @@ Defined in: [candidate-execution/types.ts:349](https://github.com/tangle-network > **executionId**: `string` -Defined in: [candidate-execution/types.ts:350](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L350) +Defined in: [candidate-execution/types.ts:358](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L358) ##### termination > **termination**: `AgentCandidateTermination` -Defined in: [candidate-execution/types.ts:351](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L351) +Defined in: [candidate-execution/types.ts:359](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L359) *** ### AgentCandidateExecutorTaskOutcomeCapture -Defined in: [candidate-execution/types.ts:355](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L355) +Defined in: [candidate-execution/types.ts:363](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L363) Raw evaluator capture made only after the candidate process is dead. @@ -3168,7 +3385,7 @@ Raw evaluator capture made only after the candidate process is dead. > **resultTree**: `string` -Defined in: [candidate-execution/types.ts:357](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L357) +Defined in: [candidate-execution/types.ts:365](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L365) Claimed final tree. The runtime recomputes it independently from `gitDiff`. @@ -3176,7 +3393,7 @@ Claimed final tree. The runtime recomputes it independently from `gitDiff`. > **afterState**: `AgentCandidateWorkspaceManifestMaterialV1` -Defined in: [candidate-execution/types.ts:359](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L359) +Defined in: [candidate-execution/types.ts:367](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L367) Complete evaluator-captured workspace description after candidate execution. @@ -3184,7 +3401,7 @@ Complete evaluator-captured workspace description after candidate execution. > **archive**: `Uint8Array` -Defined in: [candidate-execution/types.ts:361](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L361) +Defined in: [candidate-execution/types.ts:369](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L369) Reproducible workspace archive corresponding to `afterState`. @@ -3192,7 +3409,7 @@ Reproducible workspace archive corresponding to `afterState`. > **gitDiff**: `Uint8Array` -Defined in: [candidate-execution/types.ts:363](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L363) +Defined in: [candidate-execution/types.ts:371](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L371) Exact binary patch from the signed task base to `afterState`. @@ -3200,7 +3417,7 @@ Exact binary patch from the signed task base to `afterState`. ### AgentCandidateExecutorMemoryCapture -Defined in: [candidate-execution/types.ts:367](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L367) +Defined in: [candidate-execution/types.ts:375](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L375) Raw isolated-memory capture made only after access has been revoked. @@ -3210,19 +3427,19 @@ Raw isolated-memory capture made only after access has been revoked. > `readonly` **afterState**: `AgentCandidateWorkspaceManifestMaterialV1` -Defined in: [candidate-execution/types.ts:368](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L368) +Defined in: [candidate-execution/types.ts:376](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L376) ##### archive > `readonly` **archive**: `Uint8Array` -Defined in: [candidate-execution/types.ts:369](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L369) +Defined in: [candidate-execution/types.ts:377](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L377) *** ### AgentCandidateExecutorFinalCapture -Defined in: [candidate-execution/types.ts:373](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L373) +Defined in: [candidate-execution/types.ts:381](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L381) Idempotent executor result after process death and trace drain. @@ -3232,19 +3449,19 @@ Idempotent executor result after process death and trace drain. > `readonly` **stopped**: `true` -Defined in: [candidate-execution/types.ts:374](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L374) +Defined in: [candidate-execution/types.ts:382](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L382) ##### taskOutcome? > `readonly` `optional` **taskOutcome?**: [`AgentCandidateExecutorTaskOutcomeCapture`](#agentcandidateexecutortaskoutcomecapture) -Defined in: [candidate-execution/types.ts:375](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L375) +Defined in: [candidate-execution/types.ts:383](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L383) ##### memoryAfter? > `readonly` `optional` **memoryAfter?**: [`AgentCandidateExecutorMemoryCapture`](#agentcandidateexecutormemorycapture) -Defined in: [candidate-execution/types.ts:377](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L377) +Defined in: [candidate-execution/types.ts:385](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L385) Required only when the prepared candidate uses isolated task memory. @@ -3252,7 +3469,7 @@ Required only when the prepared candidate uses isolated task memory. ### VerifiedAgentCandidateTaskOutcome -Defined in: [candidate-execution/types.ts:381](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L381) +Defined in: [candidate-execution/types.ts:389](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L389) Branded task outcome that has survived independent patch and tree verification. @@ -3262,7 +3479,7 @@ Branded task outcome that has survived independent patch and tree verification. > `readonly` **evidence**: `AgentCandidateTaskOutcomeEvidence` & `object` -Defined in: [candidate-execution/types.ts:382](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L382) +Defined in: [candidate-execution/types.ts:390](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L390) ###### Type Declaration @@ -3274,19 +3491,19 @@ Defined in: [candidate-execution/types.ts:382](https://github.com/tangle-network > `readonly` **patch**: `Uint8Array` -Defined in: [candidate-execution/types.ts:385](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L385) +Defined in: [candidate-execution/types.ts:393](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L393) ##### \[verifiedTaskOutcomeBrand\] > `readonly` **\[verifiedTaskOutcomeBrand\]**: `true` -Defined in: [candidate-execution/types.ts:386](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L386) +Defined in: [candidate-execution/types.ts:394](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L394) *** ### AgentCandidateBenchmarkGraderPort -Defined in: [candidate-execution/types.ts:398](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L398) +Defined in: [candidate-execution/types.ts:406](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L406) Evaluator-owned executable grader, pinned by immutable implementation bytes. @@ -3302,19 +3519,19 @@ copying an expected digest from ambient configuration. > `readonly` **name**: `string` -Defined in: [candidate-execution/types.ts:399](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L399) +Defined in: [candidate-execution/types.ts:407](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L407) ##### version > `readonly` **version**: `string` -Defined in: [candidate-execution/types.ts:400](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L400) +Defined in: [candidate-execution/types.ts:408](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L408) ##### artifact > `readonly` **artifact**: `AgentCandidateArtifactRef` -Defined in: [candidate-execution/types.ts:401](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L401) +Defined in: [candidate-execution/types.ts:409](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L409) #### Methods @@ -3322,7 +3539,7 @@ Defined in: [candidate-execution/types.ts:401](https://github.com/tangle-network > **run**(`input`): `Promise`\<\{ `evaluation`: `BenchmarkEvaluation`; `evidence`: `Uint8Array`; `binding`: \{ `implementationDigest`: `` `sha256:${string}` ``; `taskOutcomeDigest`: `` `sha256:${string}` ``; `outputDigest`: `` `sha256:${string}` ``; \}; \}\> -Defined in: [candidate-execution/types.ts:402](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L402) +Defined in: [candidate-execution/types.ts:410](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L410) ###### Parameters @@ -3368,7 +3585,7 @@ Frozen result deadline; runners must stop work and side effects when aborted. ### AgentCandidateExecutorRequest -Defined in: [candidate-execution/types.ts:430](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L430) +Defined in: [candidate-execution/types.ts:438](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L438) One detached request passed to the trusted environment-specific executor. @@ -3378,13 +3595,13 @@ One detached request passed to the trusted environment-specific executor. > `readonly` **executionId**: `string` -Defined in: [candidate-execution/types.ts:431](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L431) +Defined in: [candidate-execution/types.ts:439](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L439) ##### inputs > `readonly` **inputs**: `object` -Defined in: [candidate-execution/types.ts:433](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L433) +Defined in: [candidate-execution/types.ts:441](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L441) Immutable bytes from which the executor creates fresh isolated workspaces. @@ -3408,7 +3625,7 @@ Immutable bytes from which the executor creates fresh isolated workspaces. > `readonly` **roots**: `object` -Defined in: [candidate-execution/types.ts:440](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L440) +Defined in: [candidate-execution/types.ts:448](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L448) ###### taskRoot @@ -3422,7 +3639,7 @@ Defined in: [candidate-execution/types.ts:440](https://github.com/tangle-network > `readonly` **profilePlan**: `object` -Defined in: [candidate-execution/types.ts:441](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L441) +Defined in: [candidate-execution/types.ts:449](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L449) ###### value @@ -3440,7 +3657,7 @@ Defined in: [candidate-execution/types.ts:441](https://github.com/tangle-network > `readonly` **executionPlan**: `object` -Defined in: [candidate-execution/types.ts:442](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L442) +Defined in: [candidate-execution/types.ts:450](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L450) ###### value @@ -3454,31 +3671,31 @@ Defined in: [candidate-execution/types.ts:442](https://github.com/tangle-network > `readonly` **materializationReceipt**: [`CanonicalCandidateDocument`](#canonicalcandidatedocument)\<`AgentCandidateMaterializationReceiptV1`\> -Defined in: [candidate-execution/types.ts:443](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L443) +Defined in: [candidate-execution/types.ts:451](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L451) ##### launch > `readonly` **launch**: [`PreparedAgentCandidateLaunch`](#preparedagentcandidatelaunch) -Defined in: [candidate-execution/types.ts:444](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L444) +Defined in: [candidate-execution/types.ts:452](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L452) ##### instruction > `readonly` **instruction**: [`PreparedAgentCandidateInstruction`](#preparedagentcandidateinstruction) -Defined in: [candidate-execution/types.ts:445](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L445) +Defined in: [candidate-execution/types.ts:453](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L453) ##### resolvedModel > `readonly` **resolvedModel**: `AgentCandidateResolvedModel` -Defined in: [candidate-execution/types.ts:446](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L446) +Defined in: [candidate-execution/types.ts:454](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L454) ##### hardLimits > `readonly` **hardLimits**: `Pick`\<`AgentCandidateExecutionLimits`, `"timeoutMs"`\> -Defined in: [candidate-execution/types.ts:448](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L448) +Defined in: [candidate-execution/types.ts:456](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L456) Mechanically enforced by the runtime plus executor process-death acknowledgement. @@ -3486,7 +3703,7 @@ Mechanically enforced by the runtime plus executor process-death acknowledgement > `readonly` **observedLimits**: `Pick`\<`AgentCandidateExecutionLimits`, `"maxSteps"`\> -Defined in: [candidate-execution/types.ts:450](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L450) +Defined in: [candidate-execution/types.ts:458](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L458) Validity bound checked against protected traces; generic black-box executors cannot preempt it. @@ -3494,7 +3711,7 @@ Validity bound checked against protected traces; generic black-box executors can > `readonly` `optional` **knowledge?**: `object` -Defined in: [candidate-execution/types.ts:451](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L451) +Defined in: [candidate-execution/types.ts:459](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L459) ###### snapshotId @@ -3512,19 +3729,19 @@ Defined in: [candidate-execution/types.ts:451](https://github.com/tangle-network > `readonly` **trace**: [`PreparedAgentCandidateTrace`](#preparedagentcandidatetrace) -Defined in: [candidate-execution/types.ts:452](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L452) +Defined in: [candidate-execution/types.ts:460](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L460) ##### memory > `readonly` **memory**: `AgentCandidateEffectiveMemory` -Defined in: [candidate-execution/types.ts:453](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L453) +Defined in: [candidate-execution/types.ts:461](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L461) *** ### AgentCandidateExecutorPort -Defined in: [candidate-execution/types.ts:464](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L464) +Defined in: [candidate-execution/types.ts:472](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L472) Executes one prepared request inside an evaluator-owned isolation boundary. @@ -3539,7 +3756,7 @@ no candidate-authored usage or score fields. > **execute**(`request`, `context`): `Promise`\<[`AgentCandidateProtectedRunCapture`](#agentcandidateprotectedruncapture)\> -Defined in: [candidate-execution/types.ts:465](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L465) +Defined in: [candidate-execution/types.ts:473](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L473) ###### Parameters @@ -3573,7 +3790,7 @@ Absolute epoch-millisecond deadline owned by the runtime. > **stopAndCapture**(`request`, `context`): `Promise`\<[`AgentCandidateExecutorFinalCapture`](#agentcandidateexecutorfinalcapture)\> -Defined in: [candidate-execution/types.ts:482](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L482) +Defined in: [candidate-execution/types.ts:490](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L490) Kill any process/container still associated with the request, drain trace writes, and capture the final task workspace before teardown. @@ -3617,7 +3834,7 @@ Absolute execution deadline; a later stop acknowledgement cannot produce success ### AgentCandidateExecutorStopRequest -Defined in: [candidate-execution/types.ts:496](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L496) +Defined in: [candidate-execution/types.ts:504](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L504) Opaque process identity used for termination without re-exposing launch credentials. @@ -3627,19 +3844,19 @@ Opaque process identity used for termination without re-exposing launch credenti > `readonly` **executionId**: `string` -Defined in: [candidate-execution/types.ts:497](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L497) +Defined in: [candidate-execution/types.ts:505](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L505) ##### executionPlanDigest > `readonly` **executionPlanDigest**: `` `sha256:${string}` `` -Defined in: [candidate-execution/types.ts:498](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L498) +Defined in: [candidate-execution/types.ts:506](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L506) *** ### AgentCandidateExecutorWorkspaceInput -Defined in: [candidate-execution/types.ts:501](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L501) +Defined in: [candidate-execution/types.ts:509](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L509) #### Properties @@ -3647,19 +3864,19 @@ Defined in: [candidate-execution/types.ts:501](https://github.com/tangle-network > `readonly` **snapshot**: `AgentCandidateWorkspaceSnapshotEvidence` -Defined in: [candidate-execution/types.ts:502](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L502) +Defined in: [candidate-execution/types.ts:510](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L510) ##### files > `readonly` **files**: readonly [`AgentCandidateExecutorWorkspaceFile`](#agentcandidateexecutorworkspacefile)[] -Defined in: [candidate-execution/types.ts:503](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L503) +Defined in: [candidate-execution/types.ts:511](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L511) *** ### AgentCandidateExecutorWorkspaceFile -Defined in: [candidate-execution/types.ts:506](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L506) +Defined in: [candidate-execution/types.ts:514](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L514) #### Properties @@ -3667,25 +3884,25 @@ Defined in: [candidate-execution/types.ts:506](https://github.com/tangle-network > `readonly` **path**: `string` -Defined in: [candidate-execution/types.ts:507](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L507) +Defined in: [candidate-execution/types.ts:515](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L515) ##### mode > `readonly` **mode**: `420` \| `493` -Defined in: [candidate-execution/types.ts:508](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L508) +Defined in: [candidate-execution/types.ts:516](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L516) ##### bytes > `readonly` **bytes**: `Uint8Array` -Defined in: [candidate-execution/types.ts:509](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L509) +Defined in: [candidate-execution/types.ts:517](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L517) *** ### AgentCandidateExecutorProfileFile -Defined in: [candidate-execution/types.ts:512](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L512) +Defined in: [candidate-execution/types.ts:520](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L520) #### Properties @@ -3693,19 +3910,19 @@ Defined in: [candidate-execution/types.ts:512](https://github.com/tangle-network > `readonly` **path**: `string` -Defined in: [candidate-execution/types.ts:513](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L513) +Defined in: [candidate-execution/types.ts:521](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L521) ##### mode > `readonly` **mode**: `420` \| `493` -Defined in: [candidate-execution/types.ts:514](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L514) +Defined in: [candidate-execution/types.ts:522](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L522) ##### bytes > `readonly` **bytes**: `Uint8Array` -Defined in: [candidate-execution/types.ts:515](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L515) +Defined in: [candidate-execution/types.ts:523](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L523) *** @@ -9923,11 +10140,45 @@ Defined in: [candidate-execution/claim.ts:216](https://github.com/tangle-network *** +### AgentCandidateModelGrantReserveInput + +> **AgentCandidateModelGrantReserveInput** = `Parameters`\<[`AgentCandidateModelPort`](#agentcandidatemodelport)\[`"reserveGrant"`\]\>\[`0`\] + +Defined in: [candidate-execution/protected-model-port.ts:18](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L18) + +*** + +### AgentCandidateModelGrantActivateInput + +> **AgentCandidateModelGrantActivateInput** = `Parameters`\<[`AgentCandidateModelPort`](#agentcandidatemodelport)\[`"activateGrant"`\]\>\[`0`\] + +Defined in: [candidate-execution/protected-model-port.ts:21](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L21) + +*** + +### AgentCandidateModelGrantSettleInput + +> **AgentCandidateModelGrantSettleInput** = `Parameters`\<[`AgentCandidateModelPort`](#agentcandidatemodelport)\[`"settleGrant"`\]\>\[`0`\] + +Defined in: [candidate-execution/protected-model-port.ts:24](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L24) + +*** + +### AgentCandidateModelGrantReservation + +> **AgentCandidateModelGrantReservation** = [`AgentCandidateProtectedModelReservation`](#agentcandidateprotectedmodelreservation) + +Defined in: [candidate-execution/protected-model-port.ts:29](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L29) + +Secret-free response from the service's reservation endpoint. + +*** + ### AgentCandidateOutputPurpose > **AgentCandidateOutputPurpose** = `"task-manifest"` \| `"task-archive"` \| `"task-patch"` \| `"task-outcome"` \| `"memory-after-manifest"` \| `"memory-after-archive"` \| `"grader-evidence"` \| `"benchmark-result"` \| `"model-settlement"` \| `"trace"` \| `"run-receipt"` \| `"failure-evidence"` -Defined in: [candidate-execution/types.ts:37](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L37) +Defined in: [candidate-execution/types.ts:38](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L38) *** @@ -9935,7 +10186,7 @@ Defined in: [candidate-execution/types.ts:37](https://github.com/tangle-network/ > **AgentCandidateModelLimits** = `Pick`\<`AgentCandidateExecutionLimits`, `"maxModelCalls"` \| `"maxInputTokens"` \| `"maxOutputTokens"` \| `"maxCostUsd"`\> -Defined in: [candidate-execution/types.ts:148](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L148) +Defined in: [candidate-execution/types.ts:149](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L149) Limits mechanically enforced by the evaluator-owned model gateway. @@ -9945,7 +10196,7 @@ Limits mechanically enforced by the evaluator-owned model gateway. > **AgentCandidateRunFinalization** = \{ `succeeded`: `true`; `receipt`: [`CanonicalCandidateDocument`](#canonicalcandidatedocument)\<`AgentCandidateRunReceiptV2`\>; `artifacts`: \{ `modelSettlement`: `AgentCandidateArtifactRef`; `taskOutcome`: `AgentCandidateArtifactRef`; `benchmarkResult`: `AgentCandidateArtifactRef`; `runReceipt`: `AgentCandidateArtifactRef`; \}; \} \| \{ `succeeded`: `false`; `reason`: `string`; `partial`: \{ `executionId`: `string`; `bundleDigest`: `Sha256Digest`; `executionPlanDigest`: `Sha256Digest`; `materializationReceiptDigest`: `Sha256Digest`; `termination?`: `AgentCandidateTermination`; \}; `usage`: `AgentCandidateSpend` \| `null`; \} -Defined in: [candidate-execution/types.ts:518](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L518) +Defined in: [candidate-execution/types.ts:526](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L526) #### Union Members @@ -10719,7 +10970,7 @@ MUST map this to `RunRecord.error` rather than recording silent > `const` **CANDIDATE\_TRACE\_TAGS**: `object` -Defined in: [candidate-execution/types.ts:544](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L544) +Defined in: [candidate-execution/types.ts:552](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L552) Protected trace tags that bind a run to one prepared candidate execution. @@ -10747,7 +10998,7 @@ Protected trace tags that bind a run to one prepared candidate execution. > `const` **CANDIDATE\_TRACE\_ENV**: `object` -Defined in: [candidate-execution/types.ts:552](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L552) +Defined in: [candidate-execution/types.ts:560](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/types.ts#L560) Environment keys used to propagate immutable candidate trace identity. @@ -11215,7 +11466,7 @@ Revoke reservations held by a prepared candidate that will not be executed. > **executePreparedAgentCandidate**(`prepared`, `options`): `Promise`\<[`AgentCandidateRunFinalization`](#agentcandidaterunfinalization)\> -Defined in: [candidate-execution/execute.ts:74](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L74) +Defined in: [candidate-execution/execute.ts:75](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/execute.ts#L75) Executes and finalizes one durably claimed candidate without exposing an unproven result. @@ -11277,7 +11528,7 @@ Persist evaluator evidence, read it back, and bind the returned locator to the e > **prepareAgentCandidateExecution**(`candidate`, `task`, `ports`, `options?`): `Promise`\<[`PreparedAgentCandidateExecution`](#preparedagentcandidateexecution)\> -Defined in: [candidate-execution/prepare.ts:95](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/prepare.ts#L95) +Defined in: [candidate-execution/prepare.ts:97](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/prepare.ts#L97) Materializes a verified candidate into one immutable evaluator-owned execution plan. @@ -11305,6 +11556,30 @@ Materializes a verified candidate into one immutable evaluator-owned execution p *** +### createProtectedAgentCandidateModelPort() + +> **createProtectedAgentCandidateModelPort**(`options`): [`AgentCandidateModelPort`](#agentcandidatemodelport) + +Defined in: [candidate-execution/protected-model-port.ts:90](https://github.com/tangle-network/agent-runtime/blob/main/src/candidate-execution/protected-model-port.ts#L90) + +Bind a protected model-grant service to the immutable candidate runtime. + +The service remains the authority for expiry, admission, revocation, and +metering. This adapter independently checks every response before allowing +it to cross into candidate execution or durable receipt finalization. + +#### Parameters + +##### options + +[`CreateProtectedAgentCandidateModelPortOptions`](#createprotectedagentcandidatemodelportoptions) + +#### Returns + +[`AgentCandidateModelPort`](#agentcandidatemodelport) + +*** + ### recoverExpiredAgentCandidateExecution() > **recoverExpiredAgentCandidateExecution**(`options`): `Promise`\<[`AgentCandidateExecutionFinishResult`](#agentcandidateexecutionfinishresult)\> diff --git a/docs/api/primitive-catalog.md b/docs/api/primitive-catalog.md index 525f268f..27b357a0 100644 --- a/docs/api/primitive-catalog.md +++ b/docs/api/primitive-catalog.md @@ -15,7 +15,7 @@ Every subpath this package declares in `package.json` `exports`. Reach for these ### Root — task lifecycle, conversation, RSI verbs, observability -Import from `@tangle-network/agent-runtime` — 310 exports. +Import from `@tangle-network/agent-runtime` — 317 exports. | Symbol | Kind | Summary | |---|---|---| @@ -36,6 +36,7 @@ Import from `@tangle-network/agent-runtime` — 310 exports. | `createIterableBackend` | function | Wrap any custom async-iterable stream into a typed `AgentExecutionBackend`. | | `createOpenAICompatibleBackend` | function | OpenAI-compat streaming backend. Routes `runAgentTaskStream` through any | | `createOtelExporter` | function | Create an OTEL exporter. Returns undefined when no endpoint is configured. | +| `createProtectedAgentCandidateModelPort` | function | Bind a protected model-grant service to the immutable candidate runtime. | | `createRuntimeEventCollector` | function | Build an in-memory collector that sanitizes and accumulates `AgentRuntimeEvent`s for inspection. | | `createRuntimeStreamEventCollector` | function | Streaming-event counterpart of `createRuntimeEventCollector`. Pass each | | `createSandboxPromptBackend` | function | Build an `AgentExecutionBackend` backed by a sandbox/sidecar `streamPrompt` call. | @@ -150,6 +151,7 @@ Import from `@tangle-network/agent-runtime` — 310 exports. | `AgentCandidateExecutorRequest` | interface | One detached request passed to the trusted environment-specific executor. | | `AgentCandidateExecutorStopRequest` | interface | Opaque process identity used for termination without re-exposing launch credentials. | | `AgentCandidateExecutorTaskOutcomeCapture` | interface | Raw evaluator capture made only after the candidate process is dead. | +| `AgentCandidateModelGrantClient` | interface | Narrow transport contract for a service that owns scoped model credentials | | `AgentCandidateOutputArtifactPort` | interface | Durable content-addressed evidence store controlled only by the evaluator. | | `AgentCandidateProtectedModelCall` | interface | One evaluator-gateway call in the final, revoked model-access ledger. | | `AgentCandidateRepositoryPort` | interface | Resolves a declared GitHub repository to an already-present local Git object store. | @@ -186,6 +188,7 @@ Import from `@tangle-network/agent-runtime` — 310 exports. | `AgentCandidateExecutionStageResult` | type | Result of durably staging the one immutable terminal outbox entry. | | `AgentCandidateExecutionTerminalRecord` | type | Durable terminal record for one acquired execution attempt. | | `AgentCandidateExecutionTerminalResult` | type | Evaluator-owned terminal facts staged durably before the terminal CAS. | +| `AgentCandidateModelGrantReservation` | type | Secret-free response from the service's reservation endpoint. | | `AgentCandidateModelLimits` | type | Limits mechanically enforced by the evaluator-owned model gateway. | | `AgentEvalErrorCode` | type | Error taxonomy for `@tangle-network/agent-eval`. | | `ImproveSurface` | type | The agent-profile lever `improve` optimizes. Mirrors the AgentProfile-law | @@ -201,7 +204,7 @@ Import from `@tangle-network/agent-runtime` — 310 exports. | `ToolLoopStopReason` | type | Why the loop stopped. `completed` = model finished naturally; `stuck-loop` = | | `Verifier` | type | Verifies the edited worktree. Sync or async; throws only on a setup fault | -**Undocumented supporting types** (add a TSDoc line at the declaration to earn a table row): `AgentAdapter`, `AgentBackendContext`, `AgentBackendInput`, `AgentCandidateContainerPort`, `AgentCandidateExecutionAttemptRef`, `AgentCandidateExecutionPorts`, `AgentCandidateExecutorProfileFile`, `AgentCandidateExecutorWorkspaceFile`, `AgentCandidateExecutorWorkspaceInput`, `AgentCandidateMemoryPort`, `AgentCandidateMemoryResetResult`, `AgentCandidateModelPort`, `AgentCandidateProtectedModelActivation`, `AgentCandidateProtectedModelReservation`, `AgentCandidateProtectedModelSettlement`, `AgentCandidateProtectedRunCapture`, `AgentCandidateTaskExecution`, `AgentCandidateVerificationPorts`, `AgentExecutionBackend`, `AgenticGeneratorOptions`, `AgentKnowledgeProvider`, `AgentKnowledgeReadinessCheckOptions`, `AgentTaskContext`, `AgentTaskRunResult`, `AgentTaskSpec`, `BackendCallPolicy`, `CanonicalCandidateDocument`, `ChatTurnHooks`, `ChatTurnResult`, `ControlBudget`, `ControlEvalResult`, `ControlRunResult`, `ControlStep`, `Conversation`, `ConversationDriveState`, `ConversationJournal`, `ConversationParticipant`, `ConversationPolicy`, `ConversationResult`, `ConversationTurn`, `D1StmtLike`, `DataAcquisitionPlan`, `DelegatedLoopResult`, `DisposePreparedAgentCandidateOptions`, `EvalRunEvent`, `EvalRunGeneration`, `EvalRunsExportConfig`, `EvalRunsExportResult`, `ExecutePreparedAgentCandidateOptions`, `FileAgentCandidateExecutionClaimStoreOptions`, `HaltContext`, `HaltSignal`, `ImprovementDriverOptions`, `ImproveOptions`, `ImproveResult`, `KnowledgeImprovementJobMeasurement`, `KnowledgeImprovementJobResult`, `KnowledgeReadinessCheckInput`, `KnowledgeReadinessReport`, `KnowledgeRequirement`, `LoopRunnerCliArgs`, `LoopRunnerCliResult`, `OtelAttribute`, `OtelExporter`, `OtelSpan`, `PersonaConversationResult`, `PrepareAgentCandidateExecutionOptions`, `PreparedAgentCandidateExecution`, `PreparedAgentCandidateInstruction`, `PreparedAgentCandidateLaunch`, `PreparedAgentCandidateTrace`, `RecoverExpiredAgentCandidateOptions`, `ResearchLoopResult`, `ResearchLoopRunnerOptions`, `ResolveAgentBackendOptions`, `ResolvedAgentCandidateContainer`, `ResolvedChatModel`, `RunChatTurnInput`, `RunConversationOptions`, `RunDelegatedLoopOptions`, `RunKnowledgeImprovementJobOptions`, `RunPersonaConfig`, `RunPersonaConversationOptions`, `RuntimeDecisionEvidenceRef`, `RuntimeDecisionPoint`, `RuntimeEventCollector`, `RuntimeHookContext`, `RuntimeHookErrorContext`, `RuntimeHookEvent`, `RuntimeRunHandle`, `RuntimeRunPersistenceAdapter`, `RuntimeRunRow`, `RuntimeSessionStore`, `RuntimeStreamEventCollector`, `RuntimeTelemetryOptions`, `RunToolLoopOptions`, `SanitizedKnowledgeReadinessReport`, `StreamToolLoopOptions`, `SupervisedKnowledgeUpdateInput`, `SupervisedKnowledgeUpdateOptions`, `SupervisedKnowledgeUpdateResult`, `ToolLoopResult`, `VerifiedAgentCandidate`, `VetoedFact`, `WorktreeLoopRunnerOptions`, `AgentCandidateOutputPurpose`, `AgentCandidateRetryRejection`, `AgentCandidateRunFinalization`, `AgentRuntimeEvent`, `AgentRuntimeEventSink`, `AgentTaskStatus`, `AuthSource`, `ControlDecision`, `ConversationStreamEvent`, `DelegatedLoopMode`, `DelegatedLoopRegistry`, `DelegatedLoopRunner`, `ForwardHeaderName`, `HaltPredicate`, `HaltReason`, `KnowledgeReadinessCheck`, `KnowledgeReadinessCheckResult`, `RuntimeDecisionKind`, `RuntimeHookTarget`, `RuntimeStreamEvent`, `StreamToolLoopYield`, `SupervisedKnowledgeUpdater`, `ToolLoopEvent`, `TurnOrder`. +**Undocumented supporting types** (add a TSDoc line at the declaration to earn a table row): `AgentAdapter`, `AgentBackendContext`, `AgentBackendInput`, `AgentCandidateContainerPort`, `AgentCandidateExecutionAttemptRef`, `AgentCandidateExecutionPorts`, `AgentCandidateExecutorProfileFile`, `AgentCandidateExecutorWorkspaceFile`, `AgentCandidateExecutorWorkspaceInput`, `AgentCandidateMemoryPort`, `AgentCandidateMemoryResetResult`, `AgentCandidateModelPort`, `AgentCandidateProtectedModelActivation`, `AgentCandidateProtectedModelReservation`, `AgentCandidateProtectedModelSettlement`, `AgentCandidateProtectedRunCapture`, `AgentCandidateTaskExecution`, `AgentCandidateVerificationPorts`, `AgentExecutionBackend`, `AgenticGeneratorOptions`, `AgentKnowledgeProvider`, `AgentKnowledgeReadinessCheckOptions`, `AgentTaskContext`, `AgentTaskRunResult`, `AgentTaskSpec`, `BackendCallPolicy`, `CanonicalCandidateDocument`, `ChatTurnHooks`, `ChatTurnResult`, `ControlBudget`, `ControlEvalResult`, `ControlRunResult`, `ControlStep`, `Conversation`, `ConversationDriveState`, `ConversationJournal`, `ConversationParticipant`, `ConversationPolicy`, `ConversationResult`, `ConversationTurn`, `CreateProtectedAgentCandidateModelPortOptions`, `D1StmtLike`, `DataAcquisitionPlan`, `DelegatedLoopResult`, `DisposePreparedAgentCandidateOptions`, `EvalRunEvent`, `EvalRunGeneration`, `EvalRunsExportConfig`, `EvalRunsExportResult`, `ExecutePreparedAgentCandidateOptions`, `FileAgentCandidateExecutionClaimStoreOptions`, `HaltContext`, `HaltSignal`, `ImprovementDriverOptions`, `ImproveOptions`, `ImproveResult`, `KnowledgeImprovementJobMeasurement`, `KnowledgeImprovementJobResult`, `KnowledgeReadinessCheckInput`, `KnowledgeReadinessReport`, `KnowledgeRequirement`, `LoopRunnerCliArgs`, `LoopRunnerCliResult`, `OtelAttribute`, `OtelExporter`, `OtelSpan`, `PersonaConversationResult`, `PrepareAgentCandidateExecutionOptions`, `PreparedAgentCandidateExecution`, `PreparedAgentCandidateInstruction`, `PreparedAgentCandidateLaunch`, `PreparedAgentCandidateTrace`, `RecoverExpiredAgentCandidateOptions`, `ResearchLoopResult`, `ResearchLoopRunnerOptions`, `ResolveAgentBackendOptions`, `ResolvedAgentCandidateContainer`, `ResolvedChatModel`, `RunChatTurnInput`, `RunConversationOptions`, `RunDelegatedLoopOptions`, `RunKnowledgeImprovementJobOptions`, `RunPersonaConfig`, `RunPersonaConversationOptions`, `RuntimeDecisionEvidenceRef`, `RuntimeDecisionPoint`, `RuntimeEventCollector`, `RuntimeHookContext`, `RuntimeHookErrorContext`, `RuntimeHookEvent`, `RuntimeRunHandle`, `RuntimeRunPersistenceAdapter`, `RuntimeRunRow`, `RuntimeSessionStore`, `RuntimeStreamEventCollector`, `RuntimeTelemetryOptions`, `RunToolLoopOptions`, `SanitizedKnowledgeReadinessReport`, `StreamToolLoopOptions`, `SupervisedKnowledgeUpdateInput`, `SupervisedKnowledgeUpdateOptions`, `SupervisedKnowledgeUpdateResult`, `ToolLoopResult`, `VerifiedAgentCandidate`, `VetoedFact`, `WorktreeLoopRunnerOptions`, `AgentCandidateModelGrantActivateInput`, `AgentCandidateModelGrantReserveInput`, `AgentCandidateModelGrantSettleInput`, `AgentCandidateOutputPurpose`, `AgentCandidateRetryRejection`, `AgentCandidateRunFinalization`, `AgentRuntimeEvent`, `AgentRuntimeEventSink`, `AgentTaskStatus`, `AuthSource`, `ControlDecision`, `ConversationStreamEvent`, `DelegatedLoopMode`, `DelegatedLoopRegistry`, `DelegatedLoopRunner`, `ForwardHeaderName`, `HaltPredicate`, `HaltReason`, `KnowledgeReadinessCheck`, `KnowledgeReadinessCheckResult`, `RuntimeDecisionKind`, `RuntimeHookTarget`, `RuntimeStreamEvent`, `StreamToolLoopYield`, `SupervisedKnowledgeUpdater`, `ToolLoopEvent`, `TurnOrder`. ### Vertical agent — manifest + improvement adapter diff --git a/docs/canonical-api.md b/docs/canonical-api.md index afb5a79e..c1be542f 100644 --- a/docs/canonical-api.md +++ b/docs/canonical-api.md @@ -2,7 +2,7 @@ -> **Version 0.90.1.** The export inventory + per-symbol signatures live in the generated `docs/api/` reference: **`docs/api/primitive-catalog.md`** is the never-stale, grouped list of every primitive to reuse (own surface + the agent-eval judge / authenticity / verification / statistics / campaign / token-usage surfaces), with each one's import path and one-line summary read live from source; the per-module pages hold the full signatures. The pinned substrate is agent-eval `>=0.101.0 <1.0.0`; the sandbox substrate that materializes profiles into harness shapes is `@tangle-network/sandbox` (peer `>=0.8.0 <1.0.0`). The neutral contract types (`AgentProfile`, `AgentProfileMcpServer`, `HarnessType`, `ReasoningEffort`, `Part`/`ToolPart`/`ToolState`, plus environment-provider types) are owned by **`@tangle-network/agent-interface`** (peer `>=0.22.0 <1.0.0`) — the single source of truth. Substrate primitives are re-exported through `@tangle-network/agent-eval/contract` (or `/campaign`), not local to this package — the catalog's §2 shows exactly which subpath each lives under. +> **Version 0.90.1.** The export inventory + per-symbol signatures live in the generated `docs/api/` reference: **`docs/api/primitive-catalog.md`** is the never-stale, grouped list of every primitive to reuse (own surface + the agent-eval judge / authenticity / verification / statistics / campaign / token-usage surfaces), with each one's import path and one-line summary read live from source; the per-module pages hold the full signatures. The pinned substrate is agent-eval `>=0.101.0 <1.0.0`; the sandbox substrate that materializes profiles into harness shapes is `@tangle-network/sandbox` (peer `>=0.8.0 <1.0.0`). The neutral contract types (`AgentProfile`, `AgentProfileMcpServer`, `HarnessType`, `ReasoningEffort`, `Part`/`ToolPart`/`ToolState`, plus environment-provider types) are owned by **`@tangle-network/agent-interface`** (peer `>=0.24.0 <1.0.0`) — the single source of truth. Substrate primitives are re-exported through `@tangle-network/agent-eval/contract` (or `/campaign`), not local to this package — the catalog's §2 shows exactly which subpath each lives under. > > **`./loops` is the runtime barrel** — `package.json` maps it to `src/runtime/index.ts`. Everything below labelled `/loops` is the recursive-atom + loop-kernel surface. > diff --git a/package.json b/package.json index 0d52b565..dd476291 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "devDependencies": { "@biomejs/biome": "^2.4.15", "@tangle-network/agent-eval": "^0.108.1", - "@tangle-network/agent-interface": "^0.22.0", + "@tangle-network/agent-interface": "^0.24.0", "@tangle-network/sandbox": "^0.9.7", "@types/node": "^25.9.3", "playwright": "^1.61.0", @@ -130,7 +130,7 @@ "packageManager": "pnpm@10.28.0", "peerDependencies": { "@tangle-network/agent-eval": ">=0.101.0 <1.0.0", - "@tangle-network/agent-interface": ">=0.22.0 <1.0.0", + "@tangle-network/agent-interface": ">=0.24.0 <1.0.0", "@tangle-network/sandbox": ">=0.8.0 <1.0.0", "playwright": "^1.40.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba8accf6..326efe80 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,8 +22,8 @@ importers: specifier: ^0.108.1 version: 0.108.1(typescript@5.9.3) '@tangle-network/agent-interface': - specifier: ^0.22.0 - version: 0.22.0 + specifier: ^0.24.0 + version: 0.24.0 '@tangle-network/sandbox': specifier: ^0.9.7 version: 0.9.7(viem@2.54.6(typescript@5.9.3)(zod@4.4.3)) @@ -660,8 +660,8 @@ packages: '@tangle-network/agent-interface@0.21.0': resolution: {integrity: sha512-jDxhVJgxymrvU1RLWxWKueuaWQIpBAfrW8BuVTB5m2Y4eFMLo1SawDBEMDLdZN4/Gf34xrFrsRk+PAj9brGKMQ==} - '@tangle-network/agent-interface@0.22.0': - resolution: {integrity: sha512-7fsJhNdvTmOB1X9E2owl06jzyrqaN+jMkOPVKbK7dvNqQvf627PowCtL/edhUqEEv+K0FWtkwG3R3xqjX7VNZg==} + '@tangle-network/agent-interface@0.24.0': + resolution: {integrity: sha512-iaHWNTYne49cBYXwb72NjGDw5bjT2KVJlfawXygbvkqnfUsQG57BS++Yjf/XYvwJJdDeEaGs+Xvy8SWYkvu0qA==} '@tangle-network/agent-knowledge@1.11.1': resolution: {integrity: sha512-2vvNHHsb4TQFnY+SrL7rYaMhwTxTK8R/lD9IAteonmgwcE5amhpPQZqftw+UDMl1d6OBe4QX+Qv1pNmWQDXzqQ==} @@ -1659,7 +1659,7 @@ snapshots: dependencies: zod: 4.4.3 - '@tangle-network/agent-interface@0.22.0': + '@tangle-network/agent-interface@0.24.0': dependencies: zod: 4.4.3 diff --git a/src/candidate-execution/execute.ts b/src/candidate-execution/execute.ts index 524bc5d7..e9e8339e 100644 --- a/src/candidate-execution/execute.ts +++ b/src/candidate-execution/execute.ts @@ -23,6 +23,7 @@ import { } from './executor-capture' import { failedAgentCandidateRun, finalizeAgentCandidateRun } from './finalize' import { + appendAuthoritativeModelSettlementSpans, type SealedAgentCandidateModelSettlement, sealAgentCandidateModelSettlement, } from './model-settlement' @@ -375,6 +376,11 @@ export async function executePreparedAgentCandidate( ) result = await withinCandidateResultDeadline( async (signal) => { + await appendAuthoritativeModelSettlementSpans( + options.traceStore, + state.trace.runId, + settlementResult.settlement as SealedAgentCandidateModelSettlement, + ) const taskOutcome = await persistVerifiedCandidateTaskOutcome( state, execution.finalCapture.taskOutcome!, diff --git a/src/candidate-execution/index.ts b/src/candidate-execution/index.ts index 9627adad..6dad4f9a 100644 --- a/src/candidate-execution/index.ts +++ b/src/candidate-execution/index.ts @@ -36,6 +36,15 @@ export { type PrepareAgentCandidateExecutionOptions, prepareAgentCandidateExecution, } from './prepare' +export { + type AgentCandidateModelGrantActivateInput, + type AgentCandidateModelGrantClient, + type AgentCandidateModelGrantReservation, + type AgentCandidateModelGrantReserveInput, + type AgentCandidateModelGrantSettleInput, + type CreateProtectedAgentCandidateModelPortOptions, + createProtectedAgentCandidateModelPort, +} from './protected-model-port' export { type RecoverExpiredAgentCandidateOptions, recoverExpiredAgentCandidateExecution, diff --git a/src/candidate-execution/model-settlement.ts b/src/candidate-execution/model-settlement.ts index 4a35f18e..e188f4d2 100644 --- a/src/candidate-execution/model-settlement.ts +++ b/src/candidate-execution/model-settlement.ts @@ -1,4 +1,4 @@ -import type { LlmSpan } from '@tangle-network/agent-eval' +import { isLlmSpan, type LlmSpan, type TraceStore } from '@tangle-network/agent-eval' import type { AgentCandidateSpend } from '@tangle-network/agent-interface' import type { AgentCandidateExecutionUsage } from './claim' import { assertExactObjectKeys } from './exact-object' @@ -48,12 +48,31 @@ export function sealAgentCandidateModelSettlement( const calls = settlement.calls.map((source, index) => { assertExactObjectKeys( source, - ['callId', 'traceSpanId', 'model', 'inputTokens', 'outputTokens', 'costUsdNanos'], + [ + 'callId', + 'generationId', + 'traceSpanId', + 'status', + 'model', + 'startedAtMs', + 'endedAtMs', + 'inputTokens', + 'outputTokens', + 'cachedInputTokens', + 'reasoningTokens', + 'costUsdNanos', + ], `model settlement call ${index}`, - ['cachedInputTokens', 'reasoningTokens'], ) assertIdentifier(source.callId, `model settlement call ${index} callId`) + assertIdentifier(source.generationId, `model settlement call ${index} generationId`) assertIdentifier(source.traceSpanId, `model settlement call ${index} traceSpanId`) + if (source.traceSpanId !== source.generationId) { + throw new Error(`model settlement call ${index} traceSpanId is not its router generationId`) + } + if (source.status !== 'succeeded' && source.status !== 'failed') { + throw new Error(`model settlement call ${index} has an invalid status`) + } if (callIds.has(source.callId)) throw new Error('protected model settlement has duplicate call ids') if (spanIds.has(source.traceSpanId)) { @@ -64,21 +83,22 @@ export function sealAgentCandidateModelSettlement( if (source.model !== expected.model) { throw new Error(`protected model settlement call ${index} has an unexpected model`) } + assertTimestamp(source.startedAtMs, `model settlement call ${index} startedAtMs`) + assertTimestamp(source.endedAtMs, `model settlement call ${index} endedAtMs`) + if (source.endedAtMs < source.startedAtMs) { + throw new Error(`model settlement call ${index} ended before it started`) + } assertCount(source.inputTokens, `model settlement call ${index} inputTokens`) assertCount(source.outputTokens, `model settlement call ${index} outputTokens`) - if (source.cachedInputTokens !== undefined) { - assertCount(source.cachedInputTokens, `model settlement call ${index} cachedInputTokens`) - cachedInputTokens = safeAdd( - cachedInputTokens, - source.cachedInputTokens, - 'cached input token total', - ) - hasCachedInput = true - } - if (source.reasoningTokens !== undefined) { - assertCount(source.reasoningTokens, `model settlement call ${index} reasoningTokens`) - reasoningTokens = safeAdd(reasoningTokens, source.reasoningTokens, 'reasoning token total') - } + assertCount(source.cachedInputTokens, `model settlement call ${index} cachedInputTokens`) + cachedInputTokens = safeAdd( + cachedInputTokens, + source.cachedInputTokens, + 'cached input token total', + ) + hasCachedInput = true + assertCount(source.reasoningTokens, `model settlement call ${index} reasoningTokens`) + reasoningTokens = safeAdd(reasoningTokens, source.reasoningTokens, 'reasoning token total') assertCount(source.costUsdNanos, `model settlement call ${index} costUsdNanos`) inputTokens = safeAdd(inputTokens, source.inputTokens, 'input token total') outputTokens = safeAdd(outputTokens, source.outputTokens, 'output token total') @@ -114,6 +134,57 @@ export function sealAgentCandidateModelSettlement( }) } +/** + * Append the only accepted LLM spans from the router's closed ledger. + * Candidate and executor code may write tool/process spans, but never model usage. + */ +export async function appendAuthoritativeModelSettlementSpans( + traceStore: TraceStore, + runId: string, + settlement: SealedAgentCandidateModelSettlement, +): Promise { + const run = await traceStore.getRun(runId) + if (!run) throw new Error(`protected trace run is missing before model settlement: ${runId}`) + if (run.status === 'running' || run.endedAt === undefined) { + throw new Error('protected trace run must be terminal before model spans are appended') + } + const existing = await traceStore.spans({ runId }) + if (existing.some(isLlmSpan)) { + throw new Error( + 'protected trace contains a model span not authored from the closed router ledger', + ) + } + const occupiedIds = new Set(existing.map((span) => span.spanId)) + for (const call of settlement.value.calls) { + if (occupiedIds.has(call.traceSpanId)) { + throw new Error( + `protected trace span identity collides with router generation ${call.generationId}`, + ) + } + await traceStore.appendSpan({ + runId, + spanId: call.traceSpanId, + kind: 'llm', + name: 'protected model call', + model: call.model, + messages: [], + startedAt: call.startedAtMs, + endedAt: call.endedAtMs, + status: call.status === 'succeeded' ? 'ok' : 'error', + inputTokens: call.inputTokens, + outputTokens: call.outputTokens, + cachedTokens: call.cachedInputTokens, + reasoningTokens: call.reasoningTokens, + costUsd: call.costUsdNanos / USD_NANOS, + attributes: { + 'tangle.protected_model.source': 'router-settlement', + 'tangle.router.call_id': call.callId, + 'tangle.router.generation_id': call.generationId, + }, + }) + } +} + /** Match every protected trace span one-for-one against gateway call evidence. */ export function assertTraceMatchesModelSettlement( spans: readonly LlmSpan[], @@ -146,11 +217,27 @@ function assertTraceCall(span: LlmSpan, call: AgentCandidateProtectedModelCall): if (span.model !== call.model) { throw new Error(`protected trace span ${span.spanId} model does not match model ledger`) } + if ( + span.startedAt !== call.startedAtMs || + span.endedAt !== call.endedAtMs || + span.status !== (call.status === 'succeeded' ? 'ok' : 'error') + ) { + throw new Error( + `protected trace span ${span.spanId} timing or status does not match model ledger`, + ) + } + if ( + span.attributes?.['tangle.protected_model.source'] !== 'router-settlement' || + span.attributes?.['tangle.router.call_id'] !== call.callId || + span.attributes?.['tangle.router.generation_id'] !== call.generationId + ) { + throw new Error(`protected trace span ${span.spanId} lacks router settlement provenance`) + } for (const [name, traced, settled] of [ ['inputTokens', span.inputTokens, call.inputTokens], ['outputTokens', span.outputTokens, call.outputTokens], - ['cachedInputTokens', span.cachedTokens ?? 0, call.cachedInputTokens ?? 0], - ['reasoningTokens', span.reasoningTokens ?? 0, call.reasoningTokens ?? 0], + ['cachedInputTokens', span.cachedTokens ?? 0, call.cachedInputTokens], + ['reasoningTokens', span.reasoningTokens ?? 0, call.reasoningTokens], ] as const) { if (traced === undefined || traced !== settled) { throw new Error( @@ -181,6 +268,12 @@ function assertCount(value: unknown, label: string): asserts value is number { } } +function assertTimestamp(value: unknown, label: string): asserts value is number { + if (!Number.isSafeInteger(value) || (value as number) <= 0) { + throw new Error(`${label} must be a positive safe integer`) + } +} + function safeAdd(left: number, right: number, label: string): number { const total = left + right if (!Number.isSafeInteger(total)) throw new Error(`${label} exceeds safe integer range`) diff --git a/src/candidate-execution/outcome-evidence.ts b/src/candidate-execution/outcome-evidence.ts index c2f7c821..a4fd66e2 100644 --- a/src/candidate-execution/outcome-evidence.ts +++ b/src/candidate-execution/outcome-evidence.ts @@ -74,7 +74,7 @@ export async function persistCandidateModelSettlementEvidence( outputArtifacts: AgentCandidateOutputArtifactPort, ): Promise { const material = { - schemaVersion: 1 as const, + schemaVersion: 2 as const, kind: 'agent-candidate-model-settlement-material' as const, executionPlanDigest: identity.executionPlanDigest, preparationId: settlement.value.preparationId, @@ -83,8 +83,12 @@ export async function persistCandidateModelSettlementEvidence( resolved: identity.resolvedModel, calls: settlement.value.calls.map((call) => ({ callId: call.callId, + generationId: call.generationId, traceSpanId: call.traceSpanId, + status: call.status, model: call.model, + startedAtMs: call.startedAtMs, + endedAtMs: call.endedAtMs, inputTokens: call.inputTokens, outputTokens: call.outputTokens, cachedInputTokens: call.cachedInputTokens ?? 0, diff --git a/src/candidate-execution/prepare.ts b/src/candidate-execution/prepare.ts index 0a19a929..2761dfa5 100644 --- a/src/candidate-execution/prepare.ts +++ b/src/candidate-execution/prepare.ts @@ -9,6 +9,7 @@ import type { AgentCandidateExecutionPlanEvidence, AgentCandidateExecutionPlanMaterialV1, AgentCandidateMaterializationReceipt, + AgentCandidateModelAccessNetwork, AgentCandidateResolvedModel, HarnessType, } from '@tangle-network/agent-interface' @@ -18,6 +19,7 @@ import { agentCandidateExecutionPlanEvidenceSchema, agentCandidateExecutionPlanMaterialSchema, agentCandidateMaterializationReceiptSchema, + agentCandidateModelAccessNetworkSchema, agentCandidateWorkspaceSnapshotEvidenceSchema, sha256DigestSchema, } from '@tangle-network/agent-interface' @@ -280,7 +282,11 @@ export async function prepareAgentCandidateExecution( model: { policy: 'single', resolved: resolvedModel, - access: { kind: 'evaluator-mediated', grantDigest: modelReservation.digest }, + access: { + kind: 'evaluator-mediated', + grantDigest: modelReservation.digest, + network: modelReservation.network, + }, routes, }, launch: { @@ -383,6 +389,7 @@ export async function prepareAgentCandidateExecution( digest: modelReservation.digest, expiresAtMs: modelReservation.expiresAtMs, enforcedLimits: modelReservation.enforcedLimits, + network: modelReservation.network, }, executorInputs: { taskFiles: taskExecutorFiles, @@ -875,6 +882,7 @@ function validateProtectedModelReservation( maxOutputTokens: number maxCostUsd: number } + network: AgentCandidateModelAccessNetwork }, expectedLimits: AgentCandidateExecutionLimits, preparationId: string, @@ -890,6 +898,11 @@ function validateProtectedModelReservation( if (canonicalCandidateDigest(reservation.enforcedLimits) !== canonicalCandidateDigest(limits)) { throw new Error('protected model reservation does not enforce the frozen model limits') } + const expectedNetworkMode = limits.maxModelCalls === 0 ? 'disabled' : 'gateway-only' + const network = agentCandidateModelAccessNetworkSchema.parse(reservation.network) + if (network.mode !== expectedNetworkMode) { + throw new Error('protected model reservation has the wrong network policy for its call limit') + } } function assertEnvironmentDisjoint( diff --git a/src/candidate-execution/prepared-state.ts b/src/candidate-execution/prepared-state.ts index d241ce2d..87522785 100644 --- a/src/candidate-execution/prepared-state.ts +++ b/src/candidate-execution/prepared-state.ts @@ -350,6 +350,8 @@ function assertPrivateCandidateIntegrity(state: PreparedCandidateState): void { state.modelReservation.digest !== state.executionPlan.value.material.model.access.grantDigest || state.modelReservation.preparationId !== state.preparationId || state.modelReservation.expiresAtMs !== state.reservationExpiresAtMs || + canonicalCandidateDigest(state.modelReservation.network) !== + canonicalCandidateDigest(state.executionPlan.value.material.model.access.network) || canonicalCandidateDigest(state.modelReservation.enforcedLimits) !== canonicalCandidateDigest(modelLimits(state.executionPlan.value.material.limits)) ) { diff --git a/src/candidate-execution/protected-model-port.ts b/src/candidate-execution/protected-model-port.ts new file mode 100644 index 00000000..e69194e1 --- /dev/null +++ b/src/candidate-execution/protected-model-port.ts @@ -0,0 +1,504 @@ +import type { + AgentCandidateModelAccessNetwork, + AgentCandidateResolvedModel, + Sha256Digest, +} from '@tangle-network/agent-interface' + +import { canonicalCandidateDigest, immutableCandidateValue } from './digest' +import { assertExactObjectKeys } from './exact-object' +import { sealAgentCandidateModelSettlement, usdToNanos } from './model-settlement' +import type { + AgentCandidateModelLimits, + AgentCandidateModelPort, + AgentCandidateProtectedModelActivation, + AgentCandidateProtectedModelReservation, + AgentCandidateProtectedModelSettlement, +} from './types' + +export type AgentCandidateModelGrantReserveInput = Parameters< + AgentCandidateModelPort['reserveGrant'] +>[0] +export type AgentCandidateModelGrantActivateInput = Parameters< + AgentCandidateModelPort['activateGrant'] +>[0] +export type AgentCandidateModelGrantSettleInput = Parameters< + AgentCandidateModelPort['settleGrant'] +>[0] + +/** Secret-free response from the service's reservation endpoint. */ +export type AgentCandidateModelGrantReservation = AgentCandidateProtectedModelReservation + +/** + * Narrow transport contract for a service that owns scoped model credentials + * and the authoritative per-call usage ledger. + * + * An HTTP client can bind these methods to control-plane endpoints. Keeping + * transport out of the runtime prevents parent credentials, endpoint paths, + * and retry policy from becoming part of the portable candidate contract. + */ +export interface AgentCandidateModelGrantClient { + reserve(input: AgentCandidateModelGrantReserveInput): Promise + activate( + input: AgentCandidateModelGrantActivateInput, + ): Promise + settle( + input: AgentCandidateModelGrantSettleInput, + ): Promise +} + +export interface CreateProtectedAgentCandidateModelPortOptions { + client: AgentCandidateModelGrantClient + /** Catalog/snapshot resolution stays separate from credential issuance. */ + resolveModel: AgentCandidateModelPort['resolve'] + /** The only public DNS name candidate processes may reach for inference. */ + gatewayDomain: string + /** Exact environment names the activation endpoint must return, no more or fewer. */ + activationEnvNames: readonly string[] +} + +interface RememberedReservation { + requestDigest: Sha256Digest + responseDigest: Sha256Digest + executionId: string + preparationId: string + grantDigest: Sha256Digest + expiresAtMs: number + resolved: AgentCandidateResolvedModel + limits: AgentCandidateModelLimits + maxCostUsdNanos: number + activation: 'reserved' | 'activating' | 'activated' + settlementDigest?: Sha256Digest + settlementReason?: AgentCandidateModelGrantSettleInput['reason'] +} + +interface RememberedSettlement { + settlementDigest: Sha256Digest + settlementReason: AgentCandidateModelGrantSettleInput['reason'] + expiresAtMs: number +} + +const MAX_RECENT_SETTLEMENTS = 4_096 +const RECOVERED_SETTLEMENT_RETENTION_MS = 15 * 60 * 1_000 + +/** + * Bind a protected model-grant service to the immutable candidate runtime. + * + * The service remains the authority for expiry, admission, revocation, and + * metering. This adapter independently checks every response before allowing + * it to cross into candidate execution or durable receipt finalization. + */ +export function createProtectedAgentCandidateModelPort( + options: CreateProtectedAgentCandidateModelPortOptions, +): AgentCandidateModelPort { + const gatewayDomain = assertGatewayDomain(options.gatewayDomain) + const activationEnvNames = exactEnvironmentNames(options.activationEnvNames) + const reservations = new Map() + const recentSettlements = new Map() + + return { + resolve: async (input) => { + const request = immutableCandidateValue(input) + const resolved = validateResolvedModel(await options.resolveModel(request), request) + return immutableCandidateValue(resolved) + }, + + reserveGrant: async (input) => { + pruneExpiredState(reservations, recentSettlements, Date.now()) + const request = immutableCandidateValue(input) + validateReserveInput(request) + const requestDigest = canonicalCandidateDigest(request) + const key = reservationKey(request.executionId, request.preparationId) + if (recentSettlements.has(key)) { + throw new Error('protected model reservation is already settled') + } + const previous = reservations.get(key) + if (previous && previous.requestDigest !== requestDigest) { + throw new Error('protected model reservation retry changed immutable input') + } + + const reservation = validateReservation( + await options.client.reserve(request), + request, + gatewayDomain, + ) + const responseDigest = canonicalCandidateDigest(reservation) + if (recentSettlements.has(key)) { + throw new Error('protected model reservation completed after the grant was settled') + } + const recorded = previous ?? reservations.get(key) + if (recorded && recorded.requestDigest !== requestDigest) { + throw new Error('protected model reservation retry changed immutable input') + } + if (recorded && recorded.responseDigest !== responseDigest) { + throw new Error('protected model reservation retry returned different evidence') + } + if (!recorded) { + reservations.set(key, { + requestDigest, + responseDigest, + executionId: request.executionId, + preparationId: request.preparationId, + grantDigest: reservation.digest, + expiresAtMs: request.expiresAtMs, + resolved: immutableCandidateValue(request.resolved), + limits: immutableCandidateValue(request.limits), + maxCostUsdNanos: usdToNanos(request.limits.maxCostUsd, 'reserved maxCostUsd'), + activation: 'reserved', + }) + } + return reservation + }, + + activateGrant: async (input) => { + pruneExpiredState(reservations, recentSettlements, Date.now()) + const request = immutableCandidateValue(input) + const key = reservationKey(request.executionId, request.preparationId) + if (recentSettlements.has(key)) throw new Error('protected model grant is already settled') + const state = reservations.get(key) + if (!state) throw new Error('protected model grant was not reserved by this port') + assertGrantIdentity(state, request) + if (state.settlementDigest) throw new Error('protected model grant is already settled') + if (state.activation !== 'reserved') { + throw new Error('protected model grant activation is single-use') + } + if (!Number.isSafeInteger(request.deadlineAtMs) || request.deadlineAtMs <= 0) { + throw new Error('protected model activation deadline must be a positive safe integer') + } + if (request.deadlineAtMs <= Date.now()) { + throw new Error('protected model activation deadline must be in the future') + } + if (request.deadlineAtMs > state.expiresAtMs) { + throw new Error('protected model activation deadline exceeds the reserved expiry') + } + state.activation = 'activating' + try { + const activation = validateActivation( + await options.client.activate(request), + state.limits.maxModelCalls === 0 ? [] : activationEnvNames, + ) + if (recentSettlements.has(key) || reservations.get(key) !== state) { + throw new Error('protected model grant settled while activation was in flight') + } + state.activation = 'activated' + return activation + } catch (error) { + state.activation = 'reserved' + throw error + } + }, + + settleGrant: async (input) => { + const now = Date.now() + pruneExpiredState(reservations, recentSettlements, now) + const request = immutableCandidateValue(input) + const key = reservationKey(request.executionId, request.preparationId) + const state = reservations.get(key) + if (state) assertGrantIdentity(state, request) + const remembered = state ?? recentSettlements.get(key) + if (remembered?.settlementReason && remembered.settlementReason !== request.reason) { + throw new Error('protected model settlement retry changed the termination reason') + } + + const sealed = sealAgentCandidateModelSettlement(await options.client.settle(request), { + preparationId: request.preparationId, + grantDigest: request.grantDigest, + model: request.resolved.model, + }) + if (state) assertWithinReservedLimits(sealed.fixedUsage, state) + + const settlementDigest = canonicalCandidateDigest(sealed.value) + if (remembered?.settlementDigest && remembered.settlementDigest !== settlementDigest) { + throw new Error('protected model settlement retry returned a different final ledger') + } + if (remembered?.settlementReason && remembered.settlementReason !== request.reason) { + throw new Error('protected model settlement retry changed the termination reason') + } + const expiresAtMs = state?.expiresAtMs ?? now + RECOVERED_SETTLEMENT_RETENTION_MS + if (state) { + state.settlementDigest = settlementDigest + state.settlementReason = request.reason + } + reservations.delete(key) + rememberSettlement(recentSettlements, key, { + settlementDigest, + settlementReason: request.reason, + expiresAtMs, + }) + return sealed.value + }, + } +} + +function validateReserveInput(input: AgentCandidateModelGrantReserveInput): void { + if (!Number.isSafeInteger(input.expiresAtMs) || input.expiresAtMs <= 0) { + throw new Error('protected model reservation expiry must be a positive safe integer') + } + if (input.expiresAtMs <= Date.now()) { + throw new Error('protected model reservation expiry must be in the future') + } + for (const [name, value] of [ + ['maxModelCalls', input.limits.maxModelCalls], + ['maxInputTokens', input.limits.maxInputTokens], + ['maxOutputTokens', input.limits.maxOutputTokens], + ] as const) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`protected model reservation ${name} must be a nonnegative safe integer`) + } + } + usdToNanos(input.limits.maxCostUsd, 'reserved maxCostUsd') +} + +function validateReservation( + value: unknown, + expected: AgentCandidateModelGrantReserveInput, + gatewayDomain: string, +): AgentCandidateModelGrantReservation { + const source = exactRecord( + value, + ['preparationId', 'digest', 'expiresAtMs', 'enforcedLimits', 'network'], + 'protected model reservation response', + ) + if (source.preparationId !== expected.preparationId) { + throw new Error('protected model reservation response changed preparation identity') + } + if (!isSha256Digest(source.digest)) { + throw new Error('protected model reservation response has an invalid digest') + } + if (source.expiresAtMs !== expected.expiresAtMs) { + throw new Error('protected model reservation response changed expiry') + } + exactRecord( + source.enforcedLimits, + ['maxModelCalls', 'maxInputTokens', 'maxOutputTokens', 'maxCostUsd'], + 'protected model reservation enforced limits', + ) + if ( + canonicalCandidateDigest(source.enforcedLimits) !== canonicalCandidateDigest(expected.limits) + ) { + throw new Error('protected model reservation response changed enforced limits') + } + + const network = validateReservationNetwork( + source.network, + expected.limits.maxModelCalls, + gatewayDomain, + ) + return immutableCandidateValue({ + preparationId: source.preparationId, + digest: source.digest, + expiresAtMs: source.expiresAtMs, + enforcedLimits: source.enforcedLimits, + network, + }) as AgentCandidateModelGrantReservation +} + +function validateReservationNetwork( + value: unknown, + maxModelCalls: number, + gatewayDomain: string, +): AgentCandidateModelAccessNetwork { + if (maxModelCalls === 0) { + const source = exactRecord(value, ['mode'], 'protected model reservation network') + if (source.mode !== 'disabled') { + throw new Error('zero-call protected model reservation must disable gateway access') + } + return { mode: 'disabled' } + } + + const source = exactRecord(value, ['mode', 'domains'], 'protected model reservation network') + if (source.mode !== 'gateway-only') { + throw new Error('protected model reservation must allow only its model gateway') + } + if ( + !Array.isArray(source.domains) || + source.domains.length !== 1 || + source.domains[0] !== gatewayDomain + ) { + throw new Error('protected model reservation returned an unexpected gateway domain') + } + return { mode: 'gateway-only', domains: [gatewayDomain] } +} + +function validateActivation( + value: unknown, + expectedNames: readonly string[], +): AgentCandidateProtectedModelActivation { + const source = exactRecord(value, ['env'], 'protected model activation response') + const env = record(source.env, 'protected model activation environment') + const actualNames = Object.keys(env).sort() + if ( + actualNames.length !== expectedNames.length || + actualNames.some((name, index) => name !== expectedNames[index]) + ) { + throw new Error('protected model activation returned unexpected environment names') + } + const detached: Record = Object.create(null) as Record + for (const name of expectedNames) { + const value = env[name] + if (typeof value !== 'string' || value.length < 8 || value.length > 65_536) { + throw new Error(`protected model activation ${name} must be a non-empty protected value`) + } + detached[name] = value + } + return Object.freeze({ env: Object.freeze(detached) }) +} + +function validateResolvedModel( + value: unknown, + expected: Parameters[0], +): AgentCandidateResolvedModel { + const source = exactRecord( + value, + ['requested', 'provider', 'model', 'snapshot', 'reasoningEffort'], + 'resolved candidate model', + ) + if (source.requested !== expected.requested) { + throw new Error('model resolver changed the evaluator-owned request') + } + if (source.reasoningEffort !== expected.reasoningEffort) { + throw new Error('model resolver changed the evaluator-owned reasoning effort') + } + for (const name of ['requested', 'provider', 'model', 'snapshot'] as const) { + if (typeof source[name] !== 'string' || source[name].length === 0) { + throw new Error(`resolved candidate model ${name} must be non-empty`) + } + } + return source as unknown as AgentCandidateResolvedModel +} + +function assertGrantIdentity( + expected: RememberedReservation, + actual: AgentCandidateModelGrantActivateInput | AgentCandidateModelGrantSettleInput, +): void { + if ( + actual.executionId !== expected.executionId || + actual.preparationId !== expected.preparationId || + actual.grantDigest !== expected.grantDigest || + canonicalCandidateDigest(actual.resolved) !== canonicalCandidateDigest(expected.resolved) + ) { + throw new Error('protected model grant input does not match its immutable reservation') + } +} + +function assertWithinReservedLimits( + usage: { + modelCalls: number + inputTokens: number + outputTokens: number + costUsdNanos: number + }, + reservation: RememberedReservation, +): void { + for (const [name, actual, limit] of [ + ['model calls', usage.modelCalls, reservation.limits.maxModelCalls], + ['input tokens', usage.inputTokens, reservation.limits.maxInputTokens], + ['output tokens', usage.outputTokens, reservation.limits.maxOutputTokens], + ['cost USD nanos', usage.costUsdNanos, reservation.maxCostUsdNanos], + ] as const) { + if (actual > limit) { + throw new Error(`protected model settlement ${name} ${actual} exceeds reserved ${limit}`) + } + } +} + +function exactEnvironmentNames(values: readonly string[]): readonly string[] { + if (values.length === 0) { + throw new Error('protected model activation environment names must not be empty') + } + const names = [...values].sort() + if (new Set(names).size !== names.length) { + throw new Error('protected model activation environment names must be unique') + } + for (const name of names) { + if ( + !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || + ['__proto__', 'constructor', 'prototype'].includes(name) + ) { + throw new Error(`protected model activation environment name is invalid: ${name}`) + } + } + return Object.freeze(names) +} + +function assertGatewayDomain(value: string): string { + if ( + value.length > 253 || + value !== value.toLowerCase() || + value.endsWith('.') || + value.includes(':') || + value === 'localhost' || + value.endsWith('.localhost') || + value === 'metadata.google' || + value === 'metadata.google.internal' + ) { + throw new Error('protected model gateway must be an exact lowercase public DNS name') + } + const labels = value.split('.') + if ( + labels.length < 2 || + !labels.every( + (label) => + label.length >= 1 && label.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(label), + ) || + !/[a-z]/.test(labels.at(-1) ?? '') + ) { + throw new Error('protected model gateway must be an exact lowercase public DNS name') + } + return value +} + +function reservationKey(executionId: string, preparationId: string): string { + return canonicalCandidateDigest({ executionId, preparationId }) +} + +function pruneExpiredState( + reservations: Map, + settlements: Map, + now: number, +): void { + for (const [key, value] of reservations) { + if (value.expiresAtMs <= now) reservations.delete(key) + } + for (const [key, value] of settlements) { + if (value.expiresAtMs <= now) settlements.delete(key) + } +} + +function rememberSettlement( + settlements: Map, + key: string, + value: RememberedSettlement, +): void { + settlements.delete(key) + settlements.set(key, value) + while (settlements.size > MAX_RECENT_SETTLEMENTS) { + const oldest = settlements.keys().next().value + if (oldest === undefined) return + settlements.delete(oldest) + } +} + +function isSha256Digest(value: unknown): value is Sha256Digest { + return typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value) +} + +function exactRecord( + value: unknown, + keys: readonly string[], + label: string, +): Record { + const source = record(value, label) + assertExactObjectKeys(source, keys, label) + return source +} + +function record(value: unknown, label: string): Record { + if (value === null || typeof value !== 'object' || Array.isArray(value)) { + throw new Error(`${label} must be an object`) + } + const prototype = Object.getPrototypeOf(value) + if (prototype !== Object.prototype && prototype !== null) { + throw new Error(`${label} must be a plain object`) + } + return value as Record +} diff --git a/src/candidate-execution/protected-trace-store.ts b/src/candidate-execution/protected-trace-store.ts index 23ae3cde..1026a65f 100644 --- a/src/candidate-execution/protected-trace-store.ts +++ b/src/candidate-execution/protected-trace-store.ts @@ -35,6 +35,9 @@ export class ProtectedAgentCandidateTraceStore implements TraceStore { } async appendSpan(span: Parameters[0]): Promise { + if (span.kind === 'llm') { + throw new Error('candidate executors cannot author protected model spans') + } await this.inner.appendSpan(this.redact(span)) } @@ -42,6 +45,9 @@ export class ProtectedAgentCandidateTraceStore implements TraceStore { spanId: Parameters[0], patch: Parameters[1], ): Promise { + if (patch.kind === 'llm') { + throw new Error('candidate executors cannot author protected model spans') + } await this.inner.updateSpan(this.redact(spanId), this.redact(patch)) } diff --git a/src/candidate-execution/types.ts b/src/candidate-execution/types.ts index 38fa6256..50c52062 100644 --- a/src/candidate-execution/types.ts +++ b/src/candidate-execution/types.ts @@ -12,6 +12,7 @@ import type { AgentCandidateInstructionDelivery, AgentCandidateMaterializationReceipt, AgentCandidateMemoryReceipt, + AgentCandidateModelAccessNetwork, AgentCandidateOciPlatform, AgentCandidateProfilePlanEvidence, AgentCandidateResolvedModel, @@ -157,6 +158,8 @@ export interface AgentCandidateProtectedModelReservation { expiresAtMs: number /** The gateway must stop calls before any one of these limits is exceeded. */ enforcedLimits: AgentCandidateModelLimits + /** Exact public endpoint exception; every other candidate destination stays blocked. */ + network: AgentCandidateModelAccessNetwork } export interface AgentCandidateProtectedModelActivation { @@ -167,13 +170,18 @@ export interface AgentCandidateProtectedModelActivation { /** One evaluator-gateway call in the final, revoked model-access ledger. */ export interface AgentCandidateProtectedModelCall { callId: string - /** Exact protected agent-eval LLM span produced for this call. */ + /** Router-generated public response identity. */ + generationId: string + /** Exact protected agent-eval LLM span produced from the router ledger. */ traceSpanId: string + status: 'succeeded' | 'failed' model: string + startedAtMs: number + endedAtMs: number inputTokens: number outputTokens: number - cachedInputTokens?: number - reasoningTokens?: number + cachedInputTokens: number + reasoningTokens: number /** Integer billionths of one US dollar; avoids floating-point ledger drift. */ costUsdNanos: number } diff --git a/tests/candidate-execution-execute.test.ts b/tests/candidate-execution-execute.test.ts index 8498e989..a6b64cfd 100644 --- a/tests/candidate-execution-execute.test.ts +++ b/tests/candidate-execution-execute.test.ts @@ -111,6 +111,10 @@ describe('atomic prepared candidate execution', () => { expect(request.launch.env.PATH).toBeUndefined() expect(request.hardLimits).toEqual({ timeoutMs: fixture.task.limits.timeoutMs }) expect(request.observedLimits).toEqual({ maxSteps: fixture.task.limits.maxSteps }) + expect(request.executionPlan.value.material.model.access.network).toEqual({ + mode: 'gateway-only', + domains: ['router.tangle.tools'], + }) expect(Buffer.from(request.inputs.task.files[0]?.bytes ?? []).toString('utf8')).toBe( 'export const value = 1\n', ) @@ -153,6 +157,98 @@ describe('atomic prepared candidate execution', () => { ).resolves.toEqual(result.receipt.bytes) }) + it('authors paid model spans only from the closed router ledger', async () => { + const fixture = createCandidateExecutionFixture() + fixture.ports.models.settleGrant = async ({ preparationId }) => ({ + preparationId, + grantDigest: candidateSha('c'), + closed: true, + calls: [ + { + callId: 'call-paid-success', + generationId: 'generation-paid-success', + traceSpanId: 'generation-paid-success', + status: 'succeeded', + model: 'model-snapshot', + startedAtMs: 120, + endedAtMs: 180, + inputTokens: 10, + outputTokens: 5, + cachedInputTokens: 2, + reasoningTokens: 1, + costUsdNanos: 10_000_000, + }, + ], + }) + const prepared = await prepareAgentCandidateExecution( + await verifyAgentCandidateBundle(fixture.bundle, fixture.ports), + fixture.task, + fixture.ports, + ) + const traceStore = new InMemoryTraceStore() + const result = await executePreparedAgentCandidate( + prepared, + options( + { + execute: async (request, context) => { + await expect( + context.traceStore.appendSpan({ + runId: request.trace.runId, + spanId: 'candidate-authored-model-span', + kind: 'llm', + name: 'untrusted model usage', + model: request.resolvedModel.model, + messages: [], + startedAt: 120, + }), + ).rejects.toThrow(/cannot author protected model spans/) + await terminalTrace(request, context.traceStore) + return { + executionId: request.executionId, + termination: { kind: 'exit', exitCode: 0 }, + } + }, + stopAndCapture: async () => ({ stopped: true }), + }, + traceStore, + ), + ) + if (!result.succeeded) throw new Error(result.reason) + expect(result.receipt.value).toMatchObject({ + usage: { modelCalls: 1, inputTokens: 10, outputTokens: 5, costUsd: 0.01 }, + modelSettlement: { + material: { + schemaVersion: 2, + calls: [ + { + generationId: 'generation-paid-success', + traceSpanId: 'generation-paid-success', + startedAtMs: 120, + endedAtMs: 180, + costUsdNanos: 10_000_000, + }, + ], + }, + }, + }) + expect(await traceStore.spans({ runId: prepared.trace.runId })).toEqual([ + expect.objectContaining({ + spanId: 'generation-paid-success', + kind: 'llm', + inputTokens: 10, + outputTokens: 5, + cachedTokens: 2, + reasoningTokens: 1, + costUsd: 0.01, + startedAt: 120, + endedAt: 180, + attributes: expect.objectContaining({ + 'tangle.protected_model.source': 'router-settlement', + }), + }), + ]) + }) + it('rejects pre-launch staging mutation before activation or executor access', async () => { const fixture = createCandidateExecutionFixture(true) const prepared = await prepareAgentCandidateExecution( @@ -760,6 +856,10 @@ describe('atomic prepared candidate execution', () => { digest: candidateSha('c'), expiresAtMs, enforcedLimits: limits, + network: + limits.maxModelCalls === 0 + ? { mode: 'disabled' as const } + : { mode: 'gateway-only' as const, domains: ['router.tangle.tools'] }, } } fixture.ports.models.activateGrant = async ({ preparationId }) => { @@ -855,10 +955,16 @@ describe('atomic prepared candidate execution', () => { calls: [ { callId: 'call-paid-1', - traceSpanId: 'llm-paid-1', + generationId: 'generation-paid-1', + traceSpanId: 'generation-paid-1', + status: 'failed', model: 'model-snapshot', + startedAtMs: 100, + endedAtMs: 150, inputTokens: 10, outputTokens: 5, + cachedInputTokens: 0, + reasoningTokens: 0, costUsdNanos: 10_000_000, }, ], diff --git a/tests/candidate-execution-finalize.test.ts b/tests/candidate-execution-finalize.test.ts index 64c2846d..33707c6b 100644 --- a/tests/candidate-execution-finalize.test.ts +++ b/tests/candidate-execution-finalize.test.ts @@ -73,11 +73,16 @@ async function finalizePrepared( calls: [ { callId: 'call-1', + generationId: 'llm-1', traceSpanId: 'llm-1', + status: 'succeeded', model: execution.resolvedModel.model, + startedAtMs: 120, + endedAtMs: 200, inputTokens: 10, outputTokens: 5, cachedInputTokens: 2, + reasoningTokens: 0, costUsdNanos: 10_000_000, }, ], @@ -162,6 +167,11 @@ async function traceStore( startedAt: 120, endedAt: 200, status: 'ok', + attributes: { + 'tangle.protected_model.source': 'router-settlement', + 'tangle.router.call_id': 'call-1', + 'tangle.router.generation_id': 'llm-1', + }, ...(overrides.omitUsage ? {} : { inputTokens: 10, outputTokens: 5, cachedTokens: 2, costUsd: 0.01 }), @@ -388,6 +398,11 @@ describe('protected candidate run finalization', () => { startedAt: 120 + index * 100, endedAt: 180 + index * 100, status: 'ok', + attributes: { + 'tangle.protected_model.source': 'router-settlement', + 'tangle.router.call_id': `call-${index + 1}`, + 'tangle.router.generation_id': `llm-${index + 1}`, + }, }) } const settlement = sealAgentCandidateModelSettlement( @@ -398,18 +413,30 @@ describe('protected candidate run finalization', () => { calls: [ { callId: 'call-1', + generationId: 'llm-1', traceSpanId: 'llm-1', + status: 'succeeded', model: execution.resolvedModel.model, + startedAtMs: 120, + endedAtMs: 180, inputTokens: 5, outputTokens: 2, + cachedInputTokens: 0, + reasoningTokens: 0, costUsdNanos: 100_000_000, }, { callId: 'call-2', + generationId: 'llm-2', traceSpanId: 'llm-2', + status: 'succeeded', model: execution.resolvedModel.model, + startedAtMs: 220, + endedAtMs: 280, inputTokens: 5, outputTokens: 2, + cachedInputTokens: 0, + reasoningTokens: 0, costUsdNanos: 200_000_000, }, ], diff --git a/tests/candidate-execution-model-port.test.ts b/tests/candidate-execution-model-port.test.ts new file mode 100644 index 00000000..e2464ff5 --- /dev/null +++ b/tests/candidate-execution-model-port.test.ts @@ -0,0 +1,611 @@ +import { InMemoryTraceStore, type LlmSpan } from '@tangle-network/agent-eval' +import type { AgentCandidateResolvedModel, Sha256Digest } from '@tangle-network/agent-interface' +import { describe, expect, it } from 'vitest' +import { + appendAuthoritativeModelSettlementSpans, + assertTraceMatchesModelSettlement, + sealAgentCandidateModelSettlement, +} from '../src/candidate-execution/model-settlement' +import { + type AgentCandidateModelGrantActivateInput, + type AgentCandidateModelGrantClient, + type AgentCandidateModelGrantReservation, + type AgentCandidateModelGrantReserveInput, + type AgentCandidateModelGrantSettleInput, + createProtectedAgentCandidateModelPort, +} from '../src/candidate-execution/protected-model-port' +import type { + AgentCandidateModelPort, + AgentCandidateProtectedModelActivation, + AgentCandidateProtectedModelCall, + AgentCandidateProtectedModelSettlement, +} from '../src/candidate-execution/types' + +const GATEWAY_DOMAIN = 'router.tangle.tools' +const ACTIVATION_ENV_NAMES = ['MODEL_API_KEY', 'MODEL_BASE_URL'] as const +const EXPIRES_AT_MS = 2_000_000_000_000 + +function sha(character: string): Sha256Digest { + return `sha256:${character.repeat(64)}` as Sha256Digest +} + +const resolvedModel: AgentCandidateResolvedModel = { + requested: 'openai/gpt-5.2-codex', + provider: 'openai', + model: 'openai/gpt-5.2-codex-2026-07-01', + snapshot: 'gpt-5.2-codex-2026-07-01', + reasoningEffort: 'high', +} + +function reserveInput( + limits: AgentCandidateModelGrantReserveInput['limits'] = { + maxModelCalls: 2, + maxInputTokens: 30, + maxOutputTokens: 20, + maxCostUsd: 0.1, + }, +): AgentCandidateModelGrantReserveInput { + return { + executionId: 'execution-1', + preparationId: 'preparation-1', + expiresAtMs: EXPIRES_AT_MS, + attempt: { number: 1, maxAttempts: 1, retryPolicy: 'none' }, + bundleDigest: sha('a'), + resolved: resolvedModel, + limits, + } +} + +function reservation( + input: AgentCandidateModelGrantReserveInput, +): AgentCandidateModelGrantReservation { + return { + preparationId: input.preparationId, + digest: sha('b'), + expiresAtMs: input.expiresAtMs, + enforcedLimits: input.limits, + network: + input.limits.maxModelCalls === 0 + ? { mode: 'disabled' } + : { mode: 'gateway-only', domains: [GATEWAY_DOMAIN] }, + } +} + +function activateInput(): AgentCandidateModelGrantActivateInput { + return { + executionId: 'execution-1', + preparationId: 'preparation-1', + grantDigest: sha('b'), + resolved: resolvedModel, + deadlineAtMs: EXPIRES_AT_MS - 1_000, + } +} + +function settleInput( + reason: AgentCandidateModelGrantSettleInput['reason'] = 'completed', +): AgentCandidateModelGrantSettleInput { + return { + executionId: 'execution-1', + preparationId: 'preparation-1', + grantDigest: sha('b'), + resolved: resolvedModel, + reason, + } +} + +function modelCall( + index: number, + overrides: Partial = {}, +): AgentCandidateProtectedModelCall { + return { + callId: `call-${index}`, + generationId: `generation-${index}`, + traceSpanId: `generation-${index}`, + status: 'succeeded', + model: resolvedModel.model, + startedAtMs: 1_000 + index * 100, + endedAtMs: 1_050 + index * 100, + inputTokens: 10, + outputTokens: 5, + cachedInputTokens: 0, + reasoningTokens: 0, + costUsdNanos: 10_000_000, + ...overrides, + } +} + +function settlement( + calls: readonly AgentCandidateProtectedModelCall[] = [], +): AgentCandidateProtectedModelSettlement { + return { + preparationId: 'preparation-1', + grantDigest: sha('b'), + closed: true, + calls, + } +} + +interface FakeClientOptions { + reserve?: ( + input: AgentCandidateModelGrantReserveInput, + ) => Promise + activate?: ( + input: AgentCandidateModelGrantActivateInput, + ) => Promise + settle?: ( + input: AgentCandidateModelGrantSettleInput, + ) => Promise +} + +function deferred(): { promise: Promise; resolve: (value: T) => void } { + let resolve!: (value: T) => void + const promise = new Promise((done) => { + resolve = done + }) + return { promise, resolve } +} + +function fakeClient(options: FakeClientOptions = {}): AgentCandidateModelGrantClient & { + reserveInputs: AgentCandidateModelGrantReserveInput[] + activateInputs: AgentCandidateModelGrantActivateInput[] + settleInputs: AgentCandidateModelGrantSettleInput[] +} { + const reserveInputs: AgentCandidateModelGrantReserveInput[] = [] + const activateInputs: AgentCandidateModelGrantActivateInput[] = [] + const settleInputs: AgentCandidateModelGrantSettleInput[] = [] + return { + reserveInputs, + activateInputs, + settleInputs, + reserve: async (input) => { + reserveInputs.push(input) + return options.reserve ? await options.reserve(input) : reservation(input) + }, + activate: async (input) => { + activateInputs.push(input) + return options.activate + ? await options.activate(input) + : { + env: { + MODEL_API_KEY: 'protected-candidate-token', + MODEL_BASE_URL: 'https://router.tangle.tools/v1', + }, + } + }, + settle: async (input) => { + settleInputs.push(input) + return options.settle ? await options.settle(input) : settlement() + }, + } +} + +function createPort(client: AgentCandidateModelGrantClient): AgentCandidateModelPort { + return createProtectedAgentCandidateModelPort({ + client, + gatewayDomain: GATEWAY_DOMAIN, + activationEnvNames: ACTIVATION_ENV_NAMES, + resolveModel: async ({ requested, reasoningEffort }) => ({ + ...resolvedModel, + requested, + reasoningEffort, + }), + }) +} + +async function reserve(port: AgentCandidateModelPort, input = reserveInput()) { + return await port.reserveGrant(input) +} + +describe('protected candidate model port', () => { + it('reserves without a live secret, activates exact protected env, and settles exact usage', async () => { + const client = fakeClient({ + settle: async () => + settlement([ + modelCall(1, { + inputTokens: 20, + outputTokens: 10, + cachedInputTokens: 4, + reasoningTokens: 3, + costUsdNanos: 100_000_000, + }), + ]), + }) + const port = createPort(client) + + await expect( + port.resolve({ + requested: resolvedModel.requested, + harness: 'opencode', + reasoningEffort: resolvedModel.reasoningEffort, + }), + ).resolves.toEqual(resolvedModel) + const reserved = await reserve(port) + expect(JSON.stringify(reserved)).not.toContain('protected-candidate-token') + expect(reserved).toEqual({ + preparationId: 'preparation-1', + digest: sha('b'), + expiresAtMs: EXPIRES_AT_MS, + enforcedLimits: reserveInput().limits, + network: { mode: 'gateway-only', domains: [GATEWAY_DOMAIN] }, + }) + expect(Object.isFrozen(reserved)).toBe(true) + + const activated = await port.activateGrant(activateInput()) + expect(activated).toEqual({ + env: { + MODEL_API_KEY: 'protected-candidate-token', + MODEL_BASE_URL: 'https://router.tangle.tools/v1', + }, + }) + expect(Object.isFrozen(activated.env)).toBe(true) + + const settled = await port.settleGrant(settleInput('completed')) + expect(settled.calls).toEqual([ + modelCall(1, { + inputTokens: 20, + outputTokens: 10, + cachedInputTokens: 4, + reasoningTokens: 3, + costUsdNanos: 100_000_000, + }), + ]) + expect(client.settleInputs).toEqual([settleInput('completed')]) + expect(Object.isFrozen(settled.calls)).toBe(true) + }) + + it('keeps zero-call reservation, activation, and settlement entirely credential-free', async () => { + const client = fakeClient({ activate: async () => ({ env: {} }) }) + const port = createPort(client) + const input = reserveInput({ + maxModelCalls: 0, + maxInputTokens: 0, + maxOutputTokens: 0, + maxCostUsd: 0, + }) + await expect(port.reserveGrant(input)).resolves.toMatchObject({ + network: { mode: 'disabled' }, + }) + await expect(port.activateGrant(activateInput())).resolves.toEqual({ env: {} }) + await expect(port.settleGrant(settleInput())).resolves.toEqual(settlement()) + expect(client.activateInputs).toHaveLength(1) + }) + + it('rejects any protected env or final model row for a zero-call reservation', async () => { + const client = fakeClient() + const port = createPort(client) + const input = reserveInput({ + maxModelCalls: 0, + maxInputTokens: 0, + maxOutputTokens: 0, + maxCostUsd: 0, + }) + await port.reserveGrant(input) + await expect(port.activateGrant(activateInput())).rejects.toThrow(/environment names/) + + const rowClient = fakeClient({ + activate: async () => ({ env: {} }), + settle: async () => settlement([modelCall(1)]), + }) + const rowPort = createPort(rowClient) + await rowPort.reserveGrant(input) + await rowPort.activateGrant(activateInput()) + await expect(rowPort.settleGrant(settleInput())).rejects.toThrow( + /model calls.*exceeds reserved/, + ) + }) + + it('rejects any secret or other extra field in the reservation response', async () => { + const client = fakeClient({ + reserve: async (input) => + ({ + ...reservation(input), + env: { MODEL_API_KEY: 'must-not-exist-before-activation' }, + }) as AgentCandidateModelGrantReservation, + }) + await expect(reserve(createPort(client))).rejects.toThrow(/unknown field env/) + }) + + it('requires reservation retries to preserve both request and returned evidence', async () => { + let digest = sha('b') + const client = fakeClient({ + reserve: async (input) => ({ ...reservation(input), digest }), + }) + const port = createPort(client) + await expect(reserve(port)).resolves.toMatchObject({ digest: sha('b') }) + await expect( + reserve(port, reserveInput({ ...reserveInput().limits, maxInputTokens: 29 })), + ).rejects.toThrow(/changed immutable input/) + expect(client.reserveInputs).toHaveLength(1) + + digest = sha('c') + await expect(reserve(port)).rejects.toThrow(/different evidence/) + }) + + it('linearizes concurrent reservation retries before accepting changed input', async () => { + const firstResponse = deferred() + const secondResponse = deferred() + const client = fakeClient({ + reserve: async (input) => + input.limits.maxInputTokens === 30 ? firstResponse.promise : secondResponse.promise, + }) + const port = createPort(client) + const firstInput = reserveInput() + const secondInput = reserveInput({ ...firstInput.limits, maxInputTokens: 29 }) + const first = reserve(port, firstInput) + const second = reserve(port, secondInput) + + firstResponse.resolve(reservation(firstInput)) + await expect(first).resolves.toMatchObject({ enforcedLimits: firstInput.limits }) + secondResponse.resolve(reservation(secondInput)) + await expect(second).rejects.toThrow(/changed immutable input/) + }) + + it('does not resurrect a reservation when a concurrent retry returns after settlement', async () => { + const firstResponse = deferred() + const secondResponse = deferred() + let call = 0 + const client = fakeClient({ + reserve: async () => (++call === 1 ? firstResponse.promise : secondResponse.promise), + }) + const port = createPort(client) + const input = reserveInput() + const first = reserve(port, input) + const second = reserve(port, input) + firstResponse.resolve(reservation(input)) + await first + await port.settleGrant(settleInput()) + + secondResponse.resolve(reservation(input)) + await expect(second).rejects.toThrow(/after the grant was settled/) + await expect(port.activateGrant(activateInput())).rejects.toThrow(/already settled/) + }) + + it.each([ + [ + 'expiry', + (input: AgentCandidateModelGrantReserveInput) => ({ + ...reservation(input), + expiresAtMs: input.expiresAtMs + 1, + }), + ], + [ + 'limits', + (input: AgentCandidateModelGrantReserveInput) => ({ + ...reservation(input), + enforcedLimits: { ...input.limits, maxInputTokens: input.limits.maxInputTokens + 1 }, + }), + ], + [ + 'domain', + (input: AgentCandidateModelGrantReserveInput) => ({ + ...reservation(input), + network: { mode: 'gateway-only' as const, domains: ['other.example.com'] }, + }), + ], + ])('rejects reservation %s drift', async (_name, mutate) => { + const client = fakeClient({ reserve: async (input) => mutate(input) }) + await expect(reserve(createPort(client))).rejects.toThrow(/changed|unexpected/) + }) + + it('requires the activation endpoint to return exactly the configured protected env', async () => { + const extra = fakeClient({ + activate: async () => ({ + env: { + MODEL_API_KEY: 'protected-candidate-token', + MODEL_BASE_URL: 'https://router.tangle.tools/v1', + DATABASE_URL: 'must-never-reach-candidate', + }, + }), + }) + const extraPort = createPort(extra) + await reserve(extraPort) + await expect(extraPort.activateGrant(activateInput())).rejects.toThrow(/environment names/) + + const missing = fakeClient({ + activate: async () => ({ env: { MODEL_API_KEY: 'protected-candidate-token' } }), + }) + const missingPort = createPort(missing) + await reserve(missingPort) + await expect(missingPort.activateGrant(activateInput())).rejects.toThrow(/environment names/) + }) + + it('rejects activation before reservation, after settlement, and with changed grant identity', async () => { + const client = fakeClient() + const port = createPort(client) + await expect(port.activateGrant(activateInput())).rejects.toThrow(/not reserved/) + + await reserve(port) + await expect(port.activateGrant({ ...activateInput(), grantDigest: sha('c') })).rejects.toThrow( + /immutable reservation/, + ) + await port.settleGrant(settleInput()) + await expect(port.activateGrant(activateInput())).rejects.toThrow(/already settled/) + expect(client.activateInputs).toHaveLength(0) + }) + + it('treats protected environment activation as single-use', async () => { + const client = fakeClient() + const port = createPort(client) + await reserve(port) + await expect(port.activateGrant(activateInput())).resolves.toMatchObject({ env: {} }) + await expect(port.activateGrant(activateInput())).rejects.toThrow(/single-use/) + expect(client.activateInputs).toHaveLength(1) + }) + + it('linearizes concurrent activation before the service returns a credential', async () => { + const response = deferred() + const client = fakeClient({ activate: async () => response.promise }) + const port = createPort(client) + await reserve(port) + const first = port.activateGrant(activateInput()) + await expect(port.activateGrant(activateInput())).rejects.toThrow(/single-use/) + response.resolve({ + env: { + MODEL_API_KEY: 'protected-candidate-token', + MODEL_BASE_URL: 'https://router.tangle.tools/v1', + }, + }) + await expect(first).resolves.toMatchObject({ env: {} }) + expect(client.activateInputs).toHaveLength(1) + }) + + it('does not return a credential when settlement wins an activation race', async () => { + const response = deferred() + const client = fakeClient({ activate: async () => response.promise }) + const port = createPort(client) + await reserve(port) + const activation = port.activateGrant(activateInput()) + await port.settleGrant(settleInput('failed')) + response.resolve({ + env: { + MODEL_API_KEY: 'protected-candidate-token', + MODEL_BASE_URL: 'https://router.tangle.tools/v1', + }, + }) + await expect(activation).rejects.toThrow(/settled while activation was in flight/) + }) + + it.each([ + [ + 'model calls', + [ + modelCall(1, { inputTokens: 0, outputTokens: 0, costUsdNanos: 0 }), + modelCall(2, { inputTokens: 0, outputTokens: 0, costUsdNanos: 0 }), + modelCall(3, { inputTokens: 0, outputTokens: 0, costUsdNanos: 0 }), + ], + ], + ['input tokens', [modelCall(1, { inputTokens: 31, outputTokens: 0, costUsdNanos: 0 })]], + ['output tokens', [modelCall(1, { inputTokens: 0, outputTokens: 21, costUsdNanos: 0 })]], + [ + 'cost USD nanos', + [modelCall(1, { inputTokens: 0, outputTokens: 0, costUsdNanos: 100_000_001 })], + ], + ])('rejects %s above the immutable reservation', async (label, calls) => { + const client = fakeClient({ settle: async () => settlement(calls) }) + const port = createPort(client) + await reserve(port) + await expect(port.settleGrant(settleInput())).rejects.toThrow( + new RegExp(`${label}.*exceeds reserved`), + ) + }) + + it.each([ + [ + 'duplicate call ids', + [modelCall(1), modelCall(2, { callId: 'call-1' })], + /duplicate call ids/, + ], + [ + 'duplicate trace span ids', + [modelCall(1), modelCall(2, { generationId: 'generation-1', traceSpanId: 'generation-1' })], + /duplicate trace span ids/, + ], + ])('rejects %s in the final ledger', async (_name, calls, pattern) => { + const client = fakeClient({ settle: async () => settlement(calls) }) + const port = createPort(client) + await reserve(port) + await expect(port.settleGrant(settleInput())).rejects.toThrow(pattern) + }) + + it.each([ + ['preparation', { ...settlement(), preparationId: 'other-preparation' }, /preparation/], + ['grant', { ...settlement(), grantDigest: sha('c') }, /grant digest/], + [ + 'router generation', + settlement([modelCall(1, { traceSpanId: 'caller-chosen-span' })]), + /router generationId/, + ], + ['model', settlement([modelCall(1, { model: 'openai/other-snapshot' })]), /unexpected model/], + ])('rejects a mismatched %s in the final ledger', async (_name, value, pattern) => { + const client = fakeClient({ settle: async () => value }) + const port = createPort(client) + await reserve(port) + await expect(port.settleGrant(settleInput())).rejects.toThrow(pattern) + }) + + it('rejects a partial or structurally extended final ledger', async () => { + const partial = fakeClient({ + settle: async () => ({ ...settlement(), closed: false }) as never, + }) + const partialPort = createPort(partial) + await reserve(partialPort) + await expect(partialPort.settleGrant(settleInput())).rejects.toThrow(/not closed/) + + const extended = fakeClient({ + settle: async () => + settlement([{ ...modelCall(1), providerRequest: 'untrusted-extra-field' } as never]), + }) + const extendedPort = createPort(extended) + await reserve(extendedPort) + await expect(extendedPort.settleGrant(settleInput())).rejects.toThrow(/unknown field/) + }) + + it('requires settlement retries to preserve both final rows and termination reason', async () => { + let calls = [modelCall(1)] + const client = fakeClient({ settle: async () => settlement(calls) }) + const port = createPort(client) + await reserve(port) + await expect(port.settleGrant(settleInput('failed'))).resolves.toEqual(settlement(calls)) + await expect(port.settleGrant(settleInput('failed'))).resolves.toEqual(settlement(calls)) + + calls = [modelCall(2)] + await expect(port.settleGrant(settleInput('failed'))).rejects.toThrow(/different final ledger/) + calls = [modelCall(1)] + await expect(port.settleGrant(settleInput('timeout'))).rejects.toThrow(/termination reason/) + expect(client.settleInputs).toHaveLength(3) + }) + + it('composes with the existing trace check to reject missing or extra model rows 1:1', async () => { + const client = fakeClient({ settle: async () => settlement([modelCall(1)]) }) + const port = createPort(client) + await reserve(port) + const settled = await port.settleGrant(settleInput()) + const sealed = sealAgentCandidateModelSettlement(settled, { + preparationId: 'preparation-1', + grantDigest: sha('b'), + model: resolvedModel.model, + }) + const span = (index: number): LlmSpan => ({ + spanId: `generation-${index}`, + runId: 'run-1', + kind: 'llm', + name: 'protected model call', + startedAt: 1_000 + index * 100, + endedAt: 1_050 + index * 100, + status: 'ok', + model: resolvedModel.model, + messages: [], + inputTokens: 10, + outputTokens: 5, + cachedTokens: 0, + reasoningTokens: 0, + costUsd: 0.01, + attributes: { + 'tangle.protected_model.source': 'router-settlement', + 'tangle.router.call_id': `call-${index}`, + 'tangle.router.generation_id': `generation-${index}`, + }, + }) + + expect(() => assertTraceMatchesModelSettlement([], sealed)).toThrow(/do not match/) + expect(() => assertTraceMatchesModelSettlement([span(1), span(2)], sealed)).toThrow( + /do not match/, + ) + expect(() => assertTraceMatchesModelSettlement([span(1)], sealed)).not.toThrow() + + const store = new InMemoryTraceStore() + await store.appendRun({ + runId: 'run-1', + scenarioId: 'candidate-model-port', + startedAt: 900, + endedAt: 1_200, + status: 'completed', + }) + await appendAuthoritativeModelSettlementSpans(store, 'run-1', sealed) + const protectedSpans = (await store.spans({ runId: 'run-1' })).filter( + (candidate): candidate is LlmSpan => candidate.kind === 'llm', + ) + expect(protectedSpans).toEqual([span(1)]) + expect(() => assertTraceMatchesModelSettlement(protectedSpans, sealed)).not.toThrow() + }) +}) diff --git a/tests/candidate-execution-prepare.test.ts b/tests/candidate-execution-prepare.test.ts index dce98dc7..65cb7fb3 100644 --- a/tests/candidate-execution-prepare.test.ts +++ b/tests/candidate-execution-prepare.test.ts @@ -226,6 +226,10 @@ function fixture(active = false): { digest: sha('c'), expiresAtMs, enforcedLimits: limits, + network: + limits.maxModelCalls === 0 + ? { mode: 'disabled' as const } + : { mode: 'gateway-only' as const, domains: ['router.tangle.tools'] }, }), activateGrant: async () => ({ env: { MODEL_GATEWAY_TOKEN: 'protected' } }), settleGrant: async ({ preparationId }) => ({ @@ -329,6 +333,10 @@ describe('candidate execution preparation', () => { expect(JSON.stringify(plan)).not.toContain(value.task.instruction) expect(JSON.stringify(prepared)).not.toContain('MODEL_GATEWAY_TOKEN') expect(JSON.stringify(prepared)).not.toContain('protected') + expect(plan.model.access.network).toEqual({ + mode: 'gateway-only', + domains: ['router.tangle.tools'], + }) }) it('keeps argv task bytes out of fixed args and exposes deterministic delivery separately', async () => { @@ -389,6 +397,7 @@ describe('candidate execution preparation', () => { digest: sha('c'), expiresAtMs, enforcedLimits: { ...limits, maxCostUsd: limits.maxCostUsd + 1 }, + network: { mode: 'gateway-only', domains: ['router.tangle.tools'] }, }) unenforced.ports.models.settleGrant = async ({ preparationId }) => { settledReservations++ @@ -404,6 +413,47 @@ describe('candidate execution preparation', () => { expect(settledReservations).toBe(1) }) + it('requires a frozen model gateway only when model calls are allowed', async () => { + const invalid = fixture() + let settledReservations = 0 + invalid.ports.models.reserveGrant = async ({ preparationId, expiresAtMs, limits }) => ({ + preparationId, + digest: sha('c'), + expiresAtMs, + enforcedLimits: limits, + network: { mode: 'disabled' }, + }) + invalid.ports.models.settleGrant = async ({ preparationId }) => { + settledReservations++ + return { preparationId, grantDigest: sha('c'), closed: true, calls: [] } + } + await expect( + prepareAgentCandidateExecution( + await verifyAgentCandidateBundle(invalid.bundle, invalid.ports), + invalid.task, + invalid.ports, + ), + ).rejects.toThrow(/wrong network policy/) + expect(settledReservations).toBe(1) + + const zeroCall = fixture() + zeroCall.task.limits = { + ...zeroCall.task.limits, + maxModelCalls: 0, + maxInputTokens: 0, + maxOutputTokens: 0, + maxCostUsd: 0, + } + const prepared = await prepareAgentCandidateExecution( + await verifyAgentCandidateBundle(zeroCall.bundle, zeroCall.ports), + zeroCall.task, + zeroCall.ports, + ) + expect(prepared.executionPlan.value.material.model.access.network).toEqual({ + mode: 'disabled', + }) + }) + it('bounds failed-preparation cleanup and rejects another preparation settlement', async () => { const hanging = fixture() hanging.ports.models.reserveGrant = async ({ preparationId, expiresAtMs, limits }) => ({ @@ -411,6 +461,7 @@ describe('candidate execution preparation', () => { digest: sha('c'), expiresAtMs, enforcedLimits: { ...limits, maxModelCalls: limits.maxModelCalls + 1 }, + network: { mode: 'gateway-only', domains: ['router.tangle.tools'] }, }) hanging.ports.models.settleGrant = async () => await new Promise(() => undefined) const startedAt = Date.now() @@ -430,6 +481,7 @@ describe('candidate execution preparation', () => { digest: sha('c'), expiresAtMs, enforcedLimits: { ...limits, maxModelCalls: limits.maxModelCalls + 1 }, + network: { mode: 'gateway-only', domains: ['router.tangle.tools'] }, }) mismatched.ports.models.settleGrant = async () => ({ preparationId: `candidate-preparation-v1.${'A'.repeat(43)}`, diff --git a/tests/helpers/candidate-execution-fixture.ts b/tests/helpers/candidate-execution-fixture.ts index 7c2202e2..3b74bddc 100644 --- a/tests/helpers/candidate-execution-fixture.ts +++ b/tests/helpers/candidate-execution-fixture.ts @@ -317,6 +317,10 @@ export function createCandidateExecutionFixture(active = false): CandidateExecut digest: candidateSha('c'), expiresAtMs, enforcedLimits: limits, + network: + limits.maxModelCalls === 0 + ? { mode: 'disabled' as const } + : { mode: 'gateway-only' as const, domains: ['router.tangle.tools'] }, }), activateGrant: async () => ({ env: { MODEL_GATEWAY_TOKEN: 'protected' } }), settleGrant: async ({ preparationId }) => ({