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
537 changes: 269 additions & 268 deletions docs/api-reference/veryfront/agent.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/api-reference/veryfront/embedding.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const { POST, GET, DELETE } = createUploadHandler(store, {
| `ragStore` | Creates a persistent RAG store with lazy embedding and similarity search. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/embedding/rag-store.ts#L212) |
| `registerEmbeddingProvider` | Register an embedding provider factory. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/embedding/resolve.ts#L25) |
| `resolveEmbeddingModel` | Resolve a "provider/model" string to an embedding runtime instance. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/embedding/resolve.ts#L116) |
| `similarity` | Compute cosine similarity between two numeric vectors. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/runtime/runtime-bridge.ts#L987) |
| `similarity` | Compute cosine similarity between two numeric vectors. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/runtime/runtime-bridge.ts#L1128) |
| `vectorStore` | Creates an in-memory vector store with integrated embedding and similarity search. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/embedding/vector-store.ts#L46) |

### Types
Expand Down
32 changes: 16 additions & 16 deletions docs/api-reference/veryfront/provider.md

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion docs/guides/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,11 @@ export default agent({
For step-boundary refresh during a long-lived run, use `resolveRuntimeState`
instead of relying on `system()` to run again mid-turn.

`request.system` is always a string, so existing text transformations remain
compatible. When the runtime has structured system messages, use
`request.structuredSystem` to read their provider metadata and return
`structuredSystem` to replace them without flattening that metadata.

```ts
import { agent } from "veryfront/agent";

Expand All @@ -436,7 +441,7 @@ export default agent({
| `name` | `string` | Human-readable display name for listings |
| `description` | `string` | Optional summary for listings |
| `model` | `string` | Optional provider/model override. Omit for `openai/gpt-5.4-nano`; use `"auto"` for runtime selection. |
| `system` | `string \| () => string \| Promise<string>` | System prompt |
| `system` | `AgentSystem \| () => AgentSystem \| Promise<AgentSystem>` | Text or structured system instructions |
| `resolveRuntimeState` | `(request: RuntimeStateRequest) => ResolvedRuntimeState \| Promise<ResolvedRuntimeState \| undefined>` | Refresh system/context before later model steps in the same run |
| `tools` | `true \| Record<string, boolean \| Tool>` | Omit for no project tools, use `true` for deferred scoped discovery, or select eager tools explicitly |
| `delegates` | `string[]` | Exact agent ids exposed as scoped `agent_<id>` tools |
Expand Down
30 changes: 18 additions & 12 deletions docs/guides/skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,31 @@ skills/
Every agent gets `load_skill`. Local and project runtimes also expose the two
supporting skill tools:

| Tool | Availability | Description |
| ---------------------- | -------------------------- | ---------------------------------------------------------- |
| `load_skill` | Every runtime | List authorized IDs or load full instructions by ID |
| `load_skill_reference` | Local and project runtimes | Read a file from `references/`, `resources/`, or `assets/` |
| `execute_skill_script` | Local and project runtimes | Execute a script from a skill (5-minute timeout) |

After loading a skill, hosted chat can read only a reference listed by that
skill through `load_skill({ load: { skillId, file } })`. It does not execute
skill scripts directly. Direct tool consumers can continue to use the legacy
flat input forms.

When the prompt provides a discovery cursor, call
| Tool | Availability | Description |
| ---------------------- | -------------------------- | ---------------------------------------------------------------------- |
| `load_skill` | Every runtime | Load full instructions by ID. Hosted chat can also list authorized IDs |
| `load_skill_reference` | Local and project runtimes | Read a file from `references/`, `resources/`, or `assets/` |
| `execute_skill_script` | Local and project runtimes | Execute a script from a skill (5-minute timeout) |

Hosted chat providers use nested `load_skill` input. After loading a skill,
hosted chat can read only a reference listed by that skill through
`load_skill({ load: { skillId, file } })`. It does not execute skill scripts
directly.

When a hosted chat prompt provides a discovery cursor, call
`load_skill({ inventory: { cursor: <CURSOR> } })`. Otherwise, call
`load_skill({ inventory: {} })` when the prompt does not show the complete
authorized skill inventory. The result contains a bounded `skillIds` page. If
it also contains `nextCursor`, call
`load_skill({ inventory: { cursor: nextCursor } })` until the response omits
`nextCursor`. Then call `load_skill({ load: { skillId } })` with a listed ID.

Direct local and project tool consumers use flat input forms. Call
`load_skill({ skillId: "<SKILL_ID>" })` to load a skill. After loading it, call
`load_skill_reference({ skillId: "<SKILL_ID>", reference: "<PATH>" })` to read
a listed reference, resource, or asset. Direct local and project tools do not
support inventory paging through `load_skill`.
Comment thread
kojiwakayama marked this conversation as resolved.

Discovered skills visible to the agent are advertised by default:

```ts
Expand Down
231 changes: 231 additions & 0 deletions extensions/ext-llm-anthropic/src/anthropic-request-builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,237 @@ function createWarningCollector() {
}

describe("ext-llm-anthropic/anthropic-request-builder", () => {
it("keeps a cached static system block separate from the uncached dynamic tail", () => {
const body = buildAnthropicMessagesRequest(
"claude-sonnet-4-5-20250929",
"anthropic",
{
prompt: [
{
role: "system",
content: "Shared prompt",
providerOptions: {
anthropic: { cacheControl: { type: "ephemeral", ttl: "1h" } },
},
},
{ role: "system", content: "Dynamic tail" },
{ role: "user", content: [{ type: "text", text: "Hello" }] },
],
},
false,
createWarningCollector(),
);

assertEquals(body.system, [
{
type: "text",
text: "Shared prompt",
cache_control: { type: "ephemeral", ttl: "1h" },
},
{ type: "text", text: "Dynamic tail" },
]);
});

it("uses the provider alias and normalizes the default cache TTL", () => {
const buildSystem = (providerName: string) =>
buildAnthropicMessagesRequest(
"claude-sonnet-4-5-20250929",
providerName,
{
prompt: [
{
role: "system",
content: "Shared prompt",
providerOptions: {
anthropic: { cacheControl: { type: "ephemeral", ttl: "5m" } },
bedrock: { cacheControl: { type: "ephemeral", ttl: "1h" } },
},
},
{ role: "user", content: [{ type: "text", text: "Hello" }] },
],
},
false,
createWarningCollector(),
).system;

assertEquals(buildSystem("anthropic"), [{
type: "text",
text: "Shared prompt",
cache_control: { type: "ephemeral" },
}]);
assertEquals(buildSystem("bedrock"), [{
type: "text",
text: "Shared prompt",
cache_control: { type: "ephemeral", ttl: "1h" },
}]);
});

it("does not let an undefined provider alias hide the canonical cache control", () => {
const body = buildAnthropicMessagesRequest(
"claude-sonnet-4-5-20250929",
"bedrock",
{
prompt: [
{
role: "system",
content: "Shared prompt",
providerOptions: {
anthropic: { cacheControl: { type: "ephemeral", ttl: "1h" } },
bedrock: { cacheControl: undefined },
},
},
{ role: "user", content: [{ type: "text", text: "Hello" }] },
],
},
false,
createWarningCollector(),
);

assertEquals(body.system, [{
type: "text",
text: "Shared prompt",
cache_control: { type: "ephemeral", ttl: "1h" },
}]);
});

it("applies the call-level cache breakpoint to the final system block", () => {
const body = buildAnthropicMessagesRequest(
"claude-sonnet-4-5-20250929",
"anthropic",
{
prompt: [
{
role: "system",
content: "Shared prompt",
providerOptions: {
anthropic: { cacheControl: { type: "ephemeral", ttl: "1h" } },
},
},
{ role: "system", content: "Dynamic tail" },
{ role: "user", content: [{ type: "text", text: "Hello" }] },
],
cacheControl: { system: true },
},
false,
createWarningCollector(),
);

assertEquals(body.system, [
{
type: "text",
text: "Shared prompt",
cache_control: { type: "ephemeral", ttl: "1h" },
},
{
type: "text",
text: "Dynamic tail",
cache_control: { type: "ephemeral" },
},
]);
});

it("rejects system cache metadata accessors without invoking them", () => {
let accessed = false;
const cacheControl = Object.defineProperty({}, "type", {
enumerable: true,
get() {
accessed = true;
return "ephemeral";
},
});

assertThrows(
() =>
buildAnthropicMessagesRequest(
"claude-sonnet-4-5-20250929",
"anthropic",
{
prompt: [
{
role: "system",
content: "Shared prompt",
providerOptions: { anthropic: { cacheControl } },
},
{ role: "user", content: [{ type: "text", text: "Hello" }] },
],
},
false,
createWarningCollector(),
),
TypeError,
"only enumerable data properties",
);
assertEquals(accessed, false);
});

it("rejects system provider-options accessors without invoking them", () => {
let accessed = 0;
const systemMessage = Object.defineProperty(
{ role: "system", content: "Shared prompt" },
"providerOptions",
{
enumerable: true,
get() {
accessed += 1;
return { anthropic: { cacheControl: { type: "ephemeral" } } };
},
},
) as ModelRuntimePromptMessage;

assertThrows(
() =>
buildAnthropicMessagesRequest(
"claude-sonnet-4-5-20250929",
"anthropic",
{
prompt: [
systemMessage,
{ role: "user", content: [{ type: "text", text: "Hello" }] },
],
},
false,
createWarningCollector(),
),
TypeError,
"providerOptions must be an own enumerable data property",
);
assertEquals(accessed, 0);
});

it("rejects provider cache-control accessors without invoking them", () => {
let accessed = false;
const anthropicOptions = Object.defineProperty({}, "cacheControl", {
enumerable: true,
get() {
accessed = true;
return { type: "ephemeral", ttl: "1h" };
},
});

assertThrows(
() =>
buildAnthropicMessagesRequest(
"claude-sonnet-4-5-20250929",
"anthropic",
{
prompt: [
{
role: "system",
content: "Shared prompt",
providerOptions: { anthropic: anthropicOptions },
},
{ role: "user", content: [{ type: "text", text: "Hello" }] },
],
},
false,
createWarningCollector(),
),
TypeError,
"enumerable data propert",
);
assertEquals(accessed, false);
});

it("preserves Messages request shaping, provider option merge order, and warnings", () => {
const prompt: RuntimePromptMessage[] = [
{ role: "system", content: "You are careful." },
Expand Down
Loading