Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/api/candidate-execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,12 @@ Re-exports [PreparedAgentCandidateInstruction](index.md#preparedagentcandidatein

***

### PreparedAgentCandidateKnowledge

Re-exports [PreparedAgentCandidateKnowledge](index.md#preparedagentcandidateknowledge)

***

### PreparedAgentCandidateLaunch

Re-exports [PreparedAgentCandidateLaunch](index.md#preparedagentcandidatelaunch)
Expand Down
512 changes: 261 additions & 251 deletions docs/api/index.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions docs/api/intelligence.md
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,7 @@ Defined in: [intelligence/improvement-cycle.ts:76](https://github.com/tangle-net

##### candidateBundle?

> `optional` **candidateBundle?**: `AgentCandidateBundleV1`
> `optional` **candidateBundle?**: `AgentCandidateBundle`

Defined in: [intelligence/improvement-cycle.ts:77](https://github.com/tangle-network/agent-runtime/blob/main/src/intelligence/improvement-cycle.ts#L77)

Expand Down Expand Up @@ -1338,7 +1338,7 @@ Defined in: [intelligence/improvement-cycle.ts:111](https://github.com/tangle-ne

##### buildCandidate?

> `optional` **buildCandidate?**: (`input`) => `AgentCandidateBundleV1` \| [`AgentCandidateBundleInput`](index.md#agentcandidatebundleinput) \| `Promise`\<`AgentCandidateBundleV1` \| [`AgentCandidateBundleInput`](index.md#agentcandidatebundleinput)\>
> `optional` **buildCandidate?**: (`input`) => `AgentCandidateBundle` \| [`AgentCandidateBundleInput`](index.md#agentcandidatebundleinput) \| `Promise`\<`AgentCandidateBundle` \| [`AgentCandidateBundleInput`](index.md#agentcandidatebundleinput)\>

Defined in: [intelligence/improvement-cycle.ts:117](https://github.com/tangle-network/agent-runtime/blob/main/src/intelligence/improvement-cycle.ts#L117)

Expand All @@ -1360,7 +1360,7 @@ measured comparison recommends the candidate. Return the sealed output of

###### Returns

`AgentCandidateBundleV1` \| [`AgentCandidateBundleInput`](index.md#agentcandidatebundleinput) \| `Promise`\<`AgentCandidateBundleV1` \| [`AgentCandidateBundleInput`](index.md#agentcandidatebundleinput)\>
`AgentCandidateBundle` \| [`AgentCandidateBundleInput`](index.md#agentcandidatebundleinput) \| `Promise`\<`AgentCandidateBundle` \| [`AgentCandidateBundleInput`](index.md#agentcandidatebundleinput)\>

##### now?

Expand Down Expand Up @@ -1464,7 +1464,7 @@ Defined in: [intelligence/improvement-cycle.ts:139](https://github.com/tangle-ne

##### candidateBundle?

> `optional` **candidateBundle?**: `AgentCandidateBundleV1` \| [`AgentCandidateBundleInput`](index.md#agentcandidatebundleinput)
> `optional` **candidateBundle?**: `AgentCandidateBundle` \| [`AgentCandidateBundleInput`](index.md#agentcandidatebundleinput)

Defined in: [intelligence/improvement-cycle.ts:140](https://github.com/tangle-network/agent-runtime/blob/main/src/intelligence/improvement-cycle.ts#L140)

Expand Down
8 changes: 5 additions & 3 deletions docs/api/primitive-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@

# Primitive catalog — the never-stale anti-reinvention inventory

> **GENERATED** from `@tangle-network/agent-runtime@0.94.12` and `@tangle-network/agent-eval@0.120.1` by `scripts/gen-primitive-catalog.mjs`. Do NOT hand-edit — run `pnpm run docs:api`. This is the mechanical companion to the JUDGMENT in `canonical-api.md` (§2 decision table + §1.5 AgentProfile law): that doc says WHICH primitive to reach for and what NOT to build; this catalog proves WHAT exists. Per-symbol signatures + `file:line` live in the per-module pages under `docs/api/`.
> **GENERATED** from `@tangle-network/agent-runtime@0.94.13` and `@tangle-network/agent-eval@0.120.1` by `scripts/gen-primitive-catalog.mjs`. Do NOT hand-edit — run `pnpm run docs:api`. This is the mechanical companion to the JUDGMENT in `canonical-api.md` (§2 decision table + §1.5 AgentProfile law): that doc says WHICH primitive to reach for and what NOT to build; this catalog proves WHAT exists. Per-symbol signatures + `file:line` live in the per-module pages under `docs/api/`.

## 1. agent-runtime — own public surface

Every subpath this package declares in `package.json` `exports`. Reach for these before hand-rolling a loop, driver, conversation runner, optimizer wrapper, or observability shim.

### Root — task lifecycle, conversation, RSI verbs, observability

Import from `@tangle-network/agent-runtime` — 341 exports.
Import from `@tangle-network/agent-runtime` — 342 exports.

| Symbol | Kind | Summary |
|---|---|---|
Expand Down Expand Up @@ -174,6 +174,7 @@ Import from `@tangle-network/agent-runtime` — 341 exports.
| `ModelInfo` | interface | A model entry as returned by the Tangle Router `/v1/models` endpoint. |
| `OpenAIChatTool` | interface | OpenAI Chat Completions tool descriptor. The shape mirrors the |
| `OtelExportConfig` | interface | OTEL span exporter — streams LoopTraceEvents to an OTLP/HTTP collector. |
| `PreparedAgentCandidateKnowledge` | interface | Exact file-backed knowledge admitted by the candidate bundle. |
| `RawTraceDistillerOptions` | interface | `rawTraceDistiller` — the meta-harness `analyzeGeneration` producer. |
| `ReflectiveGeneratorOptions` | interface | `reflectiveGenerator` — the cheap, no-sandbox `CandidateGenerator`. It drafts |
| `RouterEnv` | interface | Env keys the router base URL is resolved from. |
Expand Down Expand Up @@ -935,7 +936,7 @@ Import from `@tangle-network/agent-runtime/primeintellect` — 27 exports.

### Candidate execution — immutable prepare, run, grade, and receipt

Import from `@tangle-network/agent-runtime/candidate-execution` — 95 exports.
Import from `@tangle-network/agent-runtime/candidate-execution` — 96 exports.

| Symbol | Kind | Summary |
|---|---|---|
Expand Down Expand Up @@ -981,6 +982,7 @@ Import from `@tangle-network/agent-runtime/candidate-execution` — 95 exports.
| `AgentCandidateRepositoryPort` | interface | Resolves a declared GitHub repository to an already-present local Git object store. |
| `AgentCandidateWorkspacePort` | interface | Materializes an already-verified workspace archive. |
| `BuildAgentCandidateBundleInput` | interface | Complete measured surfaces and execution policy compiled into one candidate bundle. |
| `PreparedAgentCandidateKnowledge` | interface | Exact file-backed knowledge admitted by the candidate bundle. |
| `VerifiedAgentCandidateTaskOutcome` | interface | Branded task outcome that has survived independent patch and tree verification. |
| `AgentCandidateBundleInput` | type | Exact candidate wire shape before the runtime computes its canonical digest. |
| `AgentCandidateCodeSource` | type | Explicit control/no-op code or one finalized CodeSurface whose bytes must still verify. |
Expand Down
2 changes: 1 addition & 1 deletion docs/canonical-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<!-- This doc is the JUDGMENT layer: the mental model (§1), the AgentProfile law (§1.5), and the anti-reinvention decision table (§2) — WHICH primitive to reach for and what NOT to build. The export INVENTORY (WHAT exists) and per-symbol signatures + `file:line` are GENERATED into `docs/api/` (TypeDoc + `scripts/gen-primitive-catalog.mjs`, do NOT hand-edit) — that is the mechanical reference: `docs/api/primitive-catalog.md` is the never-stale list of every primitive to reuse. The freshness gate (`pnpm docs:freshness`) FAILS CI if a version pin, a cited `file:line`, a decision-table symbol, or the generated catalog drifts from source — see `docs/MAINTAINING.md`. Keep this file the small, hand-curated spine; never re-list the inventory here — point at the catalog. -->

> **Version 0.94.12.** 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.120.1 <0.121.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.25.0 <0.26.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.94.13.** 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.120.1 <0.121.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.26.1 <0.27.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.
>
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tangle-network/agent-runtime",
"version": "0.94.12",
"version": "0.94.13",
"description": "Shared task-lifecycle skeleton for agents: a recursive loop kernel for chat turns, one-shot tasks, and multi-attempt loops, with trace capture and eval-gated self-improvement. Domain behavior lives in adapters; scoring and ship-gates in @tangle-network/agent-eval.",
"homepage": "https://github.com/tangle-network/agent-runtime#readme",
"repository": {
Expand Down Expand Up @@ -117,7 +117,7 @@
"devDependencies": {
"@biomejs/biome": "^2.4.15",
"@tangle-network/agent-eval": "0.120.1",
"@tangle-network/agent-interface": "^0.25.0",
"@tangle-network/agent-interface": "0.26.1",
"@tangle-network/sandbox": "^0.9.7",
"@types/node": "^25.9.3",
"@types/tar-stream": "3.1.4",
Expand Down Expand Up @@ -148,7 +148,7 @@
"packageManager": "pnpm@10.28.0",
"peerDependencies": {
"@tangle-network/agent-eval": ">=0.120.1 <0.121.0",
"@tangle-network/agent-interface": ">=0.25.0 <0.26.0",
"@tangle-network/agent-interface": ">=0.26.1 <0.27.0",
"@tangle-network/sandbox": ">=0.8.0 <1.0.0",
"playwright": "^1.40.0"
},
Expand Down
4 changes: 2 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 33 additions & 33 deletions src/candidate-execution/artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { relative, resolve, sep } from 'node:path'
import type {
AgentCandidateArtifactRef,
AgentCandidateCapturedArtifact,
AgentCandidateProfilePlanMaterialV1,
AgentCandidateWorkspaceManifestMaterialV1,
AgentCandidateProfilePlanMaterial,
AgentCandidateWorkspaceManifestMaterial,
AgentCandidateWorkspaceSnapshotEvidence,
} from '@tangle-network/agent-interface'

Expand Down Expand Up @@ -64,7 +64,7 @@ export async function verifyWorkspaceSnapshotArtifacts(

export async function verifyMaterializedWorkspace(
root: string,
expected: AgentCandidateWorkspaceManifestMaterialV1,
expected: AgentCandidateWorkspaceManifestMaterial,
options: { ignoredProtectedRootEntries?: readonly ('.git' | '.sidecar')[] } = {},
): Promise<void> {
const observed = await scanWorkspace(root, new Set(options.ignoredProtectedRootEntries ?? []))
Expand All @@ -82,8 +82,8 @@ export async function captureMaterializedWorkspace(
}
} = {},
): Promise<{
manifest: AgentCandidateWorkspaceManifestMaterialV1
files: ReadonlyArray<{ path: string; mode: 0o644 | 0o755; bytes: Uint8Array }>
manifest: AgentCandidateWorkspaceManifestMaterial
files: ReadonlyArray<{ path: string; mode: number; bytes: Uint8Array }>
}> {
const observed = await scanWorkspace(
root,
Expand All @@ -103,19 +103,36 @@ export async function captureMaterializedWorkspace(
/** Capture exact verified regular-file bytes for fresh isolated materialization. */
export async function readMaterializedWorkspaceFiles(
root: string,
expected: AgentCandidateWorkspaceManifestMaterialV1,
expected: AgentCandidateWorkspaceManifestMaterial,
options: { ignoredProtectedRootEntries?: readonly ('.git' | '.sidecar')[] } = {},
): Promise<ReadonlyArray<{ path: string; mode: 0o644 | 0o755; bytes: Uint8Array }>> {
): Promise<ReadonlyArray<{ path: string; mode: number; bytes: Uint8Array }>> {
const observed = await captureMaterializedWorkspace(root, options)
assertWorkspaceManifest(observed.manifest, expected)
return observed.files.map((file) =>
Object.freeze({ path: file.path, mode: file.mode, bytes: Uint8Array.from(file.bytes) }),
)
}

export function candidateWorkspaceManifest(
files: ReadonlyArray<{ path: string; mode: number; bytes: Uint8Array }>,
): AgentCandidateWorkspaceManifestMaterial {
return {
schemaVersion: 2,
kind: 'agent-candidate-workspace-manifest',
files: files
.map((file) => ({
path: file.path,
mode: file.mode,
sha256: sha256Bytes(file.bytes),
byteLength: file.bytes.byteLength,
}))
.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0)),
}
}

function assertWorkspaceManifest(
observed: AgentCandidateWorkspaceManifestMaterialV1,
expected: AgentCandidateWorkspaceManifestMaterialV1,
observed: AgentCandidateWorkspaceManifestMaterial,
expected: AgentCandidateWorkspaceManifestMaterial,
): void {
if (!Buffer.from(canonicalCandidateBytes(observed)).equals(canonicalCandidateBytes(expected))) {
throw new Error(
Expand All @@ -126,7 +143,7 @@ function assertWorkspaceManifest(

export async function verifyMaterializedProfileWorkspace(
root: string,
expected: AgentCandidateProfilePlanMaterialV1,
expected: AgentCandidateProfilePlanMaterial,
): Promise<void> {
const observed = await scanWorkspace(root, new Set())
const observedProfile = observed.manifest.files.map(({ path, mode, sha256 }) => ({
Expand All @@ -152,8 +169,8 @@ async function scanWorkspace(
maxTotalFileBytes: number
},
): Promise<{
manifest: AgentCandidateWorkspaceManifestMaterialV1
files: Array<{ path: string; mode: 0o644 | 0o755; bytes: Uint8Array }>
manifest: AgentCandidateWorkspaceManifestMaterial
files: Array<{ path: string; mode: number; bytes: Uint8Array }>
}> {
const absoluteRoot = resolve(root)
const rootStats = await lstat(absoluteRoot)
Expand All @@ -163,8 +180,7 @@ async function scanWorkspace(
if ((await realpath(absoluteRoot)) !== absoluteRoot) {
throw new Error('workspace root has a symlinked path component')
}
const files: AgentCandidateWorkspaceManifestMaterialV1['files'] = []
const capturedFiles: Array<{ path: string; mode: 0o644 | 0o755; bytes: Uint8Array }> = []
const capturedFiles: Array<{ path: string; mode: number; bytes: Uint8Array }> = []
let totalBytes = 0

async function visit(directory: string): Promise<void> {
Expand All @@ -190,7 +206,7 @@ async function scanWorkspace(
if (!stats.isFile()) {
throw new Error(`workspace contains a non-regular entry: ${relPath}`)
}
if (limits && files.length >= limits.maxFiles) {
if (limits && capturedFiles.length >= limits.maxFiles) {
throw new Error('workspace exceeds maxFiles')
}
const descriptor = await open(
Expand All @@ -207,9 +223,6 @@ async function scanWorkspace(
throw new Error(`workspace contains a hard-linked file: ${relPath}`)
}
const mode = openedStats.mode & 0o777
if (mode !== 0o644 && mode !== 0o755) {
throw new Error(`workspace file has unsupported mode ${mode.toString(8)}: ${relPath}`)
}
if (limits && openedStats.size > limits.maxFileBytes) {
throw new Error(`workspace file exceeds maxFileBytes: ${relPath}`)
}
Expand All @@ -223,29 +236,16 @@ async function scanWorkspace(
relPath,
)
totalBytes += bytes.byteLength
const supportedMode = mode as 0o644 | 0o755
files.push({
path: relPath,
mode: supportedMode,
sha256: sha256Bytes(bytes),
byteLength: bytes.byteLength,
})
capturedFiles.push({ path: relPath, mode: supportedMode, bytes: Uint8Array.from(bytes) })
capturedFiles.push({ path: relPath, mode, bytes: Uint8Array.from(bytes) })
} finally {
await descriptor.close()
}
}
}

await visit(absoluteRoot)
files.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0))
capturedFiles.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0))
return {
manifest: {
schemaVersion: 1,
kind: 'agent-candidate-workspace-manifest',
files,
},
manifest: candidateWorkspaceManifest(capturedFiles),
files: capturedFiles,
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/candidate-execution/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function buildAgentCandidateBundle(
const compiledProfile = compileCandidateProfile(input.profile)
const profileDiffIds = compiledProfile.profileDiffIds
const bundle: AgentCandidateBundleInput = {
schemaVersion: 1,
schemaVersion: 2,
kind: 'agent-candidate-bundle',
digestAlgorithm: 'rfc8785-sha256',
profile: compiledProfile.profile,
Expand Down
25 changes: 25 additions & 0 deletions src/candidate-execution/executor-capture.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import {
type AgentCandidateTaskOutcomeEvidence,
type AgentCandidateTermination,
type AgentCandidateWorkspaceSnapshotEvidence,
agentCandidateTerminationSchema,
agentCandidateWorkspaceManifestMaterialSchema,
type Sha256Digest,
} from '@tangle-network/agent-interface'
import { canonicalCandidateBytes } from './digest'
import { assertExactObjectKeys as assertExactKeys } from './exact-object'
import type { AgentCandidateExecutorFinalCapture, AgentCandidateProtectedRunCapture } from './types'

Expand Down Expand Up @@ -39,6 +44,26 @@ export function sealAgentCandidateExecutorFinalCapture(
})
}

/** Encode the complete evaluator-owned process result as one durable artifact. */
export function encodeAgentCandidateExecutorCapture(input: {
executionId: string
executionPlanDigest: Sha256Digest
termination: AgentCandidateTermination
taskOutcome: AgentCandidateTaskOutcomeEvidence
memoryAfter?: AgentCandidateWorkspaceSnapshotEvidence
}): Uint8Array {
return canonicalCandidateBytes({
schemaVersion: 1,
kind: 'agent-candidate-executor-capture',
executionId: input.executionId,
executionPlanDigest: input.executionPlanDigest,
termination: input.termination,
stopped: true,
taskOutcome: input.taskOutcome,
...(input.memoryAfter ? { memoryAfter: input.memoryAfter } : {}),
})
}

function sealMemoryCapture(
value: unknown,
): NonNullable<AgentCandidateExecutorFinalCapture['memoryAfter']> {
Expand Down
Loading
Loading