Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/agent-profile-json-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tangle-network/agent-interface": minor
---

Export a model-facing JSON Schema generated from the canonical `AgentProfile` validator.
5 changes: 5 additions & 0 deletions .changeset/agent-profile-routing-identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tangle-network/agent-interface": patch
---

Document and test that authored harness and model-routing preferences participate in canonical profile identity while execution receipts separately bind overrides.
5 changes: 5 additions & 0 deletions .changeset/cli-bridge-exact-profile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tangle-network/agent-provider-cli-bridge": patch
---

Keep the task message separate from the exact `AgentProfile`, and forward the profile's reasoning effort to cli-bridge.
5 changes: 5 additions & 0 deletions .changeset/cli-bridge-steerable-sessions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tangle-network/agent-provider-cli-bridge": minor
---

Expose cli-bridge durable runs through `AgentEnvironment.dispatch()` and `AgentEnvironment.session()` with exact identity, cursor replay, usage-preserving results, continuation, and terminal-confirmed cancellation.
2 changes: 1 addition & 1 deletion packages/agent-interface/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@

### Minor Changes

- afe552d: Add optional `AgentProfile.harness` — a typed, executor-overridable preferred execution harness (`HarnessType`). Formalizes the `profile.harness` runtimes already read untyped; identity stays harness-agnostic (the leaderboard `harness × model` axis and per-worker supervisor routing still override it), and it becomes a first-class lever an improvement loop can optimize.
- afe552d: Add optional `AgentProfile.harness` — a typed, executor-overridable preferred execution harness (`HarnessType`). Formalizes the `profile.harness` runtimes already read untyped; the authored preference participates in canonical profile identity while execution receipts separately bind any override, and it becomes a first-class lever an improvement loop can optimize.

## 0.19.0

Expand Down
85 changes: 85 additions & 0 deletions packages/agent-interface/src/agent-execution-preparation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,91 @@ describe("profile materialization leaves", () => {
});

describe("AgentExecutionPreparationReceipt", () => {
it("binds authored routing preferences and separately receipts execution overrides", () => {
const authoredProfile: AgentProfile = {
name: "worker",
harness: "pi",
model: {
default: "tangle/glm-5.2",
provider: "tangle-router",
},
};
const harnessChanged: AgentProfile = {
...authoredProfile,
harness: "codex",
};
const modelChanged: AgentProfile = {
...authoredProfile,
model: {
...authoredProfile.model,
default: "anthropic/claude-opus-4-1",
},
};
const providerChanged: AgentProfile = {
...authoredProfile,
model: {
...authoredProfile.model,
provider: "anthropic",
},
};
const authoredDigests = [
authoredProfile,
harnessChanged,
modelChanged,
providerChanged,
].map(canonicalAgentProfileDigest);
expect(new Set(authoredDigests)).toHaveLength(4);

const effectiveProfile: AgentProfile = {
...authoredProfile,
harness: "codex",
model: {
default: "anthropic/claude-opus-4-1",
provider: "anthropic",
},
};
const overriddenAxes = new Set([
"harness",
"modelDefault",
"modelProvider",
]);
const receipt = buildReceipt({
authoredProfile,
effectiveProfile,
axisResults: coverage(authoredProfile).map((result) =>
overriddenAxes.has(result.axis)
? {
...result,
disposition: "overridden" as const,
reason: "execution request selected a different route",
}
: result,
),
});

expect(receipt.authoredProfileDigest).toBe(
canonicalAgentProfileDigest(authoredProfile),
);
expect(receipt.effectiveProfileDigest).toBe(
canonicalAgentProfileDigest(effectiveProfile),
);
expect(receipt.authoredProfileDigest).not.toBe(
receipt.effectiveProfileDigest,
);
expect(receipt).toMatchObject({
harness: "codex",
resolvedModel: {
requested: "anthropic/claude-opus-4-1",
provider: "anthropic",
},
});
expect(
receipt.axisResults
.filter((result) => result.disposition === "overridden")
.map((result) => result.axis),
).toEqual(["modelDefault", "modelProvider", "harness"]);
});

it("builds from a sealed lease and validates only after execution binding", () => {
const receipt = buildReceipt();

Expand Down
2 changes: 2 additions & 0 deletions packages/agent-interface/src/agent-execution-preparation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ export interface AgentExecutionPreparationReceipt {
schemaVersion: 1;
preparationId: string;
requestDigest: Sha256Digest;
/** Canonical identity before executor overrides. */
authoredProfileDigest: Sha256Digest;
/** Canonical identity after executor overrides. */
effectiveProfileDigest: Sha256Digest;
backend: string;
harness: HarnessType;
Expand Down
13 changes: 6 additions & 7 deletions packages/agent-interface/src/agent-profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,12 @@ export interface AgentProfile {
* Preferred execution harness for this profile — the coding-CLI runtime that
* materializes and runs it (`claude-code`, `codex`, `opencode`, `pi`, …).
*
* This is an optional PREFERENCE, not part of profile IDENTITY: the same
* profile still runs on any harness, and an executor MAY override it (e.g. the
* leaderboard's `harness × model` axis sweeps a profile across every harness,
* and a supervisor spawning a worker may pick a harness per subtask). When
* unset, the caller/executor chooses. Formalizes what runtimes already read as
* `profile.harness`; making it typed also lets an improvement loop optimize
* harness routing as a first-class lever.
* This optional authored preference participates in canonical profile
* identity. An executor MAY override it for one run; the effective profile and
* execution receipt then bind that override without changing what was
* authored. When unset, the caller/executor chooses. Formalizes what runtimes
* already read as `profile.harness`; making it typed also lets an improvement
* loop optimize harness routing as a first-class lever.
*/
harness?: HarnessType;
permissions?: Record<string, AgentProfilePermission>;
Expand Down
13 changes: 6 additions & 7 deletions packages/agent-interface/src/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ import { z } from "zod";
/**
* The execution runner for an agent — WHICH runtime materializes and runs an `AgentProfile`.
*
* Harness is an EXECUTION concern, not part of profile IDENTITY: the same `AgentProfile`
* (prompt/model/skills/tools/mcp/subagents) runs on any harness. A profile MAY carry an optional
* `harness` PREFERENCE (`AgentProfile.harness`), but the caller/executor can always override it per
* run — the leaderboard's `harness × model` axis sweeps one profile across every harness, and a
* supervisor may pick a harness per spawned worker. This is the single shared enum every layer
* references instead of keeping its own copy (session control, the profile materializer, the
* cli-bridge backends, VB profile specs).
* This enum describes an EXECUTION concern. When an `AgentProfile` authors a `harness`
* preference, that field participates in its canonical identity like every other parsed profile
* field. A caller or executor may override the preference for one run, but the execution receipt
* preserves the authored identity, effective identity, and selected harness separately. This is
* the single shared enum every layer references instead of keeping its own copy (session control,
* the profile materializer, the cli-bridge backends, VB profile specs).
*
* `cli-base` is the router-backed mode — a plain multi-turn router call (a reviewer, a cheap judge,
* a one-shot) with no full coding-agent harness. The rest are full agentic harnesses run in a
Expand Down
8 changes: 6 additions & 2 deletions packages/agent-interface/src/profile-harness.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import { canonicalAgentProfileDigest } from "./agent-execution-preparation.js";
import type { AgentProfile } from "./agent-profile.js";
import { agentProfileSchema } from "./profile-schema.js";
import type { HarnessType } from "./harness.js";
Expand All @@ -14,7 +15,7 @@ describe("AgentProfile.harness (optional overridable preference)", () => {
expect(parsed.harness).toBe("codex");
});

it("is optional — a profile without harness still parses (harness-agnostic identity)", () => {
it("is optional — a profile without a preference still parses", () => {
const parsed = agentProfileSchema.parse({ name: "w" });
expect(parsed.harness).toBeUndefined();
});
Expand All @@ -26,12 +27,15 @@ describe("AgentProfile.harness (optional overridable preference)", () => {
expect(() => agentProfileSchema.parse({ harness: "kimi" })).toThrow();
});

it("does not constrain identity — the same profile is valid with any harness swapped in", () => {
it("accepts every known runner while binding each preference into authored identity", () => {
const base: AgentProfile = { name: "w", prompt: { systemPrompt: "do the task" } };
const digests = [];
for (const harness of ["claude-code", "opencode", "pi", "cli-base"] as const) {
const parsed = agentProfileSchema.parse({ ...base, harness });
expect(parsed.harness).toBe(harness);
expect(parsed.prompt).toEqual(base.prompt);
digests.push(canonicalAgentProfileDigest(parsed));
}
expect(new Set(digests)).toHaveLength(4);
});
});
Loading
Loading