From aff1f4dfe7871836efbfa159c398ac14cee34453 Mon Sep 17 00:00:00 2001 From: Kentaro Wakayama Date: Wed, 29 Jul 2026 16:31:50 +0200 Subject: [PATCH 1/8] Enforce agent skill selector policy --- deno.json | 2 +- src/agent/factory.test.ts | 287 +++++++++++++++--- src/agent/factory.ts | 81 ++++- .../hosted/agent-project-steering.test.ts | 2 +- src/agent/hosted/chat-preparation.test.ts | 20 +- src/agent/hosted/chat-preparation.ts | 36 +-- src/agent/hosted/chat-runtime-contract.ts | 3 + .../hosted/chat-runtime-tool-assembly.test.ts | 6 +- src/agent/hosted/cloud-agent-child-tools.ts | 85 ++++-- src/agent/hosted/default-chat-runtime.ts | 2 + src/agent/hosted/default-invoke-agent-tool.ts | 1 + .../default-project-steering-refresh.test.ts | 49 +++ .../default-project-steering-refresh.ts | 58 +++- .../hosted/project-steering-adapter.test.ts | 52 +++- src/agent/hosted/project-steering-adapter.ts | 51 +++- .../hosted/runtime-essential-tools.test.ts | 27 +- src/agent/hosted/runtime-essential-tools.ts | 21 +- .../veryfront-cloud-agent-service.test.ts | 233 +++++++++++++- .../hosted/veryfront-cloud-agent-service.ts | 2 + src/agent/project/context.ts | 2 + .../runtime/agent-markdown-adapter.test.ts | 8 +- src/agent/runtime/agent-runtime-step.test.ts | 56 ++++ src/agent/runtime/agent-runtime-step.ts | 23 +- src/agent/runtime/load-skill-tool.test.ts | 47 +++ src/agent/runtime/load-skill-tool.ts | 34 ++- src/agent/runtime/skill-metadata.test.ts | 162 ++++++++++ src/agent/runtime/skill-metadata.ts | 33 ++ src/agent/service/runtime.test.ts | 24 +- .../request/agent-stream.handler.test.ts | 6 +- .../request/api/project-discovery.test.ts | 37 +-- src/skill/registry.test.ts | 112 ++++++- src/skill/registry.ts | 30 ++ src/skill/selector.ts | 150 +++++++++ src/skill/skill-owner-scope.test.ts | 33 +- src/skill/tools.test.ts | 70 +++++ src/skill/tools.ts | 52 +++- src/utils/version-constant.ts | 2 +- 37 files changed, 1703 insertions(+), 196 deletions(-) create mode 100644 src/skill/selector.ts diff --git a/deno.json b/deno.json index ba977cc5df..e3e9abfaec 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "veryfront", - "version": "0.1.1175", + "version": "0.1.1176", "license": "Apache-2.0", "nodeModulesDir": "auto", "minimumDependencyAge": { diff --git a/src/agent/factory.test.ts b/src/agent/factory.test.ts index 7f30aae7d0..1ceb4c78cf 100644 --- a/src/agent/factory.test.ts +++ b/src/agent/factory.test.ts @@ -12,9 +12,57 @@ import { VeryfrontError } from "#veryfront/errors"; import { getEffectiveAgentSystem } from "./runtime/effective-agent-system.ts"; import { agentRegistry } from "./composition/index.ts"; import { agent } from "./factory.ts"; -import type { AgentConfig } from "./types.ts"; +import type { AgentConfig, AgentResponse } from "./types.ts"; import { registerSkill, skillRegistry } from "#veryfront/skill/registry.ts"; import { reset as resetExtensionContracts, tryResolve } from "#veryfront/extensions/contracts.ts"; +import { createSkillTestAdapter } from "#veryfront/skill/testing.ts"; +import type { ModelRuntime } from "#veryfront/provider"; + +function createSkill(id: string, description: string) { + return { + id, + metadata: { name: id, description }, + rootPath: `/test/skills/${id}`, + }; +} + +function createLoadSkillModel(skillId: string): ModelRuntime { + let callCount = 0; + return { + provider: "hosted", + modelId: `hosted/load-${skillId}`, + async doGenerate() { + callCount++; + if (callCount === 1) { + return { + content: [{ + type: "tool-call", + toolCallId: `load-${skillId}`, + toolName: "load_skill", + input: JSON.stringify({ skillId }), + }], + finishReason: "tool-calls", + usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 }, + }; + } + return { + content: [{ type: "text", text: "done" }], + finishReason: "stop", + usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 }, + }; + }, + async doStream() { + return { + stream: new ReadableStream({ + start(controller) { + controller.enqueue({ type: "finish", finishReason: "stop" }); + controller.close(); + }, + }), + }; + }, + }; +} describe("agent factory", () => { beforeEach(() => { @@ -29,22 +77,18 @@ describe("agent factory", () => { const assistant = agent({ id: "schema-bootstrap", system: "Stay helpful." }); assertEquals(typeof tryResolve<{ object: unknown }>("SchemaValidator")?.object, "function"); - assertEquals(assistant.config.tools, { - load_skill: true, - load_skill_reference: true, - execute_skill_script: true, - }); + assertEquals(Object.keys(assistant.config.tools ?? {}).sort(), [ + "execute_skill_script", + "load_skill", + "load_skill_reference", + ]); }); - it("enables skill infrastructure for every agent and defaults to visible skills", async () => { - registerSkill("support-triage", { - id: "support-triage", - metadata: { - name: "support-triage", - description: "Triage incoming support requests", - }, - rootPath: "/test/skills/support-triage", - }); + it("enables skill infrastructure for skill-enabled agents and defaults to visible skills", async () => { + registerSkill( + "support-triage", + createSkill("support-triage", "Triage incoming support requests"), + ); registerSkill("researcher--cite", { id: "researcher--cite", metadata: { name: "cite", description: "Cite primary sources" }, @@ -58,11 +102,11 @@ describe("agent factory", () => { system: "You are a custom agent.", }); - assertEquals(assistant.config.tools, { - load_skill: true, - load_skill_reference: true, - execute_skill_script: true, - }); + assertEquals(Object.keys(assistant.config.tools ?? {}).sort(), [ + "execute_skill_script", + "load_skill", + "load_skill_reference", + ]); assertEquals(toolRegistry.has("load_skill"), true); const effectiveSystem = getEffectiveAgentSystem(assistant); const prompt = typeof effectiveSystem === "function" @@ -79,11 +123,7 @@ describe("agent factory", () => { system: "Do not advertise skills.", skills: [], }); - assertEquals(explicitlyEmpty.config.tools, { - load_skill: true, - load_skill_reference: true, - execute_skill_script: true, - }); + assertEquals(explicitlyEmpty.config.tools, undefined); const explicitlyEmptySystem = getEffectiveAgentSystem(explicitlyEmpty); const explicitlyEmptyPrompt = typeof explicitlyEmptySystem === "function" ? await explicitlyEmptySystem() @@ -91,18 +131,191 @@ describe("agent factory", () => { assertEquals(explicitlyEmptyPrompt.includes("## Available Skills"), false); }); + it("uses the same selector snapshot for prompt disclosure and direct skill tools", async () => { + registerSkill("global-plan", createSkill("global-plan", "Plan the work")); + registerSkill("global-review", createSkill("global-review", "Review the work")); + registerSkill("writer--draft", { + ...createSkill("writer--draft", "Draft copy"), + ownerAgentId: "writer", + shortName: "draft", + }); + + const none = agent({ + id: "no-skills", + system: "No skills.", + skills: [], + tools: { + ordinary_tool: tool({ + id: "ordinary_tool", + description: "Ordinary tool", + inputSchema: defineSchema((v) => v.object({}))(), + execute: async () => ({ ok: true }), + }), + }, + }); + assertEquals(Object.keys(none.config.tools ?? {}).sort(), ["ordinary_tool"]); + const noneSystem = getEffectiveAgentSystem(none); + assertEquals( + (typeof noneSystem === "function" ? await noneSystem() : noneSystem ?? "").includes( + "Available Skills", + ), + false, + ); + + const allowlisted = agent({ + id: "writer", + system: "Use selected skills.", + skills: ["draft", "global-plan"], + }); + const allowlistedSystem = getEffectiveAgentSystem(allowlisted); + const prompt = typeof allowlistedSystem === "function" + ? await allowlistedSystem() + : allowlistedSystem ?? ""; + + assertStringIncludes(prompt, "**writer--draft**: Draft copy"); + assertStringIncludes(prompt, "**global-plan**: Plan the work"); + assertEquals(prompt.includes("global-review"), false); + + if (!allowlisted.config.tools || allowlisted.config.tools === true) { + throw new Error("Expected a concrete skill tool map"); + } + assertEquals(typeof allowlisted.config.tools.load_skill, "object"); + assertThrows( + () => + agent({ + id: "unknown-skill-agent", + system: "Bad config.", + skills: ["missing"], + }), + Error, + "configured skills are not available", + ); + }); + + it("enforces the skill allowlist for tools true registry execution", async () => { + let selectedReads = 0; + let excludedReads = 0; + const selectedAdapter = createSkillTestAdapter({ + "/test/skills/selected/SKILL.md": `--- +name: selected +description: Selected skill +--- +# Selected`, + }); + const excludedAdapter = createSkillTestAdapter({ + "/test/skills/excluded/SKILL.md": `--- +name: excluded +description: Excluded skill +--- +# Excluded`, + }); + registerSkill("selected", { + ...createSkill("selected", "Selected skill"), + fsAdapter: { + ...selectedAdapter, + async readFile(path) { + selectedReads++; + return await selectedAdapter.readFile(path); + }, + }, + }); + registerSkill("excluded", { + ...createSkill("excluded", "Excluded skill"), + fsAdapter: { + ...excludedAdapter, + async readFile(path) { + excludedReads++; + return await excludedAdapter.readFile(path); + }, + }, + }); + + async function runLoad(skillId: string): Promise { + const assistant = agent({ + id: `tools-true-${skillId}`, + model: "hosted/load-skill", + system: "Load a skill.", + tools: true, + skills: ["selected"], + resolveModelTransport: async () => ({ model: createLoadSkillModel(skillId) }), + }); + return await assistant.generate({ input: `Load ${skillId}` }); + } + + const selected = await runLoad("selected"); + assertEquals(selected.toolCalls[0]?.status, "completed"); + assertEquals(selectedReads, 1); + + const excluded = await runLoad("excluded"); + assertEquals(excluded.toolCalls[0]?.status, "error"); + assertStringIncludes(excluded.toolCalls[0]?.error ?? "", "not available to this agent"); + assertEquals(excludedReads, 0); + }); + + it("does not let runtime state spoof tools true skill authorization", async () => { + let excludedReads = 0; + const selectedAdapter = createSkillTestAdapter({ + "/test/skills/selected/SKILL.md": `--- +name: selected +description: Selected skill +--- +# Selected`, + }); + const excludedAdapter = createSkillTestAdapter({ + "/test/skills/excluded/SKILL.md": `--- +name: excluded +description: Excluded skill +--- +# Excluded`, + }); + registerSkill("selected", { + ...createSkill("selected", "Selected skill"), + fsAdapter: selectedAdapter, + }); + registerSkill("excluded", { + ...createSkill("excluded", "Excluded skill"), + fsAdapter: { + ...excludedAdapter, + async readFile(path) { + excludedReads++; + return await excludedAdapter.readFile(path); + }, + }, + }); + + const assistant = agent({ + id: "tools-true-spoofed-selector", + model: "hosted/load-skill", + system: "Load a skill.", + tools: true, + skills: ["selected"], + resolveRuntimeState: async () => ({ + context: { allowedSkillIds: ["excluded"] }, + }), + resolveModelTransport: async () => ({ model: createLoadSkillModel("excluded") }), + }); + + const response = await assistant.generate({ input: "Load excluded" }); + + assertEquals(response.toolCalls[0]?.status, "error"); + assertStringIncludes(response.toolCalls[0]?.error ?? "", "not available to this agent"); + assertEquals(excludedReads, 0); + }); + it("derives load_skill from skills without user-authored tools config", () => { + registerSkill("code-review", createSkill("code-review", "Review code")); + const assistant = agent({ id: "skill-platform-tool-test", system: "Use skills when they match the task.", skills: ["code-review"], }); - assertEquals(assistant.config.tools, { - load_skill: true, - load_skill_reference: true, - execute_skill_script: true, - }); + assertEquals(Object.keys(assistant.config.tools ?? {}).sort(), [ + "execute_skill_script", + "load_skill", + "load_skill_reference", + ]); assertEquals(toolRegistry.has("load_skill"), true); assertEquals(toolRegistry.has("load-skill"), false); }); @@ -125,14 +338,15 @@ describe("agent factory", () => { throw new Error("Expected an agent tool map"); } assertStrictEquals(assistant.config.tools.load_skill, runtimeLoadSkill); - assertEquals(assistant.config.tools.load_skill_reference, true); - assertEquals(assistant.config.tools.execute_skill_script, true); + assertEquals(typeof assistant.config.tools.load_skill_reference, "object"); + assertEquals(typeof assistant.config.tools.execute_skill_script, "object"); }); - it("does not let false disable universal skill infrastructure", () => { + it("treats legacy skills false as the explicit none selector", () => { const assistant = agent({ id: "universal-skill-tools", system: "Use skills when they match the task.", + skills: false, tools: { load_skill: false, load_skill_reference: false, @@ -140,11 +354,8 @@ describe("agent factory", () => { }, }); - assertEquals(assistant.config.tools, { - load_skill: true, - load_skill_reference: true, - execute_skill_script: true, - }); + assertEquals(assistant.config.tools, {}); + assertEquals(assistant.config.skills, false); }); it("binds one scoped tool for each declared delegate", () => { diff --git a/src/agent/factory.ts b/src/agent/factory.ts index 051664dab7..651c9ba169 100644 --- a/src/agent/factory.ts +++ b/src/agent/factory.ts @@ -83,6 +83,21 @@ const SKILL_TOOL_REGISTRATIONS = [ { id: "execute_skill_script", create: createExecuteSkillScriptTool }, ] as const; +function isExplicitNoneSkillSelector(skills: AgentConfig["skills"]): boolean { + return skills === false || (Array.isArray(skills) && skills.length === 0); +} + +function withAllowedSkillIdsContext( + context: Record | undefined, + allowedSkillIds: readonly string[], + shouldAttachAllowedSkillIds: boolean, +): Record | undefined { + if (!shouldAttachAllowedSkillIds) { + return context; + } + return { ...context, allowedSkillIds: [...allowedSkillIds] }; +} + function createAgentStreamResult(stream: ReadableStream): AgentStreamResult { return { toDataStreamResponse(options): Response { @@ -108,6 +123,15 @@ export function agent(config: AgentConfig): Agent { const id = config.id ?? generateAgentId(); const delegates = normalizeAgentDelegateIds(id, config.delegates); + const skillsConfig = config.skills === false ? [] : config.skills; + const shouldAttachAllowedSkillIds = skillsConfig !== undefined; + + const resolveSkillSnapshot = () => + skillRegistry.resolveSelectorForAgent(skillsConfig, { agentId: id }); + + if (Array.isArray(skillsConfig) && skillsConfig.length > 0) { + resolveSkillSnapshot(); + } const publicConfig: ResolvedAgentConfig = { ...config, @@ -128,9 +152,10 @@ export function agent(config: AgentConfig): Agent { } } - // Skill tools are framework infrastructure shared by every agent. Project - // skills remain project-scoped and owner-aware at resolution time. + // Skill tools are framework infrastructure shared by skill-enabled agents. + // Project skills remain project-scoped and owner-aware at resolution time. let mergedToolsConfig = config.tools; + const shouldExposeSkillTools = !isExplicitNoneSkillSelector(config.skills); ensureBuiltinSchemaValidator(); for (const registration of SKILL_TOOL_REGISTRATIONS) { @@ -142,14 +167,24 @@ export function agent(config: AgentConfig): Agent { if (config.tools !== true) { const configuredTools = { ...(config.tools ?? {}) }; for (const registration of SKILL_TOOL_REGISTRATIONS) { + if (!shouldExposeSkillTools) { + delete configuredTools[registration.id]; + continue; + } + const configuredTool = configuredTools[registration.id]; - // Skill infrastructure cannot be disabled with `false`. Preserve - // concrete tools because hosted runs bind them to request context. - if (typeof configuredTool !== "object" || configuredTool === null) { - configuredTools[registration.id] = true; + if (typeof configuredTool === "object" && configuredTool !== null) { + continue; } + + configuredTools[registration.id] = registration.create({ + resolveAllowedSkillIds: () => resolveSkillSnapshot().allowedSkillIds, + }); } - mergedToolsConfig = configuredTools; + const hasConfiguredTools = Object.keys(configuredTools).length > 0; + mergedToolsConfig = hasConfiguredTools || config.tools !== undefined + ? configuredTools + : undefined; } if (delegates?.length) { @@ -168,13 +203,13 @@ export function agent(config: AgentConfig): Agent { // System prompt augmentation with skill manifest. // Re-resolve registry-backed entries at invocation time so HMR changes are picked up. const originalSystem = config.system; - const skillsConfig = config.skills === false ? [] : config.skills ?? true; const augmentedSystem = async () => { // Owner-aware: omitted selectors advertise every skill visible to this // agent (unowned project skills plus its own). Explicit lists, including // an empty list, retain their authored catalog selection. - const currentSkills = skillRegistry.resolveForAgent(skillsConfig, { agentId: id }); + const snapshot = resolveSkillSnapshot(); + const currentSkills = new Map(snapshot.definitions.map((skill) => [skill.id, skill])); const basePrompt = (typeof originalSystem === "function" ? await originalSystem() : originalSystem) ?? "You are a helpful assistant."; @@ -227,10 +262,15 @@ export function agent(config: AgentConfig): Agent { generate(input): Promise { return withSpan( "agent.factory.generate", - () => - runtime.generate( + () => { + const skillSnapshot = resolveSkillSnapshot(); + return runtime.generate( input.input, - input.context, + withAllowedSkillIdsContext( + input.context, + skillSnapshot.allowedSkillIds, + shouldAttachAllowedSkillIds, + ), input.model, input.maxOutputTokens, input.abortSignal, @@ -238,7 +278,8 @@ export function agent(config: AgentConfig): Agent { toolReplacements: input.tools, retainSkillLoaderTools: input.retainSkillLoaderTools, }, - ), + ); + }, { "agent.id": id }, ); }, @@ -257,9 +298,14 @@ export function agent(config: AgentConfig): Agent { ] : (input.messages ?? []); + const skillSnapshot = resolveSkillSnapshot(); const stream = await runtime.stream( inputMessages, - input.context, + withAllowedSkillIdsContext( + input.context, + skillSnapshot.allowedSkillIds, + shouldAttachAllowedSkillIds, + ), { onToolCall: input.onToolCall, onChunk: input.onChunk, @@ -299,9 +345,14 @@ export function agent(config: AgentConfig): Agent { } const messages = body.messages; + const skillSnapshot = resolveSkillSnapshot(); const stream = await runtime.stream( messages, - body.context, + withAllowedSkillIdsContext( + body.context, + skillSnapshot.allowedSkillIds, + shouldAttachAllowedSkillIds, + ), undefined, modelOverride, body.maxOutputTokens, diff --git a/src/agent/hosted/agent-project-steering.test.ts b/src/agent/hosted/agent-project-steering.test.ts index f3b67ca8ca..900ea0594d 100644 --- a/src/agent/hosted/agent-project-steering.test.ts +++ b/src/agent/hosted/agent-project-steering.test.ts @@ -212,7 +212,7 @@ Plan carefully.`, projectId: null, authToken: "auth-token", branchId: null, - availableSkillIds: [], + availableSkillIds: ["plan"], availableToolNames: [], }); const result = await tool.execute({ skillId: "plan" }); diff --git a/src/agent/hosted/chat-preparation.test.ts b/src/agent/hosted/chat-preparation.test.ts index 68a3b409df..d9d7a3a9a7 100644 --- a/src/agent/hosted/chat-preparation.test.ts +++ b/src/agent/hosted/chat-preparation.test.ts @@ -268,6 +268,10 @@ Deno.test("prepareHostedChatRuntimeCreationOptions builds runtime options from r parentRunId: "run-1", parentMessageId: "message-1", availableSkillIds: ["debug"], + skillSelectorPolicy: { + kind: "all-visible", + source: "omitted", + }, publishParentRunEvents: result.creationOptions.publishParentRunEvents, clientProfile: null, liveProjectSteering: { @@ -278,6 +282,10 @@ Deno.test("prepareHostedChatRuntimeCreationOptions builds runtime options from r maxSteps: 50, tools: ["get_agent", "load_skill", "update_agent"], }, + skillSelectorPolicy: { + kind: "all-visible", + source: "omitted", + }, environmentContext: "Browser workspace", initialProjectInstructions: "Project instructions", initialSkills: [skill], @@ -1269,9 +1277,12 @@ Deno.test("prepareHostedChatRuntimeCreationOptions applies the skill selector an }); const advertised = ["researcher--cite"]; - const loadable = ["global-howto", "researcher--cite"]; assertEquals(seenByInstructions, [advertised]); - assertEquals(result.creationOptions.availableSkillIds, loadable); + assertEquals(result.creationOptions.availableSkillIds, advertised); + assertEquals(result.creationOptions.skillSelectorPolicy, { + kind: "allowlist", + entries: ["cite"], + }); assertEquals(result.creationOptions.skillSourcePaths, { "researcher--cite": "agents/researcher/skills/cite/SKILL.md", }); @@ -1284,7 +1295,7 @@ Deno.test("prepareHostedChatRuntimeCreationOptions applies the skill selector an assertEquals(result.steering.skills.map((skill) => skill.id), advertised); }); -Deno.test("prepareHostedChatRuntimeCreationOptions keeps the loader but advertises no skills for an empty selector", async () => { +Deno.test("prepareHostedChatRuntimeCreationOptions uses the exact selector snapshot for an empty selector", async () => { const result = await prepareHostedChatRuntimeCreationOptions({ request: createParsedHostedChatRequest({}), agentConfig: { id: "researcher", model: "configured-model", skills: [] }, @@ -1306,7 +1317,8 @@ Deno.test("prepareHostedChatRuntimeCreationOptions keeps the loader but advertis buildInstructions: (input) => [{ role: "system", content: `${input.skills.length}` }], }); - assertEquals(result.creationOptions.availableSkillIds, ["global-howto"]); + assertEquals(result.creationOptions.availableSkillIds, []); + assertEquals(result.creationOptions.skillSelectorPolicy, { kind: "none" }); assertEquals(result.creationOptions.instructions, [{ role: "system", content: "0" }]); assertEquals(result.steering.skills, []); }); diff --git a/src/agent/hosted/chat-preparation.ts b/src/agent/hosted/chat-preparation.ts index 138e3403e6..12cffa440e 100644 --- a/src/agent/hosted/chat-preparation.ts +++ b/src/agent/hosted/chat-preparation.ts @@ -29,9 +29,10 @@ import { import { getRuntimeUploadUrl } from "../runtime/upload-url-client.ts"; import { getProviderNativeToolNames } from "../runtime/provider-native-tool-inventory.ts"; import { - resolveRuntimeSkillsForAgent, + resolveRuntimeSkillSelectorForAgent, type RuntimeSkillDefinition, } from "../runtime/skill-metadata.ts"; +import type { ResolvedSkillSelectorPolicy } from "#veryfront/skill/selector.ts"; import { applyContextBudget, type ContextBudgetDiagnostics, @@ -281,12 +282,14 @@ export function normalizeParsedHostedChatRequest( function buildHostedChatRuntimeProjectSteering(input: { agentConfig: TRuntimeAgentDefinition; + skillSelectorPolicy: ResolvedSkillSelectorPolicy; environmentContext?: string; instructions: string; skills: RuntimeSkillDefinition[]; }): HostedChatRuntimeProjectSteering { return { agent: input.agentConfig, + skillSelectorPolicy: input.skillSelectorPolicy, ...(input.environmentContext ? { environmentContext: input.environmentContext } : {}), ...(input.instructions ? { initialProjectInstructions: input.instructions } : {}), ...(input.skills.length > 0 ? { initialSkills: input.skills } : {}), @@ -304,26 +307,19 @@ export async function prepareHostedChatRuntimeCreationOptions< authToken: input.authToken, branchId: input.branchId, }); - // The selector controls what the prompt advertises, not what load_skill can - // resolve. Keep the hosted execution gate aligned with the classic runtime: - // every owner-visible skill remains loadable by id. - const loadableSkills = resolveRuntimeSkillsForAgent({ + const skillSelectorSnapshot = resolveRuntimeSkillSelectorForAgent({ skills: steering.skills, agentId: input.agentConfig.id, - selector: true, - }); - const advertisedSkills = resolveRuntimeSkillsForAgent({ - skills: steering.skills, - agentId: input.agentConfig.id, - selector: input.agentConfig.skills, + selector: input.agentConfig.skills === false ? [] : input.agentConfig.skills, }); + const selectedSkills = skillSelectorSnapshot.definitions; const agentInstructions = input.buildInstructions({ agentConfig: input.agentConfig, projectId: input.projectId, branchId: input.branchId, environmentContext: input.environmentContext, instructions: steering.instructions, - skills: advertisedSkills, + skills: selectedSkills, }); const runtimeConfig = resolveHostedRuntimeRequestConfig({ request: input.request, @@ -375,14 +371,11 @@ export async function prepareHostedChatRuntimeCreationOptions< ...(input.rootRunContext?.effectiveParentMessageId ? { parentMessageId: input.rootRunContext.effectiveParentMessageId } : {}), - availableSkillIds: loadableSkills.map((skill) => skill.id), - ...(loadableSkills.some((skill) => skill.sourcePath) + availableSkillIds: skillSelectorSnapshot.allowedSkillIds, + skillSelectorPolicy: skillSelectorSnapshot.policy, + ...(Object.keys(skillSelectorSnapshot.skillSourcePaths).length > 0 ? { - skillSourcePaths: Object.fromEntries( - loadableSkills - .filter((skill) => skill.sourcePath) - .map((skill) => [skill.id, skill.sourcePath as string]), - ), + skillSourcePaths: skillSelectorSnapshot.skillSourcePaths, } : {}), ...(input.rootRunContext?.publishParentRunEvents @@ -391,14 +384,15 @@ export async function prepareHostedChatRuntimeCreationOptions< clientProfile: runtimeConfig.clientProfile, liveProjectSteering: buildHostedChatRuntimeProjectSteering({ agentConfig: input.agentConfig, + skillSelectorPolicy: skillSelectorSnapshot.policy, environmentContext: input.environmentContext, instructions: steering.instructions, - skills: advertisedSkills, + skills: selectedSkills, }), }, steering: { ...steering, - skills: advertisedSkills, + skills: selectedSkills, agentInstructions, }, runtimeConfig, diff --git a/src/agent/hosted/chat-runtime-contract.ts b/src/agent/hosted/chat-runtime-contract.ts index 52d29e721f..b671d86f72 100644 --- a/src/agent/hosted/chat-runtime-contract.ts +++ b/src/agent/hosted/chat-runtime-contract.ts @@ -9,6 +9,7 @@ import type { AgentRuntimeMessage } from "../runtime/message-adapter.ts"; import type { ConversationRunEvent } from "../conversation/run-events.ts"; import type { RuntimeClientProfile } from "../runtime/client-profile.ts"; import type { RuntimeSkillDefinition } from "../runtime/skill-metadata.ts"; +import type { ResolvedSkillSelectorPolicy } from "#veryfront/skill/selector.ts"; /** Public API contract for hosted chat runtime finish part. */ export type HostedChatRuntimeFinishPart = { @@ -98,6 +99,7 @@ export type HostedChatRuntimeCreationResult /** Public API contract for hosted chat runtime project steering. */ export type HostedChatRuntimeProjectSteering = { agent: TRuntimeAgentDefinition; + skillSelectorPolicy?: ResolvedSkillSelectorPolicy; environmentContext?: string; initialProjectInstructions?: string; initialSkills?: RuntimeSkillDefinition[]; @@ -138,6 +140,7 @@ export type HostedChatRuntimeCreationOptions discovered SKILL.md source path (owner-aware catalog). */ skillSourcePaths?: Readonly>; publishParentRunEvents?: (events: ConversationRunEvent[]) => Promise; diff --git a/src/agent/hosted/chat-runtime-tool-assembly.test.ts b/src/agent/hosted/chat-runtime-tool-assembly.test.ts index 5c15f55eff..b557774fd9 100644 --- a/src/agent/hosted/chat-runtime-tool-assembly.test.ts +++ b/src/agent/hosted/chat-runtime-tool-assembly.test.ts @@ -179,7 +179,7 @@ Deno.test("prepareHostedChatRuntimeToolAssembly keeps skill infrastructure for c assertEquals(taskContext.availableToolNames, ["invoke_agent", "load_skill"]); }); -Deno.test("prepareHostedChatRuntimeToolAssembly keeps the loader for config-derived empty non-skill runs", async () => { +Deno.test("prepareHostedChatRuntimeToolAssembly removes skill infrastructure for known empty skill runs", async () => { const taskContext: HostedChatRuntimeToolAssemblyContext = { authToken: "token", projectId: "project-1", @@ -204,8 +204,8 @@ Deno.test("prepareHostedChatRuntimeToolAssembly keeps the loader for config-deri preloadLatestConversationUserText: false, }); - assertEquals(toolAssembly.localToolNames, ["load_skill"]); - assertEquals(taskContext.availableToolNames, ["load_skill"]); + assertEquals(toolAssembly.localToolNames, []); + assertEquals(taskContext.availableToolNames, []); }); Deno.test("prepareHostedChatRuntimeToolAssembly builds provider-compatible runtime inventory", async () => { diff --git a/src/agent/hosted/cloud-agent-child-tools.ts b/src/agent/hosted/cloud-agent-child-tools.ts index 179270accc..52065e5566 100644 --- a/src/agent/hosted/cloud-agent-child-tools.ts +++ b/src/agent/hosted/cloud-agent-child-tools.ts @@ -28,9 +28,10 @@ import type { AgentMcpToolPolicy } from "../types.ts"; import type { RuntimeLoadSkillToolContext } from "../runtime/load-skill-tool.ts"; import type { RuntimeProjectSteeringLookup } from "../runtime/project-skill-catalog.ts"; import { - resolveRuntimeSkillsForAgent, + resolveRuntimeSkillSelectorForAgent, type RuntimeSkillDefinition, } from "../runtime/skill-metadata.ts"; +import type { ResolvedSkillSelectorSnapshot } from "#veryfront/skill/selector.ts"; import type { RuntimeAgentMarkdownDefinition } from "../runtime/agent-definition.ts"; import { buildAgentDelegateTools } from "../runtime/agent-delegation.ts"; import { buildVeryfrontCloudRuntimeInstructions } from "./cloud-runtime-system-messages.ts"; @@ -55,6 +56,7 @@ import { const HOSTED_CHILD_LOCAL_SKILL_TOOL_NAMES = new Set([ "execute_skill_script", + "load_skill", "load_skill_reference", ]); @@ -68,6 +70,7 @@ export type ChildRunContext = RuntimeLoadSkillToolContext, | "agentId" | "availableSkillIds" + | "skillSelectorPolicy" | "skillSourcePaths" | "loadedSkillResponses" | "loadedSkillReferenceResponses" @@ -229,11 +232,17 @@ function shouldRethrowInvokeAgentError(error: unknown): boolean { /** Resolves the effective tool name allowlist for a hosted child agent run. */ export function resolveHostedChildToolNames( agentConfig: RuntimeAgentMarkdownDefinition, + skillSelectorSnapshot?: Pick< + ResolvedSkillSelectorSnapshot, + "allowedSkillIds" + >, ): string[] | undefined { if (agentConfig.tools === true) { return undefined; } + const hasAuthorizedSkills = skillSelectorSnapshot === undefined || + skillSelectorSnapshot.allowedSkillIds.length > 0; return [ ...new Set([ ...(agentConfig.tools ?? []).filter((toolName) => @@ -241,11 +250,35 @@ export function resolveHostedChildToolNames( ), ...(agentConfig.providerTools ?? []), ...(agentConfig.delegates ?? []).map((id) => `agent_${id}`), - "load_skill", + ...(hasAuthorizedSkills ? ["load_skill"] : []), ]), ]; } +/** Builds host tools for a configured hosted child run. */ +export function buildHostedChildGlobalTools( + context: NodeVeryfrontCloudAgentServiceContext, + input: { + childAgentId: string; + childConfig?: DefaultHostedChildAgentExecutionConfig; + childToolContext: ChildRunContext; + }, +): HostToolSet { + return { + ...(input.childConfig ? getDiscoveredHostTools({ agentId: input.childAgentId }) : {}), + ...(!input.childConfig || (input.childConfig.availableSkillIds?.length ?? 0) > 0 + ? { load_skill: createLoadSkillTool(context, input.childToolContext) } + : {}), + ...(input.childConfig?.delegateIds?.length + ? buildHostedDelegateTools(context, { + delegates: input.childConfig.delegateIds, + selfId: input.childAgentId, + taskContext: input.childToolContext, + }) + : {}), + }; +} + /** Builds the child run context for a nested hosted agent invocation. */ export function buildHostedChildToolContext( globalToolContext: ChildRunContext, @@ -256,7 +289,12 @@ export function buildHostedChildToolContext( return { ...globalToolContext, agentId: childAgentId, - ...(childConfig?.availableSkillIds ? { availableSkillIds: childConfig.availableSkillIds } : {}), + ...(childConfig?.availableSkillIds !== undefined + ? { availableSkillIds: childConfig.availableSkillIds } + : {}), + ...(childConfig?.skillSelectorPolicy + ? { skillSelectorPolicy: childConfig.skillSelectorPolicy } + : {}), ...(childConfig?.skillSourcePaths ? { skillSourcePaths: childConfig.skillSourcePaths } : {}), ...(childConfig?.toolNames ? { availableToolNames: childConfig.toolNames } : {}), loadedSkillResponses: {}, @@ -304,22 +342,12 @@ export async function resolveHostedChildAgentExecutionConfig( authToken: taskContext.authToken, branchId, }, childAgentId); - const advertisedSkills = resolveRuntimeSkillsForAgent({ - skills: steering.skills, - agentId: childAgentId, - selector: agentConfig.skills, - }); - const loadableSkills = resolveRuntimeSkillsForAgent({ + const skillSelectorSnapshot = resolveRuntimeSkillSelectorForAgent({ skills: steering.skills, agentId: childAgentId, - selector: true, + selector: agentConfig.skills === false ? [] : agentConfig.skills, }); - const skillSourcePaths = Object.fromEntries( - loadableSkills - .filter((skill) => skill.sourcePath) - .map((skill) => [skill.id, skill.sourcePath as string]), - ); - const toolNames = resolveHostedChildToolNames(agentConfig); + const toolNames = resolveHostedChildToolNames(agentConfig, skillSelectorSnapshot); const thinking = agentConfig.thinking?.enabled === false ? 0 : agentConfig.thinking?.budgetTokens; return { @@ -328,7 +356,7 @@ export async function resolveHostedChildAgentExecutionConfig( projectId: projectId || null, branchId, instructions: steering.instructions, - skills: advertisedSkills, + skills: skillSelectorSnapshot.definitions, availableToolNames: toolNames, })), ...(agentConfig.model ? { model: agentConfig.model } : {}), @@ -337,8 +365,11 @@ export async function resolveHostedChildAgentExecutionConfig( ...(thinking === undefined ? {} : { thinking }), ...(toolNames === undefined ? {} : { toolNames }), mcpServers: resolveMcpServers(context.options, agentConfig), - availableSkillIds: loadableSkills.map((skill) => skill.id), - ...(Object.keys(skillSourcePaths).length > 0 ? { skillSourcePaths } : {}), + availableSkillIds: skillSelectorSnapshot.allowedSkillIds, + skillSelectorPolicy: skillSelectorSnapshot.policy, + ...(Object.keys(skillSelectorSnapshot.skillSourcePaths).length > 0 + ? { skillSourcePaths: skillSelectorSnapshot.skillSourcePaths } + : {}), ...(agentConfig.delegates === undefined ? {} : { delegateIds: agentConfig.delegates }), }; } @@ -369,17 +400,11 @@ export function createInvokeAgentTool( childConfig, durableChildRun, ); - return { - ...(childConfig ? getDiscoveredHostTools({ agentId: childAgentId }) : {}), - load_skill: createLoadSkillTool(context, childToolContext), - ...(childConfig?.delegateIds?.length - ? buildHostedDelegateTools(context, { - delegates: childConfig.delegateIds, - selfId: childAgentId, - taskContext: childToolContext, - }) - : {}), - }; + return buildHostedChildGlobalTools(context, { + childAgentId, + childConfig, + childToolContext, + }); }, resolveChildAgentExecutionConfig: (childAgentId, projectId) => resolveHostedChildAgentExecutionConfig(context, childContext, childAgentId, projectId), diff --git a/src/agent/hosted/default-chat-runtime.ts b/src/agent/hosted/default-chat-runtime.ts index d3196d8bd4..9b86e592aa 100644 --- a/src/agent/hosted/default-chat-runtime.ts +++ b/src/agent/hosted/default-chat-runtime.ts @@ -88,6 +88,7 @@ export type DefaultHostedChatRuntimeTaskContext = HostedRuntimeStateResolverCont parentRunId?: string; parentMessageId?: string; availableSkillIds?: string[]; + skillSelectorPolicy?: DefaultHostedChatRuntimeCreationOptions["skillSelectorPolicy"]; /** Per-run skill id -> discovered SKILL.md source path (owner-aware catalog). */ skillSourcePaths?: Readonly>; publishParentRunEvents?: DefaultHostedChatRuntimeCreationOptions["publishParentRunEvents"]; @@ -168,6 +169,7 @@ function createDefaultTaskContext( parentRunId: input.options.parentRunId, parentMessageId: input.options.parentMessageId, availableSkillIds: input.options.availableSkillIds, + skillSelectorPolicy: input.options.skillSelectorPolicy, skillSourcePaths: input.options.skillSourcePaths, publishParentRunEvents: input.options.publishParentRunEvents, submittedFormInputResult: input.options.submittedFormInputResult, diff --git a/src/agent/hosted/default-invoke-agent-tool.ts b/src/agent/hosted/default-invoke-agent-tool.ts index 22283cccf1..fa23105f9f 100644 --- a/src/agent/hosted/default-invoke-agent-tool.ts +++ b/src/agent/hosted/default-invoke-agent-tool.ts @@ -108,6 +108,7 @@ export type DefaultHostedChildAgentExecutionConfig = { toolNames?: string[]; mcpServers?: readonly AgentServiceMcpServerConfig[]; availableSkillIds?: string[]; + skillSelectorPolicy?: import("#veryfront/skill/selector.ts").ResolvedSkillSelectorPolicy; skillSourcePaths?: Readonly>; loadedSkillResponses?: RuntimeLoadSkillToolContext["loadedSkillResponses"]; loadedSkillReferenceResponses?: RuntimeLoadSkillToolContext["loadedSkillReferenceResponses"]; diff --git a/src/agent/hosted/default-project-steering-refresh.test.ts b/src/agent/hosted/default-project-steering-refresh.test.ts index 481f0f7e33..f3fe10bbda 100644 --- a/src/agent/hosted/default-project-steering-refresh.test.ts +++ b/src/agent/hosted/default-project-steering-refresh.test.ts @@ -223,6 +223,55 @@ describe("agent/default-hosted-project-steering-refresh", () => { assertEquals(system.includes("Fresh instructions:"), true); }); + it("refreshes omitted selectors dynamically but does not broaden explicit allowlists", async () => { + const refresh = createDefaultHostedProjectSteeringRefresh({ + fetchProjectInstructions: () => Promise.resolve("Fresh instructions"), + fetchSkills: () => Promise.resolve([createSkill("build"), createSkill("new-skill")]), + buildInstructions: (input) => + `${input.instructions}:${input.skills.map((skill) => skill.id).join(",")}`, + }); + + const dynamicInput = createRefreshInput(); + dynamicInput.taskContext.skillSelectorPolicy = { kind: "all-visible", source: "omitted" }; + dynamicInput.taskContext.availableSkillIds = ["build"]; + + const dynamicSystem = await refresh(dynamicInput); + + assertStringIncludes(dynamicSystem, "Fresh instructions:build,new-skill"); + assertEquals(dynamicInput.taskContext.availableSkillIds, ["build", "new-skill"]); + + const explicitInput = createRefreshInput(); + explicitInput.liveProjectSteering.agent.skills = ["build"]; + explicitInput.taskContext.skillSelectorPolicy = { kind: "allowlist", entries: ["build"] }; + explicitInput.taskContext.availableSkillIds = ["build"]; + + const explicitSystem = await refresh(explicitInput); + + assertStringIncludes(explicitSystem, "Fresh instructions:build"); + assertEquals(explicitSystem.includes("new-skill"), false); + assertEquals(explicitInput.taskContext.availableSkillIds, ["build"]); + }); + + it("removes deleted explicit skill selections during refresh", async () => { + const refresh = createDefaultHostedProjectSteeringRefresh({ + fetchProjectInstructions: () => Promise.resolve("Fresh instructions"), + fetchSkills: () => Promise.resolve([createSkill("new-skill")]), + buildInstructions: (input) => + `${input.instructions}:${input.skills.map((skill) => skill.id).join(",")}`, + }); + const input = createRefreshInput(); + input.liveProjectSteering.agent.skills = ["build"]; + input.taskContext.skillSelectorPolicy = { kind: "allowlist", entries: ["build"] }; + input.taskContext.availableSkillIds = ["build"]; + + const system = await refresh(input); + + assertStringIncludes(system, "Fresh instructions:"); + assertEquals(system.includes("build"), false); + assertEquals(system.includes("new-skill"), false); + assertEquals(input.taskContext.availableSkillIds, []); + }); + it("keeps provider-native tools in refreshed runtime inventory", async () => { const refresh = createDefaultHostedProjectSteeringRefresh({ fetchProjectInstructions: () => Promise.resolve("Fresh instructions"), diff --git a/src/agent/hosted/default-project-steering-refresh.ts b/src/agent/hosted/default-project-steering-refresh.ts index 5c41a2b6ea..3796db0377 100644 --- a/src/agent/hosted/default-project-steering-refresh.ts +++ b/src/agent/hosted/default-project-steering-refresh.ts @@ -11,12 +11,16 @@ import type { import type { HostedChatRuntimePreparationSteering } from "./chat-preparation.ts"; import type { RuntimeAgentMarkdownDefinition } from "../runtime/agent-definition.ts"; import { - resolveRuntimeSkillsForAgent, + resolveRuntimeSkillSelectorSnapshotForAgent, type RuntimeSkillDefinition, } from "../runtime/skill-metadata.ts"; import { selectProviderCompatibleToolNames } from "../runtime/provider-tool-compat.ts"; import { flattenSystemInstructions, withRuntimeToolInventory } from "../runtime/tool-inventory.ts"; import type { HostedChatRuntimeInstructionsInput } from "./chat-preparation.ts"; +import { + createNoneSkillSelectorSnapshot, + type ResolvedSkillSelectorPolicy, +} from "#veryfront/skill/selector.ts"; /** Public API contract for default hosted project steering refresh logger. */ export type DefaultHostedProjectSteeringRefreshLogger = { @@ -165,6 +169,46 @@ async function fetchSkillsWithFallback(input: { } } +function resolveRefreshedSkillSnapshot(input: { + skills: readonly RuntimeSkillDefinition[]; + agentId: string; + selector: true | false | readonly string[] | undefined; + policy: ResolvedSkillSelectorPolicy | undefined; +}) { + if ( + !input.policy && + (input.selector === false || (Array.isArray(input.selector) && input.selector.length === 0)) + ) { + return createNoneSkillSelectorSnapshot(); + } + + if (!input.policy && Array.isArray(input.selector)) { + return resolveRuntimeSkillSelectorSnapshotForAgent({ + skills: input.skills, + agentId: input.agentId, + selector: [...input.selector], + }); + } + + if (!input.policy || input.policy.kind === "all-visible") { + return resolveRuntimeSkillSelectorSnapshotForAgent({ + skills: input.skills, + agentId: input.agentId, + selector: input.policy?.source === "true" ? true : undefined, + }); + } + + if (input.policy.kind === "none") { + return createNoneSkillSelectorSnapshot(input.policy); + } + + return resolveRuntimeSkillSelectorSnapshotForAgent({ + skills: input.skills, + agentId: input.agentId, + selector: input.policy.entries, + }); +} + /** Create default hosted project steering refresh. */ export function createDefaultHostedProjectSteeringRefresh( options: CreateDefaultHostedProjectSteeringRefreshOptions, @@ -200,11 +244,19 @@ export function createDefaultHostedProjectSteeringRefresh( }), ]); - const advertisedSkills = resolveRuntimeSkillsForAgent({ + const skillSelectorSnapshot = resolveRefreshedSkillSnapshot({ skills, agentId: input.liveProjectSteering.agent.id, selector: input.liveProjectSteering.agent.skills, + policy: input.taskContext.skillSelectorPolicy ?? + input.liveProjectSteering.skillSelectorPolicy, }); + input.taskContext.availableSkillIds = skillSelectorSnapshot.allowedSkillIds; + input.taskContext.skillSelectorPolicy = skillSelectorSnapshot.policy; + input.taskContext.skillSourcePaths = + Object.keys(skillSelectorSnapshot.skillSourcePaths).length > 0 + ? skillSelectorSnapshot.skillSourcePaths + : undefined; const sourceAllowedRemoteToolNames = applySourceIntegrationPolicy( remoteToolNames, input.toolAssembly.sourceIntegrationPolicy, @@ -228,7 +280,7 @@ export function createDefaultHostedProjectSteeringRefresh( branchId, environmentContext: input.liveProjectSteering.environmentContext, instructions: projectInstructions, - skills: advertisedSkills, + skills: skillSelectorSnapshot.definitions, availableToolNames: toolNames, }); diff --git a/src/agent/hosted/project-steering-adapter.test.ts b/src/agent/hosted/project-steering-adapter.test.ts index 19bdf2a9f4..7acbf05125 100644 --- a/src/agent/hosted/project-steering-adapter.test.ts +++ b/src/agent/hosted/project-steering-adapter.test.ts @@ -1,7 +1,10 @@ import "#veryfront/schemas/_test-setup.ts"; import { assert, assertEquals } from "#veryfront/testing/assert.ts"; import { join } from "node:path"; -import { createHostedProjectSteeringAdapter } from "./project-steering-adapter.ts"; +import { + createHostedProjectSteeringAdapter, + type HostedProjectSkillIdsContext, +} from "./project-steering-adapter.ts"; import type { RuntimeGetProjectFileOptions, RuntimeProjectFile, @@ -118,7 +121,7 @@ Use project instructions.`, getProjectFiles: async () => [{ path: ".veryfront/skills/project/SKILL.md" }], }), }); - const context = { + const context: HostedProjectSkillIdsContext = { projectId: "project-1", authToken: "token-1", branchId: null, @@ -172,7 +175,6 @@ Deno.test("hosted project steering adapter accepts a custom builtin skill store" projectId: null, authToken: "token-1", branchId: null, - availableSkillIds: [], }); const result = await loadSkillTool.execute({ skillId: "custom" }); @@ -240,3 +242,47 @@ Body.`; assertEquals(projectContext.availableSkillIds, ["builtin", "global"]); }); }); + +Deno.test("refreshProjectSkillIds re-resolves authored allowlist entries without broadening", async () => { + await withSkillsDir(async (skillsDir) => { + const adapter = createHostedProjectSteeringAdapter({ + apiUrl: "https://api.example.test", + skillsDir, + projectFilesClient: createProjectFilesClient({ + getProjectFile: async ({ path }) => + path === "skills/global/SKILL.md" || path === "skills/new-skill/SKILL.md" + ? { + path, + content: `--- +description: ${path} +--- +Body.`, + } + : null, + getProjectFiles: async () => [ + { path: "skills/global/SKILL.md" }, + { path: "skills/new-skill/SKILL.md" }, + ], + }), + }); + + const context: HostedProjectSkillIdsContext = { + projectId: "project-1", + authToken: "token-1", + branchId: null, + availableSkillIds: ["global", "new-skill"], + skillSelectorPolicy: { kind: "allowlist" as const, entries: ["global", "deleted"] }, + }; + + await adapter.refreshProjectSkillIds(context); + + assertEquals(context.availableSkillIds, ["global"]); + assertEquals(context.skillSelectorPolicy, { + kind: "allowlist", + entries: ["global", "deleted"], + }); + assertEquals(context.skillSourcePaths, { + global: "skills/global/SKILL.md", + }); + }); +}); diff --git a/src/agent/hosted/project-steering-adapter.ts b/src/agent/hosted/project-steering-adapter.ts index e9e7056fce..9aaa22786e 100644 --- a/src/agent/hosted/project-steering-adapter.ts +++ b/src/agent/hosted/project-steering-adapter.ts @@ -37,7 +37,11 @@ import type { RuntimeSkillDefinition, RuntimeSkillMetadataLogger, } from "../runtime/skill-metadata.ts"; -import { isRuntimeSkillVisibleTo } from "../runtime/skill-metadata.ts"; +import { resolveRuntimeSkillSelectorSnapshotForAgent } from "../runtime/skill-metadata.ts"; +import { + createNoneSkillSelectorSnapshot, + type ResolvedSkillSelectorPolicy, +} from "#veryfront/skill/selector.ts"; /** Public API contract for hosted project steering logger. */ export type HostedProjectSteeringLogger = @@ -67,6 +71,7 @@ export type HostedProjectSkillIdsContext = MutableAgentProjectContext & { * visibility beyond the caller's scope. */ agentId?: string; + skillSelectorPolicy?: ResolvedSkillSelectorPolicy; }; /** Public API contract for hosted project steering adapter. */ @@ -134,6 +139,31 @@ function createDefaultBuiltinStore(): RuntimeLoadSkillBuiltinStore { }; } +function resolveRefreshedSkillSnapshot(input: { + skills: readonly RuntimeSkillDefinition[]; + context: HostedProjectSkillIdsContext; +}) { + const policy = input.context.skillSelectorPolicy; + + if (!policy || policy.kind === "all-visible") { + return resolveRuntimeSkillSelectorSnapshotForAgent({ + skills: input.skills, + agentId: input.context.agentId ?? "", + selector: policy?.source === "true" ? true : undefined, + }); + } + + if (policy.kind === "none") { + return createNoneSkillSelectorSnapshot(policy); + } + + return resolveRuntimeSkillSelectorSnapshotForAgent({ + skills: input.skills, + agentId: input.context.agentId ?? "", + selector: policy.entries, + }); +} + /** Create hosted project steering adapter. */ export function createHostedProjectSteeringAdapter( options: HostedProjectSteeringAdapterOptions, @@ -191,20 +221,11 @@ export function createHostedProjectSteeringAdapter( branchId: context.branchId, }); - // Owner-aware: the refreshed per-run skill set keeps the caller's - // scope — never another agent's owned skills — and the source-path - // map stays in sync so colocated skills do not go stale. - const visibleSkills = skills.filter((skill) => - isRuntimeSkillVisibleTo(skill, { agentId: context.agentId }) - ); - context.availableSkillIds = visibleSkills.map((skill) => skill.id); - const skillSourcePaths = Object.fromEntries( - visibleSkills - .filter((skill) => skill.sourcePath) - .map((skill) => [skill.id, skill.sourcePath as string]), - ); - context.skillSourcePaths = Object.keys(skillSourcePaths).length > 0 - ? skillSourcePaths + const snapshot = resolveRefreshedSkillSnapshot({ skills, context }); + context.availableSkillIds = snapshot.allowedSkillIds; + context.skillSelectorPolicy = snapshot.policy; + context.skillSourcePaths = Object.keys(snapshot.skillSourcePaths).length > 0 + ? snapshot.skillSourcePaths : undefined; }, }; diff --git a/src/agent/hosted/runtime-essential-tools.test.ts b/src/agent/hosted/runtime-essential-tools.test.ts index 6e66d6ca29..a25f3af395 100644 --- a/src/agent/hosted/runtime-essential-tools.test.ts +++ b/src/agent/hosted/runtime-essential-tools.test.ts @@ -72,7 +72,7 @@ describe("resolveHostedRuntimeAllowedToolNames", () => { assertEquals(result?.size, 0); }); - it("keeps skill loading but not discovery tools for a config-derived empty selector", () => { + it("removes skill infrastructure for a config-derived empty selector with a known empty skill manifest", () => { // A sandboxed agent with tools: [] must not gain load_tools/search_tools // even when the config-derived essential-tools flag is set. Activating tools // is a broader capability than running pre-configured skills (load_skill), so @@ -86,12 +86,37 @@ describe("resolveHostedRuntimeAllowedToolNames", () => { "load_skill_reference", ], includeRuntimeEssentialToolsWhenEmpty: true, + availableSkillIds: [], }); assertEquals(result?.has("search_tools"), false); assertEquals(result?.has("load_tools"), false); + assertEquals(result?.has("load_skill"), false); + assertEquals(result?.has("load_skill_reference"), false); + }); + + it("keeps skill loading for legacy unscoped config-derived empty selectors", () => { + const result = resolveHostedRuntimeAllowedToolNames({ + allowedToolNames: new Set(), + localToolNames: ["load_skill", "load_skill_reference"], + includeRuntimeEssentialToolsWhenEmpty: true, + }); + assertEquals(result?.has("load_skill"), true); assertEquals(result?.has("load_skill_reference"), true); }); + + it("removes execute_skill_script from model-visible tools for known empty skill manifests", () => { + const result = resolveHostedRuntimeAllowedToolNames({ + allowedToolNames: new Set(["execute_skill_script", "sleep"]), + localToolNames: ["execute_skill_script", "load_skill", "invoke_agent", "sleep"], + availableSkillIds: [], + }); + + assertEquals(result?.has("execute_skill_script"), false); + assertEquals(result?.has("load_skill"), false); + assertEquals(result?.has("invoke_agent"), false); + assertEquals(result?.has("sleep"), true); + }); }); }); diff --git a/src/agent/hosted/runtime-essential-tools.ts b/src/agent/hosted/runtime-essential-tools.ts index 4763d97c52..5cd1cbe245 100644 --- a/src/agent/hosted/runtime-essential-tools.ts +++ b/src/agent/hosted/runtime-essential-tools.ts @@ -15,6 +15,12 @@ export type ResolveHostedRuntimeAllowedToolNamesInput = { // project-provided script remains a direct execution capability. const SKILL_RUNTIME_TOOL_NAMES = ["load_skill", "load_skill_reference"] as const; const SKILL_DELEGATION_TOOL_NAMES = ["invoke_agent"] as const; +const SKILL_SCRIPT_TOOL_NAMES = ["execute_skill_script"] as const; +const EMPTY_SKILL_MANIFEST_TOOL_NAMES = [ + ...SKILL_RUNTIME_TOOL_NAMES, + ...SKILL_DELEGATION_TOOL_NAMES, + ...SKILL_SCRIPT_TOOL_NAMES, +] as const; /** * Tool discovery tools are unconditionally essential: they must never be @@ -47,6 +53,14 @@ export function resolveHostedRuntimeAllowedToolNames( const localToolNames = new Set(input.localToolNames); const resolvedToolNames = new Set(allowedToolNames); + const hasKnownSkillManifest = input.availableSkillIds !== undefined; + const hasAuthorizedSkills = (input.availableSkillIds?.length ?? 0) > 0; + + if (hasKnownSkillManifest && !hasAuthorizedSkills) { + for (const toolName of EMPTY_SKILL_MANIFEST_TOOL_NAMES) { + resolvedToolNames.delete(toolName); + } + } // Tool discovery is essential only when the agent already has at least one // tool in its resolved set. Under deny-all (empty allowedToolNames), discovery @@ -65,7 +79,10 @@ export function resolveHostedRuntimeAllowedToolNames( // Hosted cloud supplies load_skill; other adapters may also supply the // reference tool. Explicit request-level empty allowlists return above and // remain deny-all. - if (resolvedToolNames.size > 0 || input.includeRuntimeEssentialToolsWhenEmpty) { + if ( + (resolvedToolNames.size > 0 || input.includeRuntimeEssentialToolsWhenEmpty) && + (!hasKnownSkillManifest || hasAuthorizedSkills) + ) { for (const toolName of SKILL_RUNTIME_TOOL_NAMES) { if (localToolNames.has(toolName)) { resolvedToolNames.add(toolName); @@ -73,7 +90,7 @@ export function resolveHostedRuntimeAllowedToolNames( } } - if (!input.availableSkillIds?.length) { + if (!hasAuthorizedSkills) { return resolvedToolNames; } diff --git a/src/agent/hosted/veryfront-cloud-agent-service.test.ts b/src/agent/hosted/veryfront-cloud-agent-service.test.ts index 35a45f9a50..1786535c6e 100644 --- a/src/agent/hosted/veryfront-cloud-agent-service.test.ts +++ b/src/agent/hosted/veryfront-cloud-agent-service.test.ts @@ -264,8 +264,41 @@ Deno.test("hosted child project agents request only materialized skill and deleg ], providerTools: ["web_search"], delegates: ["validation-agent"], - }), - ["get_file", "load_skill", "web_search", "agent_validation-agent"], + })?.toSorted(), + ["agent_validation-agent", "get_file", "load_skill", "web_search"], + ); +}); + +Deno.test("hosted child project agents omit skill tools for an empty skill selector snapshot", () => { + assertEquals( + veryfrontCloudAgentServiceInternals.resolveHostedChildToolNames({ + id: "extraction-agent", + name: "Extraction agent", + description: "Extract an application", + instructions: "Extract the application.", + skills: [], + tools: [ + "get_file", + "execute_skill_script", + "load_skill", + "load_skill_reference", + ], + }, { allowedSkillIds: [] }), + ["get_file"], + ); +}); + +Deno.test("hosted child project agents keep load_skill for a non-empty exact skill allowlist", () => { + assertEquals( + veryfrontCloudAgentServiceInternals.resolveHostedChildToolNames({ + id: "extraction-agent", + name: "Extraction agent", + description: "Extract an application", + instructions: "Extract the application.", + skills: ["extract"], + tools: ["get_file"], + }, { allowedSkillIds: ["extraction-agent--extract"] }), + ["get_file", "load_skill"], ); }); @@ -1252,6 +1285,202 @@ Deno.test("hosted child execution config resolves steering against the target pr ]); }); +Deno.test("hosted child execution config hides skill infrastructure for skills empty and false", async () => { + for (const skills of [[], false] as const) { + try { + toolRegistry.registerShared( + "get_file", + tool({ + id: "get_file", + description: "Get file", + inputSchema: defineSchema((v) => v.object({}))(), + execute: () => ({ ok: true }), + }), + ); + toolRegistry.registerShared("load_skill_reference", createLoadSkillReferenceTool()); + toolRegistry.registerShared("execute_skill_script", createExecuteSkillScriptTool()); + + const childAgent = { + id: "extraction-agent", + name: "Extraction agent", + description: "Extract job applications", + instructions: "Extract the application.", + skills, + tools: [ + "get_file", + "load_skill", + "load_skill_reference", + "execute_skill_script", + ], + }; + const context = { + options: { mcpServers: [] }, + discoveryResult: { agents: new Map([["extraction-agent", null]]) }, + agentConfigs: new Map([["extraction-agent", childAgent]]), + projectSteeringByAgentId: new Map([["extraction-agent", { + getProjectInstructions: () => Promise.resolve("Use extraction policy."), + getSkillsConfig: () => + Promise.resolve([{ + id: "global-skill", + name: "Global skill", + description: "Global skill", + instructions: "Use global skill.", + allowedTools: [], + }]), + createLoadSkillTool: () => + tool({ + id: "load_skill", + description: "Load skill", + inputSchema: defineSchema((v) => v.object({}))(), + execute: () => ({ ok: true }), + }), + }]]), + trace: (_name: string, operation: () => unknown) => operation(), + } as never; + const config = await veryfrontCloudAgentServiceInternals + .resolveHostedChildAgentExecutionConfig( + context, + { + authToken: "token-1", + projectId: "project-1", + branchId: "branch-1", + agentId: "orchestrator", + }, + "extraction-agent", + "project-1", + ); + + assertEquals(config?.availableSkillIds, []); + assertEquals(config?.toolNames, ["get_file"]); + assertEquals(config?.system.includes("global-skill"), false); + assertEquals(config?.system.includes("load_skill"), false); + assertEquals(config?.system.includes("load_skill_reference"), false); + assertEquals(config?.system.includes("execute_skill_script"), false); + const childToolContext = veryfrontCloudAgentServiceInternals.buildHostedChildToolContext( + { + authToken: "token-1", + projectId: "project-1", + branchId: "branch-1", + agentId: "orchestrator", + }, + "extraction-agent", + config, + ); + const hostTools = veryfrontCloudAgentServiceInternals.buildHostedChildGlobalTools( + context, + { + childAgentId: "extraction-agent", + childConfig: config, + childToolContext, + }, + ); + + assertEquals("get_file" in hostTools, true); + assertEquals("load_skill" in hostTools, false); + assertEquals("load_skill_reference" in hostTools, false); + assertEquals("execute_skill_script" in hostTools, false); + } finally { + toolRegistry.clearAll(); + } + } +}); + +Deno.test("hosted child execution config keeps exact non-empty skill authorization", async () => { + try { + toolRegistry.registerShared( + "get_file", + tool({ + id: "get_file", + description: "Get file", + inputSchema: defineSchema((v) => v.object({}))(), + execute: () => ({ ok: true }), + }), + ); + const childAgent = { + id: "extraction-agent", + name: "Extraction agent", + description: "Extract job applications", + instructions: "Extract the application.", + skills: ["extract"], + tools: ["get_file"], + }; + const context = { + options: { mcpServers: [] }, + discoveryResult: { agents: new Map([["extraction-agent", null]]) }, + agentConfigs: new Map([["extraction-agent", childAgent]]), + projectSteeringByAgentId: new Map([["extraction-agent", { + getProjectInstructions: () => Promise.resolve("Use extraction policy."), + getSkillsConfig: () => + Promise.resolve([{ + id: "extraction-agent--extract", + name: "Extract", + description: "Extract skill", + instructions: "Extract with skill.", + allowedTools: [], + ownerAgentId: "extraction-agent", + shortName: "extract", + sourcePath: "agents/extraction-agent/skills/extract/SKILL.md", + }, { + id: "global-skill", + name: "Global skill", + description: "Global skill", + instructions: "Use global skill.", + allowedTools: [], + }]), + createLoadSkillTool: () => + tool({ + id: "load_skill", + description: "Load skill", + inputSchema: defineSchema((v) => v.object({}))(), + execute: () => ({ ok: true }), + }), + }]]), + trace: (_name: string, operation: () => unknown) => operation(), + } as never; + const config = await veryfrontCloudAgentServiceInternals + .resolveHostedChildAgentExecutionConfig( + context, + { + authToken: "token-1", + projectId: "project-1", + branchId: "branch-1", + agentId: "orchestrator", + }, + "extraction-agent", + "project-1", + ); + + assertEquals(config?.availableSkillIds, ["extraction-agent--extract"]); + assertEquals(config?.toolNames, ["get_file", "load_skill"]); + assert(config?.system.includes("extraction-agent--extract")); + assertEquals(config?.system.includes("global-skill"), false); + + const childToolContext = veryfrontCloudAgentServiceInternals.buildHostedChildToolContext( + { + authToken: "token-1", + projectId: "project-1", + branchId: "branch-1", + agentId: "orchestrator", + }, + "extraction-agent", + config, + ); + const hostTools = veryfrontCloudAgentServiceInternals.buildHostedChildGlobalTools( + context, + { + childAgentId: "extraction-agent", + childConfig: config, + childToolContext, + }, + ); + + assertEquals("get_file" in hostTools, true); + assertEquals("load_skill" in hostTools, true); + } finally { + toolRegistry.clearAll(); + } +}); + Deno.test({ name: "createNodeVeryfrontCloudAgentServiceRuntime uses veryfront.config.ts discovery paths", // Code primitive discovery invokes the esbuild-backed transpiler, which starts diff --git a/src/agent/hosted/veryfront-cloud-agent-service.ts b/src/agent/hosted/veryfront-cloud-agent-service.ts index 3c159b608e..13220fa408 100644 --- a/src/agent/hosted/veryfront-cloud-agent-service.ts +++ b/src/agent/hosted/veryfront-cloud-agent-service.ts @@ -29,6 +29,7 @@ import { initializeNodeVeryfrontCloudAgentServiceContext, } from "./cloud-agent-config.ts"; import { + buildHostedChildGlobalTools, buildHostedChildToolContext, getDiscoveredHostTools, resolveHostedChildAgentExecutionConfig, @@ -138,6 +139,7 @@ export { getDiscoveredHostTools }; /** Internal test seams for hosted project-agent materialization. */ export const veryfrontCloudAgentServiceInternals = { + buildHostedChildGlobalTools, buildHostedChildToolContext, resolveHostedDelegationBinding, resolveHostedChildAgentExecutionConfig, diff --git a/src/agent/project/context.ts b/src/agent/project/context.ts index c39daadf16..fab96e96c5 100644 --- a/src/agent/project/context.ts +++ b/src/agent/project/context.ts @@ -5,6 +5,7 @@ export interface MutableAgentProjectContext { runtimeTargetKind?: "main_branch" | "environment" | "preview_branch" | null; runtimeTargetEnvironmentId?: string | null; availableSkillIds?: string[]; + skillSelectorPolicy?: import("#veryfront/skill/selector.ts").ResolvedSkillSelectorPolicy; /** Per-run skill id -> discovered SKILL.md source path (owner-aware catalog). */ skillSourcePaths?: Readonly>; } @@ -23,6 +24,7 @@ export function applyAgentProjectContextChange( context.runtimeTargetKind = "main_branch"; context.runtimeTargetEnvironmentId = null; context.availableSkillIds = undefined; + delete context.skillSelectorPolicy; context.skillSourcePaths = undefined; return true; } diff --git a/src/agent/runtime/agent-markdown-adapter.test.ts b/src/agent/runtime/agent-markdown-adapter.test.ts index 72c0d81d1d..df6052d594 100644 --- a/src/agent/runtime/agent-markdown-adapter.test.ts +++ b/src/agent/runtime/agent-markdown-adapter.test.ts @@ -70,7 +70,7 @@ Deno.test("createRuntimeAgentFromMarkdownDefinition preserves delegates and MCP }]); }); -Deno.test("createRuntimeAgentFromMarkdownDefinition preserves an empty catalog and binds skill tools", async () => { +Deno.test("createRuntimeAgentFromMarkdownDefinition preserves explicit empty skills and hides skill tools", async () => { skillRegistry.clearAll(); registerSkill("global-howto", { id: "global-howto", @@ -86,11 +86,7 @@ Deno.test("createRuntimeAgentFromMarkdownDefinition preserves an empty catalog a skills: [], }); - assertEquals(runtimeAgent.config.tools, { - load_skill: true, - load_skill_reference: true, - execute_skill_script: true, - }); + assertEquals(runtimeAgent.config.tools, undefined); const system = getEffectiveAgentSystem(runtimeAgent); const prompt = typeof system === "function" ? await system() : system; assertEquals(prompt, "Work alone."); diff --git a/src/agent/runtime/agent-runtime-step.test.ts b/src/agent/runtime/agent-runtime-step.test.ts index 0141784404..7ef32072da 100644 --- a/src/agent/runtime/agent-runtime-step.test.ts +++ b/src/agent/runtime/agent-runtime-step.test.ts @@ -51,6 +51,36 @@ describe("agent/runtime-step", () => { assertStrictEquals(prepared.toolContext.abortSignal, trustedAbort.signal); }); + it("does not let runtime context shadow trusted allowed skill ids", async () => { + const prepared = await prepareAgentRuntimeStep({ + agentId: "agent_1", + activeSkillPolicy: undefined, + activeSkillToolAvailability: undefined, + allowedRemoteToolNames: undefined, + config: { model: "auto", system: "Base", tools: true } as AgentConfig, + forwardedRemoteToolDefinitions: undefined, + getAvailableTools: async (_toolsConfig, options) => { + assertEquals(options?.remoteToolContext?.allowedSkillIds, ["selected"]); + return []; + }, + isLocalModel: false, + messages: [], + mode: "generate", + remoteToolSources: undefined, + resolveRuntimeState: async () => ({ + systemPrompt: "Base", + context: { allowedSkillIds: ["excluded"], keep: true }, + }), + runtimeContext: { allowedSkillIds: ["selected"], keep: true }, + step: 0, + systemPrompt: "Base", + toolContextBase: undefined, + }); + + assertEquals(prepared.toolContext.allowedSkillIds, ["selected"]); + assertEquals(prepared.runtimeContext, { allowedSkillIds: ["selected"], keep: true }); + }); + it("resolves runtime state, merges tool context, and applies active skill policy", async () => { const messages: Message[] = [{ id: "msg_1", @@ -158,6 +188,32 @@ describe("agent/runtime-step", () => { }); }); + it("does not include skill tools for the explicit none selector", async () => { + const prepared = await prepareAgentRuntimeStep({ + agentId: "agent_1", + activeSkillPolicy: undefined, + activeSkillToolAvailability: undefined, + allowedRemoteToolNames: undefined, + config: { model: "auto", system: "Base", tools: true, skills: [] } as AgentConfig, + forwardedRemoteToolDefinitions: undefined, + getAvailableTools: async (_toolsConfig, options) => { + assertEquals(options?.includeSkillTools, false); + return [toolDefinition("ordinary_tool")]; + }, + isLocalModel: false, + messages: [], + mode: "stream", + remoteToolSources: [], + runtimeContext: undefined, + step: 0, + systemPrompt: "Base", + toolContextBase: undefined, + resolveRuntimeState: async () => ({ systemPrompt: "Base", context: undefined }), + }); + + assertEquals(prepared.tools.map((tool) => tool.name), ["ordinary_tool"]); + }); + it("stamps the validated source policy into child-visible tool context", async () => { const sourceIntegrationPolicy = { schemaVersion: 1 as const, diff --git a/src/agent/runtime/agent-runtime-step.ts b/src/agent/runtime/agent-runtime-step.ts index 9d24704259..f91deb2c5a 100644 --- a/src/agent/runtime/agent-runtime-step.ts +++ b/src/agent/runtime/agent-runtime-step.ts @@ -65,10 +65,24 @@ export interface PreparedAgentRuntimeStep { tools: ToolDefinition[]; } +function shouldIncludeSkillTools(config: AgentConfig): boolean { + return config.skills !== false && (!Array.isArray(config.skills) || config.skills.length > 0); +} + +function getTrustedAllowedSkillIds( + input: PrepareAgentRuntimeStepInput, +): readonly string[] | undefined { + const value = input.toolContextBase?.allowedSkillIds ?? input.runtimeContext?.allowedSkillIds; + return Array.isArray(value) && value.every((entry): entry is string => typeof entry === "string") + ? value + : undefined; +} + /** Resolve per-step runtime state and the tools visible for that step. */ export async function prepareAgentRuntimeStep( input: PrepareAgentRuntimeStepInput, ): Promise { + const trustedAllowedSkillIds = getTrustedAllowedSkillIds(input); const runtimeState = await input.resolveRuntimeState( input.messages, input.runtimeContext, @@ -80,6 +94,9 @@ export async function prepareAgentRuntimeStep( if (input.toolContextBase?.abortSignal !== undefined) { toolContext.abortSignal = input.toolContextBase.abortSignal; } + if (trustedAllowedSkillIds !== undefined) { + toolContext.allowedSkillIds = [...trustedAllowedSkillIds]; + } delete toolContext[SOURCE_INTEGRATION_POLICY_CONTEXT_KEY]; if (input.sourceIntegrationPolicy !== undefined) { toolContext[SOURCE_INTEGRATION_POLICY_CONTEXT_KEY] = input.sourceIntegrationPolicy; @@ -93,7 +110,7 @@ export async function prepareAgentRuntimeStep( let tools = input.isLocalModel ? [] : await input.getAvailableTools(input.config.tools, { callerAgentId: input.agentId, - includeSkillTools: true, + includeSkillTools: shouldIncludeSkillTools(input.config), allowedRemoteToolNames: input.allowedRemoteToolNames, forwardedRemoteToolDefinitions: input.forwardedRemoteToolDefinitions, remoteToolSources: input.remoteToolSources, @@ -112,7 +129,9 @@ export async function prepareAgentRuntimeStep( tools = filterToolsAfterSubmittedFormInput(tools, input.messages, runtimeState.context); return { - runtimeContext: runtimeState.context, + runtimeContext: trustedAllowedSkillIds === undefined + ? runtimeState.context + : { ...runtimeState.context, allowedSkillIds: [...trustedAllowedSkillIds] }, systemPrompt: runtimeState.systemPrompt, toolContext, tools, diff --git a/src/agent/runtime/load-skill-tool.test.ts b/src/agent/runtime/load-skill-tool.test.ts index 694a1909dd..35598672d6 100644 --- a/src/agent/runtime/load-skill-tool.test.ts +++ b/src/agent/runtime/load-skill-tool.test.ts @@ -1067,6 +1067,53 @@ Deno.test("createRuntimeLoadSkillTool rejects invented skill IDs before tool exe ); }); +Deno.test("createRuntimeLoadSkillTool treats an empty availableSkillIds manifest as deny-all before storage reads", async () => { + let projectReads = 0; + let builtinReads = 0; + const tool = createRuntimeLoadSkillTool({ + context: createProjectContext({ + availableSkillIds: [], + }), + skillsDir: "/skills", + projectSkillLoader: { + listProjectSkillReferences: () => Promise.resolve([]), + loadProjectSkill: () => { + projectReads++; + return Promise.resolve({ instructions: "# Project plan", references: [] }); + }, + loadProjectSkillReference: () => Promise.resolve(null), + }, + builtinSkillIds: ["plan"], + builtinStore: { + readSkill: () => { + builtinReads++; + return "# Builtin plan"; + }, + readReferenceFile: () => { + builtinReads++; + return "Guide"; + }, + listReferences: () => { + builtinReads++; + return ["references/guide.md"]; + }, + }, + }); + + await assertRejects( + () => tool.execute({ skillId: "plan" }), + Error, + "input validation failed", + ); + await assertRejects( + () => tool.execute({ skillId: "plan", file: "references/guide.md" }), + Error, + "input validation failed", + ); + assertEquals(projectReads, 0); + assertEquals(builtinReads, 0); +}); + Deno.test("createRuntimeLoadSkillTool allows host copy overrides", async () => { const tool = createRuntimeLoadSkillTool({ context: createProjectContext(), diff --git a/src/agent/runtime/load-skill-tool.ts b/src/agent/runtime/load-skill-tool.ts index 537e1110c6..5f948fe8e1 100644 --- a/src/agent/runtime/load-skill-tool.ts +++ b/src/agent/runtime/load-skill-tool.ts @@ -27,6 +27,7 @@ import { type RuntimeLoadedSkillResponseMessages, type RuntimeSkillMetadataLogger, } from "./skill-metadata.ts"; +import type { ResolvedSkillSelectorPolicy } from "#veryfront/skill/selector.ts"; import { narrowPolicyAfterSubmittedForm } from "./skill-policy-enforcement.ts"; /** Legacy continuation-note fallback used when runtime tool inventory is unavailable. */ @@ -109,6 +110,7 @@ export type RuntimeLoadSkillToolContext = RuntimeProjectSkillContext & { /** Agent identity used to enforce owner-scoped skill visibility. */ agentId?: string; availableSkillIds?: readonly string[]; + skillSelectorPolicy?: ResolvedSkillSelectorPolicy; availableToolNames?: readonly string[]; loadedSkillResponses?: Record; loadedSkillReferenceResponses?: Record; @@ -254,10 +256,7 @@ function buildMissingSkillError( options: RuntimeLoadSkillToolOptions, skillId: string, ): RuntimeLoadSkillErrorOutput { - const knownIds = new Set([ - ...(options.context.availableSkillIds ?? []), - ...(options.builtinSkillIds ?? []), - ]); + const knownIds = new Set(getKnownRuntimeSkillIds(options) ?? []); const available = [...knownIds].sort().join(", "); return { error: `Skill not found: ${skillId}. Available skills: ${available}`, @@ -305,21 +304,22 @@ function buildRuntimeLoadSkillDescription(options: RuntimeLoadSkillToolOptions): return options.description; } - if (!options.context.availableSkillIds && !options.builtinSkillIds) { + if (options.context.availableSkillIds === undefined && !options.builtinSkillIds) { return RUNTIME_LOAD_SKILL_DESCRIPTION; } - const knownIds = new Set([ - ...(options.context.availableSkillIds ?? []), - ...(options.builtinSkillIds ?? []), - ]); + const knownIds = new Set(getKnownRuntimeSkillIds(options) ?? []); const available = [...knownIds].sort().join(", ") || "none"; return `${RUNTIME_LOAD_SKILL_DESCRIPTION} Available skill IDs: ${available}. Do not invent skill IDs. Only call load_skill with one of these IDs.`; } function getKnownRuntimeSkillIds(options: RuntimeLoadSkillToolOptions): string[] | null { - if (!options.context.availableSkillIds && !options.builtinSkillIds) { + if (options.context.availableSkillIds !== undefined) { + return [...new Set(options.context.availableSkillIds)].sort(); + } + + if (!options.builtinSkillIds) { return null; } @@ -385,10 +385,22 @@ function normalizeRuntimeLoadSkillInputSkillId( function buildRuntimeLoadSkillInputSchema(options: RuntimeLoadSkillToolOptions) { const knownIds = getKnownRuntimeSkillIds(options); - if (!knownIds || knownIds.length === 0) { + if (!knownIds) { return runtimeLoadSkillToolInputSchema; } + if (knownIds.length === 0) { + return defineSchema((v) => + v.object({ + skillId: v.string().refine( + () => false, + "No skills are available in this run.", + ).describe("No skills are available in this run."), + file: v.string().optional(), + }).strict() + )(); + } + const knownIdSet = new Set(knownIds); const loadedIds = getLoadedRuntimeSkillIds(options).filter((skillId) => knownIdSet.has(skillId)); const loadedIdSet = new Set(loadedIds); diff --git a/src/agent/runtime/skill-metadata.test.ts b/src/agent/runtime/skill-metadata.test.ts index 8642181571..fc4ba04623 100644 --- a/src/agent/runtime/skill-metadata.test.ts +++ b/src/agent/runtime/skill-metadata.test.ts @@ -5,6 +5,7 @@ import { buildRuntimeSkillDefinition, normalizeRuntimeSkillReferencePath, parseRuntimeSkillMetadata, + resolveRuntimeSkillSelectorForAgent, resolveRuntimeSkillsForAgent, } from "./skill-metadata.ts"; @@ -111,6 +112,167 @@ Deno.test("resolveRuntimeSkillsForAgent applies owner visibility and short-name ); }); +Deno.test("resolveRuntimeSkillSelectorForAgent returns a deterministic strict snapshot", () => { + const globalCite = buildRuntimeSkillDefinition({ + id: "cite", + content: "---\ndescription: Global citations\n---\nUse global citations.", + sourcePath: "skills/cite/SKILL.md", + })!; + const ownedCite = buildRuntimeSkillDefinition({ + id: "researcher--helper", + content: "---\ndescription: Research citations\n---\nUse research citations.", + ownerAgentId: "researcher", + shortName: "cite", + sourcePath: "agents/researcher/skills/cite/SKILL.md", + })!; + const otherOwned = buildRuntimeSkillDefinition({ + id: "writer--style", + content: "---\ndescription: Writer style\n---\nUse writer style.", + ownerAgentId: "writer", + shortName: "style", + sourcePath: "agents/writer/skills/style/SKILL.md", + })!; + + const selected = resolveRuntimeSkillSelectorForAgent({ + skills: [globalCite, ownedCite, otherOwned], + agentId: "researcher", + selector: ["cite", "cite"], + }); + + assertEquals(selected.policy, { kind: "allowlist", entries: ["cite", "cite"] }); + assertEquals(selected.allowedSkillIds, ["researcher--helper"]); + assertEquals(selected.skillSourcePaths, { + "researcher--helper": "agents/researcher/skills/cite/SKILL.md", + }); + assertEquals(selected.definitions.map((skill) => skill.id), ["researcher--helper"]); + + const none = resolveRuntimeSkillSelectorForAgent({ + skills: [globalCite, ownedCite, otherOwned], + agentId: "researcher", + selector: [], + }); + assertEquals(none.policy, { kind: "none" }); + assertEquals(none.allowedSkillIds, []); +}); + +Deno.test("resolveRuntimeSkillSelectorForAgent rejects unresolved explicit entries generically", () => { + const otherOwned = buildRuntimeSkillDefinition({ + id: "writer--style", + content: "---\ndescription: Writer style\n---\nUse writer style.", + ownerAgentId: "writer", + shortName: "style", + })!; + + let rejected = false; + try { + resolveRuntimeSkillSelectorForAgent({ + skills: [otherOwned], + agentId: "researcher", + selector: ["writer--style"], + }); + } catch (error) { + rejected = true; + const message = String(error); + assertEquals(message.includes("configured skills are not available"), true); + assertEquals(message.includes("writer--style"), false); + } + assertEquals(rejected, true); +}); + +Deno.test("resolveRuntimeSkillSelectorForAgent matches the canonical selector matrix", () => { + const global = buildRuntimeSkillDefinition({ + id: "global", + content: "---\ndescription: Global\n---\nGlobal.", + sourcePath: "skills/global/SKILL.md", + })!; + const bundled = buildRuntimeSkillDefinition({ + id: "bundled", + content: "---\ndescription: Bundled\n---\nBundled.", + sourcePath: "bundled/skills/bundled/SKILL.md", + })!; + const ownCite = buildRuntimeSkillDefinition({ + id: "agent--cite", + content: "---\ndescription: Own cite\n---\nCite.", + ownerAgentId: "agent", + shortName: "cite", + sourcePath: "agents/agent/skills/cite/SKILL.md", + })!; + const otherStyle = buildRuntimeSkillDefinition({ + id: "other--style", + content: "---\ndescription: Other style\n---\nStyle.", + ownerAgentId: "other", + shortName: "style", + sourcePath: "agents/other/skills/style/SKILL.md", + })!; + const globalCite = buildRuntimeSkillDefinition({ + id: "cite", + content: "---\ndescription: Global cite\n---\nGlobal cite.", + sourcePath: "skills/cite/SKILL.md", + })!; + + const skills = [global, bundled, ownCite, otherStyle, globalCite]; + const cases: Array<{ + selector: true | string[] | undefined; + expectedPolicy: object; + expectedIds: string[]; + }> = [ + { + selector: undefined, + expectedPolicy: { kind: "all-visible", source: "omitted" }, + expectedIds: ["global", "bundled", "agent--cite", "cite"], + }, + { + selector: true, + expectedPolicy: { kind: "all-visible", source: "true" }, + expectedIds: ["global", "bundled", "agent--cite", "cite"], + }, + { + selector: [], + expectedPolicy: { kind: "none" }, + expectedIds: [], + }, + { + selector: ["bundled", "cite", "global", "bundled"], + expectedPolicy: { kind: "allowlist", entries: ["bundled", "cite", "global", "bundled"] }, + expectedIds: ["bundled", "agent--cite", "global"], + }, + ]; + + for (const testCase of cases) { + const snapshot = resolveRuntimeSkillSelectorForAgent({ + skills, + agentId: "agent", + selector: testCase.selector, + }); + assertEquals(snapshot.policy, testCase.expectedPolicy); + assertEquals(snapshot.allowedSkillIds, testCase.expectedIds); + assertEquals(snapshot.definitions.map((skill) => skill.id), testCase.expectedIds); + } +}); + +Deno.test("resolveRuntimeSkillSelectorForAgent keeps the first visible duplicate id once", () => { + const projectSkill = buildRuntimeSkillDefinition({ + id: "create", + content: "---\ndescription: Project create\n---\nProject.", + sourcePath: "skills/create/SKILL.md", + })!; + const bundledSkill = buildRuntimeSkillDefinition({ + id: "create", + content: "---\ndescription: Bundled create\n---\nBundled.", + sourcePath: "bundled/skills/create/SKILL.md", + })!; + + const snapshot = resolveRuntimeSkillSelectorForAgent({ + skills: [projectSkill, bundledSkill], + agentId: "agent", + selector: ["create", "create"], + }); + + assertEquals(snapshot.allowedSkillIds, ["create"]); + assertEquals(snapshot.definitions[0], projectSkill); + assertEquals(snapshot.skillSourcePaths, { create: "skills/create/SKILL.md" }); +}); + Deno.test("buildRuntimeSkillDefinition includes optional runtime fields", () => { const content = `--- name: Skill diff --git a/src/agent/runtime/skill-metadata.ts b/src/agent/runtime/skill-metadata.ts index 04985302e3..8d46bc12c2 100644 --- a/src/agent/runtime/skill-metadata.ts +++ b/src/agent/runtime/skill-metadata.ts @@ -1,5 +1,10 @@ import { extract } from "#std/front-matter/yaml.ts"; import { defineSchema, lazySchema } from "#veryfront/schemas/index.ts"; +import { + assertResolvedSkillSelector, + type ResolvedSkillSelectorSnapshot, + resolveSkillSelector, +} from "#veryfront/skill/selector.ts"; function normalizeAllowedTools(value: string | string[] | undefined): string[] { if (value === undefined) { @@ -136,6 +141,34 @@ export function resolveRuntimeSkillsForAgent(input: { return [...selectedSkills.values()]; } +/** Resolve a presence-aware runtime skill selector snapshot without throwing on explicit misses. */ +export function resolveRuntimeSkillSelectorSnapshotForAgent(input: { + skills: readonly RuntimeSkillDefinition[]; + agentId: string; + selector: true | string[] | undefined; +}): ResolvedSkillSelectorSnapshot { + return resolveSkillSelector({ + definitions: input.skills, + selector: input.selector, + getId: (skill) => skill.id, + isVisible: (skill) => isRuntimeSkillVisibleTo(skill, { agentId: input.agentId }), + getShortName: (skill) => skill.shortName, + isOwnShortNameCandidate: (skill) => skill.ownerAgentId === input.agentId, + getSourcePath: (skill) => skill.sourcePath, + }); +} + +/** Resolve a presence-aware runtime skill selector snapshot and reject explicit misses. */ +export function resolveRuntimeSkillSelectorForAgent(input: { + skills: readonly RuntimeSkillDefinition[]; + agentId: string; + selector: true | string[] | undefined; +}): ResolvedSkillSelectorSnapshot { + const snapshot = resolveRuntimeSkillSelectorSnapshotForAgent(input); + assertResolvedSkillSelector(snapshot); + return snapshot; +} + /** Public API contract for runtime loaded skill response messages. */ export type RuntimeLoadedSkillResponseMessages = { allowedToolsNote: string; diff --git a/src/agent/service/runtime.test.ts b/src/agent/service/runtime.test.ts index eda81f2703..a940170c33 100644 --- a/src/agent/service/runtime.test.ts +++ b/src/agent/service/runtime.test.ts @@ -1,5 +1,6 @@ -import { assertEquals, assertRejects } from "#veryfront/testing/assert.ts"; +import { assert, assertEquals, assertRejects } from "#veryfront/testing/assert.ts"; import { describe, it } from "#veryfront/testing/bdd.ts"; +import { registerSkill, skillRegistry } from "#veryfront/skill/registry.ts"; import { combineAgentServiceLifecycle, createAgentServiceRuntime, @@ -83,6 +84,13 @@ describe("agent/agent-service-runtime", () => { }); it("preserves configured skills and tools on the service agent", () => { + skillRegistry.clearAll(); + registerSkill("support-triage", { + id: "support-triage", + metadata: { name: "support-triage", description: "Triage support requests" }, + rootPath: "/test/skills/support-triage", + }); + const bundle = createAgentServiceRuntime({ serviceName: "test-agent-service", getConfig: () => ({ @@ -108,13 +116,13 @@ describe("agent/agent-service-runtime", () => { const serviceAgent = bundle.runtime.contract.agents.assistant; assertEquals(serviceAgent?.config.skills, ["support-triage"]); - assertEquals(serviceAgent?.config.tools, { - search_knowledge: true, - get_file: true, - load_skill: true, - load_skill_reference: true, - execute_skill_script: true, - }); + const tools = serviceAgent?.config.tools; + assert(tools && tools !== true); + assertEquals(tools?.search_knowledge, true); + assertEquals(tools?.get_file, true); + assertEquals(typeof tools?.load_skill, "object"); + assertEquals(typeof tools?.load_skill_reference, "object"); + assertEquals(typeof tools?.execute_skill_script, "object"); }); it("starts the node agent service server from the assembled runtime", async () => { diff --git a/src/server/handlers/request/agent-stream.handler.test.ts b/src/server/handlers/request/agent-stream.handler.test.ts index 73681cb973..acb411339c 100644 --- a/src/server/handlers/request/agent-stream.handler.test.ts +++ b/src/server/handlers/request/agent-stream.handler.test.ts @@ -731,9 +731,9 @@ describe("server/handlers/request/agent-stream.handler", () => { agentConfig: { id: "assistant-1", name: "Project Assistant", - description: "Uses project-scoped skills and tools.", + description: "Uses project-scoped tools with skills disabled.", instructions: "Use project-scoped instructions.", - skills: ["support-triage"], + skills: [], tools: ["search_knowledge", "get_file"], }, }); @@ -759,7 +759,7 @@ describe("server/handlers/request/agent-stream.handler", () => { ? await (capturedSystem as () => Promise)() : capturedSystem; assertStringIncludes(String(resolvedSystem), "Use project-scoped instructions."); - assertEquals(capturedSkills, ["support-triage"]); + assertEquals(capturedSkills, []); assertEquals((capturedTools as Record).search_knowledge, true); assertEquals((capturedTools as Record).get_file, true); assertEquals(capturedAllowedRemoteTools, ["get_file", "search_knowledge"]); diff --git a/src/server/handlers/request/api/project-discovery.test.ts b/src/server/handlers/request/api/project-discovery.test.ts index 1e6923d8a1..980fd9ba04 100644 --- a/src/server/handlers/request/api/project-discovery.test.ts +++ b/src/server/handlers/request/api/project-discovery.test.ts @@ -44,6 +44,19 @@ function createHandlerContext( } as HandlerContext; } +function assertConfiguredSkillInfrastructure( + tools: unknown, +): asserts tools is Record { + assertExists(tools); + if (tools === true || typeof tools !== "object") { + throw new Error("Expected a concrete agent tool map"); + } + const toolMap = tools as Record; + assertEquals(typeof toolMap.load_skill, "object"); + assertEquals(typeof toolMap.load_skill_reference, "object"); + assertEquals(typeof toolMap.execute_skill_script, "object"); +} + async function writeAgentFile( ctx: HandlerContext, agentId: string, @@ -835,12 +848,8 @@ describe( assertExists(discoveredAgent); assertEquals(toolRegistry.has("write-report"), true); assertEquals(toolRegistry.has("writeReport"), false); - assertEquals(discoveredAgent.config.tools, { - "write-report": true, - load_skill: true, - load_skill_reference: true, - execute_skill_script: true, - }); + assertConfiguredSkillInfrastructure(discoveredAgent.config.tools); + assertEquals(discoveredAgent.config.tools["write-report"], true); }); it("keeps explicit generated-looking tool ids available for request-time project-agent runs", async () => { @@ -890,12 +899,8 @@ describe( assertExists(discoveredAgent); assertEquals(toolRegistry.has("tool_2024_01"), true); assertEquals(toolRegistry.has("writeReport"), false); - assertEquals(discoveredAgent.config.tools, { - tool_2024_01: true, - load_skill: true, - load_skill_reference: true, - execute_skill_script: true, - }); + assertConfiguredSkillInfrastructure(discoveredAgent.config.tools); + assertEquals(discoveredAgent.config.tools.tool_2024_01, true); }); it("keeps object-spread overridden tool ids available for request-time project-agent runs", async () => { @@ -946,12 +951,8 @@ describe( assertExists(discoveredAgent); assertEquals(toolRegistry.has("my-tool"), true); assertEquals(toolRegistry.has("writeReport"), false); - assertEquals(discoveredAgent.config.tools, { - "my-tool": true, - load_skill: true, - load_skill_reference: true, - execute_skill_script: true, - }); + assertConfiguredSkillInfrastructure(discoveredAgent.config.tools); + assertEquals(discoveredAgent.config.tools["my-tool"], true); }); }, ); diff --git a/src/skill/registry.test.ts b/src/skill/registry.test.ts index eadb9c6af4..15a6523d7c 100644 --- a/src/skill/registry.test.ts +++ b/src/skill/registry.test.ts @@ -1,5 +1,5 @@ import "#veryfront/schemas/_test-setup.ts"; -import { assertEquals } from "#veryfront/testing/assert.ts"; +import { assertEquals, assertThrows } from "#veryfront/testing/assert.ts"; import { beforeEach, describe, it } from "#veryfront/testing/bdd.ts"; import { getAllSkills, getSkill, registerSkill, skillRegistry } from "./registry.ts"; import type { Skill } from "./types.ts"; @@ -12,6 +12,18 @@ function createTestSkill(id: string): Skill { }; } +function createScopedTestSkill(input: { + id: string; + ownerAgentId?: string; + shortName?: string; +}): Skill { + return { + ...createTestSkill(input.id), + ...(input.ownerAgentId === undefined ? {} : { ownerAgentId: input.ownerAgentId }), + ...(input.shortName === undefined ? {} : { shortName: input.shortName }), + }; +} + describe("src/skill/registry", () => { beforeEach(() => { skillRegistry.clearAll(); @@ -75,4 +87,102 @@ describe("src/skill/registry", () => { assertEquals(resolved.size, 0); }); }); + + describe("resolveSelectorForAgent", () => { + it("preserves omitted, true, empty, and allowlist selector policies", () => { + registerSkill("a", createTestSkill("a")); + registerSkill("b", createTestSkill("b")); + + const omitted = skillRegistry.resolveSelectorForAgent(undefined); + assertEquals(omitted.policy, { kind: "all-visible", source: "omitted" }); + assertEquals(omitted.allowedSkillIds, ["a", "b"]); + + const all = skillRegistry.resolveSelectorForAgent(true); + assertEquals(all.policy, { kind: "all-visible", source: "true" }); + assertEquals(all.allowedSkillIds, ["a", "b"]); + + const none = skillRegistry.resolveSelectorForAgent([]); + assertEquals(none.policy, { kind: "none" }); + assertEquals(none.allowedSkillIds, []); + + const selected = skillRegistry.resolveSelectorForAgent(["b"]); + assertEquals(selected.policy, { kind: "allowlist", entries: ["b"] }); + assertEquals(selected.allowedSkillIds, ["b"]); + }); + + it("deduplicates explicit selections in request order and exposes source paths", () => { + registerSkill("a", createTestSkill("a")); + registerSkill("b", createTestSkill("b")); + + const resolved = skillRegistry.resolveSelectorForAgent(["b", "a", "b"]); + assertEquals(resolved.allowedSkillIds, ["b", "a"]); + assertEquals(resolved.skillSourcePaths, { + b: "/test/skills/b/SKILL.md", + a: "/test/skills/a/SKILL.md", + }); + assertEquals(resolved.definitions.map((skill) => skill.id), ["b", "a"]); + }); + + it("rejects unresolved explicit entries without echoing requested ids", () => { + registerSkill("a", createTestSkill("a")); + + const error = assertThrows( + () => skillRegistry.resolveSelectorForAgent(["missing-skill"]), + Error, + "configured skills are not available", + ); + + assertEquals(String(error).includes("missing-skill"), false); + }); + + it("applies the canonical selector matrix for owner-visible skills", () => { + registerSkill("global", createScopedTestSkill({ id: "global" })); + registerSkill("bundled", createScopedTestSkill({ id: "bundled" })); + registerSkill( + "agent--cite", + createScopedTestSkill({ id: "agent--cite", ownerAgentId: "agent", shortName: "cite" }), + ); + registerSkill( + "other--style", + createScopedTestSkill({ id: "other--style", ownerAgentId: "other", shortName: "style" }), + ); + registerSkill("cite", createScopedTestSkill({ id: "cite" })); + + const cases: Array<{ + selector: true | string[] | undefined; + expectedPolicy: object; + expectedIds: string[]; + }> = [ + { + selector: undefined, + expectedPolicy: { kind: "all-visible", source: "omitted" }, + expectedIds: ["global", "bundled", "agent--cite", "cite"], + }, + { + selector: true, + expectedPolicy: { kind: "all-visible", source: "true" }, + expectedIds: ["global", "bundled", "agent--cite", "cite"], + }, + { + selector: [], + expectedPolicy: { kind: "none" }, + expectedIds: [], + }, + { + selector: ["bundled", "cite", "global", "bundled"], + expectedPolicy: { kind: "allowlist", entries: ["bundled", "cite", "global", "bundled"] }, + expectedIds: ["bundled", "agent--cite", "global"], + }, + ]; + + for (const testCase of cases) { + const snapshot = skillRegistry.resolveSelectorForAgent(testCase.selector, { + agentId: "agent", + }); + assertEquals(snapshot.policy, testCase.expectedPolicy); + assertEquals(snapshot.allowedSkillIds, testCase.expectedIds); + assertEquals(snapshot.definitions.map((skill) => skill.id), testCase.expectedIds); + } + }); + }); }); diff --git a/src/skill/registry.ts b/src/skill/registry.ts index 04474b20c3..60122f42ea 100644 --- a/src/skill/registry.ts +++ b/src/skill/registry.ts @@ -13,6 +13,11 @@ */ import type { Skill } from "./types.ts"; +import { + assertResolvedSkillSelector, + type ResolvedSkillSelectorSnapshot, + resolveSkillSelector, +} from "./selector.ts"; import { ScopedRegistryFacade } from "#veryfront/registry/scoped-registry-facade.ts"; import { ProjectScopedRegistryManager } from "#veryfront/registry/project-scoped-registry-manager.ts"; @@ -30,6 +35,31 @@ export function isSkillVisibleTo(skill: Skill, scope?: AgentCapabilityScope): bo } class SkillRegistryClass extends ScopedRegistryFacade { + /** + * Resolve a presence-aware, execution-facing skill selector snapshot. + * + * Omitted and `true` resolve to all visible skills, `[]` resolves to none, + * and explicit entries must resolve to this caller's own short names or + * exact visible ids. Explicit misses fail closed with a generic error. + */ + resolveSelectorForAgent( + skillsConfig: true | string[] | undefined, + scope?: AgentCapabilityScope, + ): ResolvedSkillSelectorSnapshot { + const snapshot = resolveSkillSelector({ + definitions: [...this.getAll().values()], + selector: skillsConfig, + getId: (skill) => skill.id, + isVisible: (skill) => isSkillVisibleTo(skill, scope), + getShortName: (skill) => skill.shortName, + isOwnShortNameCandidate: (skill) => + scope?.agentId !== undefined && skill.ownerAgentId === scope.agentId, + getSourcePath: (skill) => `${skill.rootPath}/SKILL.md`, + }); + assertResolvedSkillSelector(snapshot); + return snapshot; + } + /** * Resolve skills for an agent configuration. * diff --git a/src/skill/selector.ts b/src/skill/selector.ts new file mode 100644 index 0000000000..188795db01 --- /dev/null +++ b/src/skill/selector.ts @@ -0,0 +1,150 @@ +import { CONFIG_INVALID } from "#veryfront/errors"; + +/** Authored skill selector policy after preserving property presence. */ +export type ResolvedSkillSelectorPolicy = + | { kind: "all-visible"; source: "omitted" | "true" } + | { kind: "none" } + | { kind: "allowlist"; entries: string[] }; + +/** Sanitized unresolved explicit selector entry. */ +export type UnresolvedSkillSelectorEntry = { + index: number; +}; + +/** Deterministic resolved selector snapshot shared by skill catalog adapters. */ +export type ResolvedSkillSelectorSnapshot = { + policy: ResolvedSkillSelectorPolicy; + definitions: TDefinition[]; + allowedSkillIds: string[]; + skillSourcePaths: Record; + unresolvedEntries: UnresolvedSkillSelectorEntry[]; +}; + +/** Empty selector snapshot for explicit none policies. */ +export function createNoneSkillSelectorSnapshot( + policy: Extract = { kind: "none" }, +): ResolvedSkillSelectorSnapshot { + return { + policy, + definitions: [], + allowedSkillIds: [], + skillSourcePaths: {}, + unresolvedEntries: [], + }; +} + +type ResolveSkillSelectorInput = { + definitions: readonly TDefinition[]; + selector: true | readonly string[] | undefined; + getId: (definition: TDefinition) => string; + isVisible: (definition: TDefinition) => boolean; + getShortName?: (definition: TDefinition) => string | undefined; + isOwnShortNameCandidate?: (definition: TDefinition) => boolean; + getSourcePath?: (definition: TDefinition) => string | undefined; +}; + +const UNAVAILABLE_SKILLS_MESSAGE = + "One or more configured skills are not available to this agent. " + + "Update the skills selector to use visible skill IDs or this agent's own skill short names."; + +function buildSnapshot( + policy: ResolvedSkillSelectorPolicy, + definitions: TDefinition[], + getId: (definition: TDefinition) => string, + getSourcePath: ((definition: TDefinition) => string | undefined) | undefined, + unresolvedEntries: UnresolvedSkillSelectorEntry[], +): ResolvedSkillSelectorSnapshot { + return { + policy, + definitions, + allowedSkillIds: definitions.map((definition) => getId(definition)), + skillSourcePaths: Object.fromEntries( + definitions + .map((definition) => [getId(definition), getSourcePath?.(definition)] as const) + .filter((entry): entry is readonly [string, string] => entry[1] !== undefined), + ), + unresolvedEntries, + }; +} + +/** Resolve a presence-aware skill selector without throwing on explicit misses. */ +export function resolveSkillSelector( + input: ResolveSkillSelectorInput, +): ResolvedSkillSelectorSnapshot { + const visibleDefinitions = input.definitions.filter(input.isVisible); + + if (input.selector === undefined || input.selector === true) { + return buildSnapshot( + { + kind: "all-visible", + source: input.selector === true ? "true" : "omitted", + }, + visibleDefinitions, + input.getId, + input.getSourcePath, + [], + ); + } + + if (input.selector.length === 0) { + return createNoneSkillSelectorSnapshot(); + } + + const byId = new Map(); + const byOwnShortName = new Map(); + + for (const definition of visibleDefinitions) { + const id = input.getId(definition); + if (!byId.has(id)) { + byId.set(id, definition); + } + + const shortName = input.getShortName?.(definition); + if ( + shortName !== undefined && + (input.isOwnShortNameCandidate?.(definition) ?? true) && + !byOwnShortName.has(shortName) + ) { + byOwnShortName.set(shortName, definition); + } + } + + const selectedDefinitions: TDefinition[] = []; + const selectedIds = new Set(); + const unresolvedEntries: UnresolvedSkillSelectorEntry[] = []; + + input.selector.forEach((requested, index) => { + const definition = byOwnShortName.get(requested) ?? byId.get(requested); + if (!definition) { + unresolvedEntries.push({ index }); + return; + } + + const id = input.getId(definition); + if (!selectedIds.has(id)) { + selectedIds.add(id); + selectedDefinitions.push(definition); + } + }); + + return buildSnapshot( + { kind: "allowlist", entries: [...input.selector] }, + selectedDefinitions, + input.getId, + input.getSourcePath, + unresolvedEntries, + ); +} + +/** Throw the generic selector configuration error for unresolved explicit entries. */ +export function assertResolvedSkillSelector( + snapshot: ResolvedSkillSelectorSnapshot, +): void { + if (snapshot.unresolvedEntries.length === 0) { + return; + } + + throw CONFIG_INVALID.create({ + detail: UNAVAILABLE_SKILLS_MESSAGE, + }); +} diff --git a/src/skill/skill-owner-scope.test.ts b/src/skill/skill-owner-scope.test.ts index 47addbcfd9..043968812c 100644 --- a/src/skill/skill-owner-scope.test.ts +++ b/src/skill/skill-owner-scope.test.ts @@ -7,7 +7,7 @@ */ import "#veryfront/schemas/_test-setup.ts"; -import { assertEquals, assertRejects } from "#veryfront/testing/assert.ts"; +import { assertEquals, assertRejects, assertThrows } from "#veryfront/testing/assert.ts"; import { registerSkill, skillRegistry } from "./registry.ts"; import { createExecuteSkillScriptTool, @@ -93,6 +93,37 @@ Deno.test("explicit selector cannot reach another agent's owned skill by full id } }); +Deno.test("strict explicit selector rejects another agent's owned skill without owner-id leakage", () => { + setupRegistry(); + try { + const error = assertThrows( + () => skillRegistry.resolveSelectorForAgent(["researcher--cite"], { agentId: "writer" }), + Error, + "configured skills are not available", + ); + const message = String(error); + assertEquals(message.includes("researcher--cite"), false); + assertEquals(message.includes("writer--style"), false); + } finally { + skillRegistry.clearAll(); + } +}); + +Deno.test("strict explicit selector resolves own short name before exact visible id", () => { + setupRegistry(); + try { + registerSkill("cite", makeSkill({ id: "cite" })); + + const resolved = skillRegistry.resolveSelectorForAgent(["cite", "global-howto", "cite"], { + agentId: "researcher", + }); + + assertEquals(resolved.allowedSkillIds, ["researcher--cite", "global-howto"]); + } finally { + skillRegistry.clearAll(); + } +}); + Deno.test("getVisibleSkillIds excludes other agents' owned skills", () => { setupRegistry(); try { diff --git a/src/skill/tools.test.ts b/src/skill/tools.test.ts index eb3c9432ce..fbec79b7e2 100644 --- a/src/skill/tools.test.ts +++ b/src/skill/tools.test.ts @@ -113,6 +113,39 @@ Review the asset files.`, assertEquals(result.references, ["assets/checklist.txt"]); }); + it("load_skill should reject skills outside the selector before reading storage", async () => { + let readCount = 0; + const fsAdapter = createSkillTestAdapter({ + "/project/skills/my-skill/SKILL.md": `--- +name: my-skill +description: Skill from adapter +--- +# Instructions +Do work.`, + }); + const countingAdapter: FileSystemAdapter = { + ...fsAdapter, + async readFile(path) { + readCount++; + return await fsAdapter.readFile(path); + }, + }; + registerSkill("my-skill", createTestSkill(countingAdapter)); + + const tool = createLoadSkillTool(); + + await assertRejects( + () => + tool.execute({ skillId: "my-skill" }, { + agentId: "agent", + allowedSkillIds: [], + }), + Error, + "not available to this agent", + ); + assertEquals(readCount, 0); + }); + it("load_skill should omit prompt notes for unavailable file tools", async () => { const fsAdapter = createSkillTestAdapter({ "/project/skills/my-skill/SKILL.md": `--- @@ -291,6 +324,43 @@ Do work.`, ); }); + it("load_skill_reference should reject stale active skill state outside the selector", async () => { + let readCount = 0; + const fsAdapter = createSkillTestAdapter({ + "/project/skills/my-skill/references/guide.md": "Guide", + }); + const countingAdapter: FileSystemAdapter = { + ...fsAdapter, + async readFile(path) { + readCount++; + return await fsAdapter.readFile(path); + }, + }; + registerSkill("my-skill", createNamedTestSkill("my-skill", countingAdapter)); + + const tool = createLoadSkillReferenceTool(); + + await assertRejects( + () => + tool.execute({ + skillId: "my-skill", + reference: "references/guide.md", + }, { + agentId: "agent", + allowedSkillIds: [], + activeSkillId: "my-skill", + activeSkillToolAvailability: { + hasActiveSkill: true, + references: ["references/guide.md"], + scripts: [], + }, + }), + Error, + "not available to this agent", + ); + assertEquals(readCount, 0); + }); + it("execute_skill_script should run a local script from the skill directory", async () => { const tempDir = await Deno.makeTempDir({ prefix: "vf-skill-script-" }); diff --git a/src/skill/tools.ts b/src/skill/tools.ts index 659957f75e..db0c78a857 100644 --- a/src/skill/tools.ts +++ b/src/skill/tools.ts @@ -32,6 +32,9 @@ import { const MAX_SCRIPT_TIMEOUT_MS = 300_000; type SkillFileKind = "reference" | "script"; +type SkillSelectorToolOptions = { + resolveAllowedSkillIds?: (context: ToolExecutionContext | undefined) => readonly string[]; +}; const getLoadSkillInputSchema = defineSchema((v) => v.object({ @@ -89,6 +92,7 @@ async function readSkillFile(skill: Skill, path: string): Promise { function resolveVisibleSkillOrThrow( skillId: string, context: ToolExecutionContext | undefined, + options: SkillSelectorToolOptions = {}, ): Skill { const scope = { agentId: context?.agentId }; const skill = skillRegistry.resolveVisibleSkill(skillId, scope); @@ -101,9 +105,45 @@ function resolveVisibleSkillOrThrow( }), ); } + assertSkillAllowedBySelector(skill, context, options); return skill; } +function getSelectorAllowedSkillIds( + context: ToolExecutionContext | undefined, + options: SkillSelectorToolOptions, +): readonly string[] | undefined { + const resolved = options.resolveAllowedSkillIds?.(context); + if (resolved !== undefined) return resolved; + + const contextAllowed = context?.allowedSkillIds; + if ( + Array.isArray(contextAllowed) && + contextAllowed.every((entry): entry is string => typeof entry === "string") + ) { + return contextAllowed; + } + return undefined; +} + +function assertSkillAllowedBySelector( + skill: Skill, + context: ToolExecutionContext | undefined, + options: SkillSelectorToolOptions, +): void { + const allowedSkillIds = getSelectorAllowedSkillIds(context, options); + if (allowedSkillIds === undefined || allowedSkillIds.includes(skill.id)) { + return; + } + + throw toError( + createError({ + type: "agent", + message: `Skill "${skill.id}" is not available to this agent.`, + }), + ); +} + function hasRuntimeSkillBoundary( context: ToolExecutionContext | undefined, ): context is ToolExecutionContext { @@ -162,14 +202,14 @@ function assertActiveSkillFileAvailable( * Create the load_skill tool. * Loads a skill's full instructions, available references, and scripts. */ -export function createLoadSkillTool(): Tool { +export function createLoadSkillTool(options: SkillSelectorToolOptions = {}): Tool { return tool({ id: "load_skill", description: "Load a skill's full instructions. Returns the skill's markdown instructions, " + "allowed tools policy, and lists of available reference files and scripts.", inputSchema: getLoadSkillInputSchema(), execute: async (input, context): Promise => { - const skill = resolveVisibleSkillOrThrow(input.skillId, context); + const skill = resolveVisibleSkillOrThrow(input.skillId, context, options); // Read SKILL.md const skillMdPath = join(skill.rootPath, SKILL_MD_FILENAME); @@ -218,14 +258,14 @@ export function createLoadSkillTool(): Tool { * Create the load_skill_reference tool. * Reads a reference file from a skill's references/, resources/, or assets/ directory. */ -export function createLoadSkillReferenceTool(): Tool { +export function createLoadSkillReferenceTool(options: SkillSelectorToolOptions = {}): Tool { return tool({ id: "load_skill_reference", description: "Read a reference file from a skill. Only files in the skill's " + "references/, resources/, and assets/ directories are accessible.", inputSchema: getLoadSkillReferenceInputSchema(), execute: async (input, context): Promise<{ content: string; path: string }> => { - const skill = resolveVisibleSkillOrThrow(input.skillId, context); + const skill = resolveVisibleSkillOrThrow(input.skillId, context, options); assertActiveSkillFileAvailable( { toolName: "load_skill_reference", @@ -256,7 +296,7 @@ export function createLoadSkillReferenceTool(): Tool { * Executes a script from a skill's scripts/ directory. */ export function createExecuteSkillScriptTool( - options: { executor?: SkillScriptExecutor } = {}, + options: { executor?: SkillScriptExecutor } & SkillSelectorToolOptions = {}, ): Tool { return tool({ id: "execute_skill_script", @@ -264,7 +304,7 @@ export function createExecuteSkillScriptTool( "Execute a script from a skill's scripts/ directory. Returns stdout, stderr, and exit code.", inputSchema: getExecuteSkillScriptInputSchema(), execute: async (input, context) => { - const skill = resolveVisibleSkillOrThrow(input.skillId, context); + const skill = resolveVisibleSkillOrThrow(input.skillId, context, options); assertActiveSkillFileAvailable( { toolName: "execute_skill_script", diff --git a/src/utils/version-constant.ts b/src/utils/version-constant.ts index 30773458a6..9027d1d83b 100644 --- a/src/utils/version-constant.ts +++ b/src/utils/version-constant.ts @@ -1,4 +1,4 @@ // Keep in sync with deno.json version. // scripts/release.ts updates this constant during releases. /** Shared version value. */ -export const VERSION = "0.1.1175"; +export const VERSION = "0.1.1176"; From d20c8d17f24adc8a279478a494b2155cfc4c00ff Mon Sep 17 00:00:00 2001 From: Kentaro Wakayama Date: Wed, 29 Jul 2026 16:46:58 +0200 Subject: [PATCH 2/8] Stabilize deploy polling test --- cli/mcp/tools/deploy-tool.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/mcp/tools/deploy-tool.test.ts b/cli/mcp/tools/deploy-tool.test.ts index 6fac60cdbd..6c6773520c 100644 --- a/cli/mcp/tools/deploy-tool.test.ts +++ b/cli/mcp/tools/deploy-tool.test.ts @@ -382,9 +382,10 @@ describe("mcp/tools/deploy-tool", () => { using time = new FakeTime(); resumeReleaseSourceRead(); await time.tickAsync(0); + // Parallel suites can need several microtask turns per fake timer. for ( let tick = 0; - releaseSourceReads < 20 && tick < 40; + releaseSourceReads < 20 && tick < 200; tick++ ) { await time.tickAsync(500); From f54b6c79a7b80d64bea31c9290211983f8a7b34f Mon Sep 17 00:00:00 2001 From: Kentaro Wakayama Date: Wed, 29 Jul 2026 17:54:40 +0200 Subject: [PATCH 3/8] Address skill selector review feedback --- src/agent/hosted/cloud-agent-child-tools.ts | 12 +-- .../default-project-steering-refresh.test.ts | 20 +++-- .../default-project-steering-refresh.ts | 2 + .../hosted/project-steering-adapter.test.ts | 17 ++-- src/agent/hosted/project-steering-adapter.ts | 2 + .../hosted/runtime-essential-tools.test.ts | 13 ++- src/agent/hosted/runtime-essential-tools.ts | 1 - .../veryfront-cloud-agent-service.test.ts | 82 +++++++++++++++++++ src/agent/project/context.test.ts | 2 + src/agent/project/context.ts | 1 - 10 files changed, 129 insertions(+), 23 deletions(-) diff --git a/src/agent/hosted/cloud-agent-child-tools.ts b/src/agent/hosted/cloud-agent-child-tools.ts index 52065e5566..8755117eb8 100644 --- a/src/agent/hosted/cloud-agent-child-tools.ts +++ b/src/agent/hosted/cloud-agent-child-tools.ts @@ -289,13 +289,13 @@ export function buildHostedChildToolContext( return { ...globalToolContext, agentId: childAgentId, - ...(childConfig?.availableSkillIds !== undefined - ? { availableSkillIds: childConfig.availableSkillIds } - : {}), - ...(childConfig?.skillSelectorPolicy - ? { skillSelectorPolicy: childConfig.skillSelectorPolicy } + ...(childConfig + ? { + availableSkillIds: childConfig.availableSkillIds, + skillSelectorPolicy: childConfig.skillSelectorPolicy, + skillSourcePaths: childConfig.skillSourcePaths, + } : {}), - ...(childConfig?.skillSourcePaths ? { skillSourcePaths: childConfig.skillSourcePaths } : {}), ...(childConfig?.toolNames ? { availableToolNames: childConfig.toolNames } : {}), loadedSkillResponses: {}, loadedSkillReferenceResponses: {}, diff --git a/src/agent/hosted/default-project-steering-refresh.test.ts b/src/agent/hosted/default-project-steering-refresh.test.ts index f3fe10bbda..29f544bc04 100644 --- a/src/agent/hosted/default-project-steering-refresh.test.ts +++ b/src/agent/hosted/default-project-steering-refresh.test.ts @@ -1,4 +1,4 @@ -import { assertEquals, assertStringIncludes } from "#veryfront/testing/assert.ts"; +import { assertEquals, assertRejects, assertStringIncludes } from "#veryfront/testing/assert.ts"; import { describe, it } from "#veryfront/testing/bdd.ts"; import { normalizeSourceIntegrationPolicy } from "#veryfront/integrations/source-policy.ts"; import type { RemoteToolSource } from "#veryfront/tool"; @@ -252,7 +252,7 @@ describe("agent/default-hosted-project-steering-refresh", () => { assertEquals(explicitInput.taskContext.availableSkillIds, ["build"]); }); - it("removes deleted explicit skill selections during refresh", async () => { + it("rejects deleted explicit skill selections during refresh without narrowing state", async () => { const refresh = createDefaultHostedProjectSteeringRefresh({ fetchProjectInstructions: () => Promise.resolve("Fresh instructions"), fetchSkills: () => Promise.resolve([createSkill("new-skill")]), @@ -264,12 +264,18 @@ describe("agent/default-hosted-project-steering-refresh", () => { input.taskContext.skillSelectorPolicy = { kind: "allowlist", entries: ["build"] }; input.taskContext.availableSkillIds = ["build"]; - const system = await refresh(input); + const error = await assertRejects( + () => refresh(input), + Error, + "configured skills are not available", + ); - assertStringIncludes(system, "Fresh instructions:"); - assertEquals(system.includes("build"), false); - assertEquals(system.includes("new-skill"), false); - assertEquals(input.taskContext.availableSkillIds, []); + assertEquals(String(error).includes("build"), false); + assertEquals(input.taskContext.availableSkillIds, ["build"]); + assertEquals(input.taskContext.skillSelectorPolicy, { + kind: "allowlist", + entries: ["build"], + }); }); it("keeps provider-native tools in refreshed runtime inventory", async () => { diff --git a/src/agent/hosted/default-project-steering-refresh.ts b/src/agent/hosted/default-project-steering-refresh.ts index 3796db0377..ef8cf120c1 100644 --- a/src/agent/hosted/default-project-steering-refresh.ts +++ b/src/agent/hosted/default-project-steering-refresh.ts @@ -18,6 +18,7 @@ import { selectProviderCompatibleToolNames } from "../runtime/provider-tool-comp import { flattenSystemInstructions, withRuntimeToolInventory } from "../runtime/tool-inventory.ts"; import type { HostedChatRuntimeInstructionsInput } from "./chat-preparation.ts"; import { + assertResolvedSkillSelector, createNoneSkillSelectorSnapshot, type ResolvedSkillSelectorPolicy, } from "#veryfront/skill/selector.ts"; @@ -251,6 +252,7 @@ export function createDefaultHostedProjectSteeringRefresh( policy: input.taskContext.skillSelectorPolicy ?? input.liveProjectSteering.skillSelectorPolicy, }); + assertResolvedSkillSelector(skillSelectorSnapshot); input.taskContext.availableSkillIds = skillSelectorSnapshot.allowedSkillIds; input.taskContext.skillSelectorPolicy = skillSelectorSnapshot.policy; input.taskContext.skillSourcePaths = diff --git a/src/agent/hosted/project-steering-adapter.test.ts b/src/agent/hosted/project-steering-adapter.test.ts index 7acbf05125..815c4bcf1d 100644 --- a/src/agent/hosted/project-steering-adapter.test.ts +++ b/src/agent/hosted/project-steering-adapter.test.ts @@ -1,5 +1,5 @@ import "#veryfront/schemas/_test-setup.ts"; -import { assert, assertEquals } from "#veryfront/testing/assert.ts"; +import { assert, assertEquals, assertRejects } from "#veryfront/testing/assert.ts"; import { join } from "node:path"; import { createHostedProjectSteeringAdapter, @@ -243,7 +243,7 @@ Body.`; }); }); -Deno.test("refreshProjectSkillIds re-resolves authored allowlist entries without broadening", async () => { +Deno.test("refreshProjectSkillIds rejects unresolved authored allowlist entries without narrowing", async () => { await withSkillsDir(async (skillsDir) => { const adapter = createHostedProjectSteeringAdapter({ apiUrl: "https://api.example.test", @@ -274,15 +274,18 @@ Body.`, skillSelectorPolicy: { kind: "allowlist" as const, entries: ["global", "deleted"] }, }; - await adapter.refreshProjectSkillIds(context); + const error = await assertRejects( + () => adapter.refreshProjectSkillIds(context), + Error, + "configured skills are not available", + ); - assertEquals(context.availableSkillIds, ["global"]); + assertEquals(String(error).includes("deleted"), false); + assertEquals(context.availableSkillIds, ["global", "new-skill"]); assertEquals(context.skillSelectorPolicy, { kind: "allowlist", entries: ["global", "deleted"], }); - assertEquals(context.skillSourcePaths, { - global: "skills/global/SKILL.md", - }); + assertEquals(context.skillSourcePaths, undefined); }); }); diff --git a/src/agent/hosted/project-steering-adapter.ts b/src/agent/hosted/project-steering-adapter.ts index 9aaa22786e..14b3622984 100644 --- a/src/agent/hosted/project-steering-adapter.ts +++ b/src/agent/hosted/project-steering-adapter.ts @@ -39,6 +39,7 @@ import type { } from "../runtime/skill-metadata.ts"; import { resolveRuntimeSkillSelectorSnapshotForAgent } from "../runtime/skill-metadata.ts"; import { + assertResolvedSkillSelector, createNoneSkillSelectorSnapshot, type ResolvedSkillSelectorPolicy, } from "#veryfront/skill/selector.ts"; @@ -222,6 +223,7 @@ export function createHostedProjectSteeringAdapter( }); const snapshot = resolveRefreshedSkillSnapshot({ skills, context }); + assertResolvedSkillSelector(snapshot); context.availableSkillIds = snapshot.allowedSkillIds; context.skillSelectorPolicy = snapshot.policy; context.skillSourcePaths = Object.keys(snapshot.skillSourcePaths).length > 0 diff --git a/src/agent/hosted/runtime-essential-tools.test.ts b/src/agent/hosted/runtime-essential-tools.test.ts index a25f3af395..1e2ee770b6 100644 --- a/src/agent/hosted/runtime-essential-tools.test.ts +++ b/src/agent/hosted/runtime-essential-tools.test.ts @@ -106,7 +106,7 @@ describe("resolveHostedRuntimeAllowedToolNames", () => { assertEquals(result?.has("load_skill_reference"), true); }); - it("removes execute_skill_script from model-visible tools for known empty skill manifests", () => { + it("removes only skill-loading and script infrastructure for known empty skill manifests", () => { const result = resolveHostedRuntimeAllowedToolNames({ allowedToolNames: new Set(["execute_skill_script", "sleep"]), localToolNames: ["execute_skill_script", "load_skill", "invoke_agent", "sleep"], @@ -118,5 +118,16 @@ describe("resolveHostedRuntimeAllowedToolNames", () => { assertEquals(result?.has("invoke_agent"), false); assertEquals(result?.has("sleep"), true); }); + + it("preserves explicitly allowed invoke_agent for known empty skill manifests", () => { + const result = resolveHostedRuntimeAllowedToolNames({ + allowedToolNames: new Set(["invoke_agent", "load_skill"]), + localToolNames: ["invoke_agent", "load_skill"], + availableSkillIds: [], + }); + + assertEquals(result?.has("invoke_agent"), true); + assertEquals(result?.has("load_skill"), false); + }); }); }); diff --git a/src/agent/hosted/runtime-essential-tools.ts b/src/agent/hosted/runtime-essential-tools.ts index 5cd1cbe245..4c15266039 100644 --- a/src/agent/hosted/runtime-essential-tools.ts +++ b/src/agent/hosted/runtime-essential-tools.ts @@ -18,7 +18,6 @@ const SKILL_DELEGATION_TOOL_NAMES = ["invoke_agent"] as const; const SKILL_SCRIPT_TOOL_NAMES = ["execute_skill_script"] as const; const EMPTY_SKILL_MANIFEST_TOOL_NAMES = [ ...SKILL_RUNTIME_TOOL_NAMES, - ...SKILL_DELEGATION_TOOL_NAMES, ...SKILL_SCRIPT_TOOL_NAMES, ] as const; diff --git a/src/agent/hosted/veryfront-cloud-agent-service.test.ts b/src/agent/hosted/veryfront-cloud-agent-service.test.ts index 1786535c6e..dce8043daa 100644 --- a/src/agent/hosted/veryfront-cloud-agent-service.test.ts +++ b/src/agent/hosted/veryfront-cloud-agent-service.test.ts @@ -288,6 +288,27 @@ Deno.test("hosted child project agents omit skill tools for an empty skill selec ); }); +Deno.test("hosted child project agents keep delegation tools for an empty skill selector snapshot", () => { + assertEquals( + veryfrontCloudAgentServiceInternals.resolveHostedChildToolNames({ + id: "extraction-agent", + name: "Extraction agent", + description: "Extract an application", + instructions: "Extract the application.", + skills: [], + tools: [ + "get_file", + "execute_skill_script", + "load_skill", + "load_skill_reference", + ], + providerTools: ["web_search"], + delegates: ["validation-agent"], + }, { allowedSkillIds: [] })?.toSorted(), + ["agent_validation-agent", "get_file", "web_search"], + ); +}); + Deno.test("hosted child project agents keep load_skill for a non-empty exact skill allowlist", () => { assertEquals( veryfrontCloudAgentServiceInternals.resolveHostedChildToolNames({ @@ -668,6 +689,67 @@ Deno.test("hosted nested delegates inherit child scope and durable lineage", () assertEquals(context.parentMessageId, "child-message"); }); +Deno.test("hosted nested delegates clear inherited skill catalog state for empty child selectors", () => { + const context = veryfrontCloudAgentServiceInternals.buildHostedChildToolContext( + { + authToken: "token-1", + projectId: "project-1", + branchId: "branch-1", + agentId: "orchestrator", + availableSkillIds: ["root-skill"], + skillSelectorPolicy: { kind: "allowlist", entries: ["root-skill"] }, + skillSourcePaths: { + "root-skill": "skills/root/SKILL.md", + }, + conversationId: "root-conversation", + parentRunId: "root-run", + parentMessageId: "root-message", + }, + "extraction-agent", + { + system: "Extract applications.", + toolNames: ["get_file", "agent_validation-agent"], + availableSkillIds: [], + skillSelectorPolicy: { kind: "none" }, + delegateIds: ["validation-agent"], + mcpServers: [], + }, + ); + + assertEquals(context.agentId, "extraction-agent"); + assertEquals(context.availableSkillIds, []); + assertEquals(context.skillSelectorPolicy, { kind: "none" }); + assertEquals(context.skillSourcePaths, undefined); +}); + +Deno.test("hosted generic delegates preserve inherited skill catalog state", () => { + const context = veryfrontCloudAgentServiceInternals.buildHostedChildToolContext( + { + authToken: "token-1", + projectId: "project-1", + branchId: "branch-1", + agentId: "orchestrator", + availableSkillIds: ["root-skill"], + skillSelectorPolicy: { kind: "allowlist", entries: ["root-skill"] }, + skillSourcePaths: { + "root-skill": "skills/root/SKILL.md", + }, + conversationId: "root-conversation", + parentRunId: "root-run", + parentMessageId: "root-message", + }, + "generic-agent", + undefined, + ); + + assertEquals(context.agentId, "generic-agent"); + assertEquals(context.availableSkillIds, ["root-skill"]); + assertEquals(context.skillSelectorPolicy, { kind: "allowlist", entries: ["root-skill"] }); + assertEquals(context.skillSourcePaths, { + "root-skill": "skills/root/SKILL.md", + }); +}); + Deno.test("hosted nested delegates preserve trusted root invocation context", () => { const context = veryfrontCloudAgentServiceInternals.buildHostedChildToolContext( { diff --git a/src/agent/project/context.test.ts b/src/agent/project/context.test.ts index f1c2fca962..724a81ad02 100644 --- a/src/agent/project/context.test.ts +++ b/src/agent/project/context.test.ts @@ -11,6 +11,7 @@ Deno.test("applyAgentProjectContextChange updates project and resets branch and projectId: "project-1", branchId: "branch-1", availableSkillIds: ["skill-a"], + skillSelectorPolicy: { kind: "allowlist", entries: ["skill-a"] }, skillSourcePaths: { "skill-a": "skills/skill-a/SKILL.md" }, steeringRevision: 3, }; @@ -24,6 +25,7 @@ Deno.test("applyAgentProjectContextChange updates project and resets branch and runtimeTargetKind: "main_branch", runtimeTargetEnvironmentId: null, availableSkillIds: undefined, + skillSelectorPolicy: { kind: "allowlist", entries: ["skill-a"] }, skillSourcePaths: undefined, steeringRevision: 3, }); diff --git a/src/agent/project/context.ts b/src/agent/project/context.ts index fab96e96c5..a023129872 100644 --- a/src/agent/project/context.ts +++ b/src/agent/project/context.ts @@ -24,7 +24,6 @@ export function applyAgentProjectContextChange( context.runtimeTargetKind = "main_branch"; context.runtimeTargetEnvironmentId = null; context.availableSkillIds = undefined; - delete context.skillSelectorPolicy; context.skillSourcePaths = undefined; return true; } From c555218d49bd8fcd4185833c9d9ccbb8aa8208ea Mon Sep 17 00:00:00 2001 From: Kentaro Wakayama Date: Wed, 29 Jul 2026 19:06:26 +0200 Subject: [PATCH 4/8] Reserve skill selector framework release --- deno.json | 2 +- src/utils/version-constant.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deno.json b/deno.json index e3e9abfaec..986247dcca 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "veryfront", - "version": "0.1.1176", + "version": "0.1.1177", "license": "Apache-2.0", "nodeModulesDir": "auto", "minimumDependencyAge": { diff --git a/src/utils/version-constant.ts b/src/utils/version-constant.ts index 9027d1d83b..76cfac2659 100644 --- a/src/utils/version-constant.ts +++ b/src/utils/version-constant.ts @@ -1,4 +1,4 @@ // Keep in sync with deno.json version. // scripts/release.ts updates this constant during releases. /** Shared version value. */ -export const VERSION = "0.1.1176"; +export const VERSION = "0.1.1177"; From c0def3e133f3cbe59bc9e14a82788abcf3197750 Mon Sep 17 00:00:00 2001 From: Kentaro Wakayama Date: Wed, 29 Jul 2026 19:30:10 +0200 Subject: [PATCH 5/8] Keep pull bootstrap tests release-aware --- cli/commands/pull/command.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/commands/pull/command.test.ts b/cli/commands/pull/command.test.ts index ec851e3beb..b3b82f84a0 100644 --- a/cli/commands/pull/command.test.ts +++ b/cli/commands/pull/command.test.ts @@ -11,7 +11,7 @@ import { assertThrows, } from "#veryfront/testing/assert.ts"; import { describe, it } from "#veryfront/testing/bdd.ts"; -import { cliLogger } from "#cli/utils"; +import { cliLogger, VERSION } from "#cli/utils"; import { _resetEnvironmentConfig } from "#veryfront/config/environment-config.ts"; import { buildFileContentUrl, @@ -117,7 +117,7 @@ function expectedBootstrapPackage(name: string): Record { dependencies: { react: "^19.2.4", "react-dom": "^19.2.4", - veryfront: "^0.1.1175", + veryfront: `^${VERSION}`, }, }; } @@ -153,7 +153,7 @@ const EXPECTED_BOOTSTRAP_PACKAGE = { dependencies: { react: "^19.2.4", "react-dom": "^19.2.4", - veryfront: "^0.1.1175", + veryfront: `^${VERSION}`, }, }; From 6a7e97b14d66b81af599148fac0089d2d812717b Mon Sep 17 00:00:00 2001 From: Koji Wakayama Date: Wed, 29 Jul 2026 19:44:38 +0200 Subject: [PATCH 6/8] Keep selector policy independent of release ownership The lifecycle release already has a dedicated version owner, so selector policy remains a feature-only change and publishes later from a dedicated release branch. Constraint: #3166 is the sole owner of version 0.1.1176 Constraint: Current main version is 0.1.1175 Rejected: Assign another version in this feature PR | release ownership would collide again as main advances Confidence: high Scope-risk: narrow Directive: Add release versions only in dedicated release pull requests Tested: selector-focused changed-file test suite, 192 passed / 230 steps Tested: version synchronization assertions and git diff check --- deno.json | 2 +- src/utils/version-constant.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deno.json b/deno.json index 986247dcca..ba977cc5df 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "veryfront", - "version": "0.1.1177", + "version": "0.1.1175", "license": "Apache-2.0", "nodeModulesDir": "auto", "minimumDependencyAge": { diff --git a/src/utils/version-constant.ts b/src/utils/version-constant.ts index 76cfac2659..30773458a6 100644 --- a/src/utils/version-constant.ts +++ b/src/utils/version-constant.ts @@ -1,4 +1,4 @@ // Keep in sync with deno.json version. // scripts/release.ts updates this constant during releases. /** Shared version value. */ -export const VERSION = "0.1.1177"; +export const VERSION = "0.1.1175"; From 0d59020e1c54529deaffedc057674a88c00a351d Mon Sep 17 00:00:00 2001 From: Koji Wakayama Date: Wed, 29 Jul 2026 21:00:29 +0200 Subject: [PATCH 7/8] Close selector authorization gaps before review approval Hosted allow-all tool resolution now treats a known empty skill manifest as an explicit filtered tool set, so dead skill-loading infrastructure is not advertised. Selector-scoped skill loading now uses a generic unavailable error before visible-skill enumeration can disclose selector-disallowed ids. The public AgentConfig.skills contract and generated agent reference now describe prompt discovery and load_skill authorization together. Constraint: Must address existing #3170 inline review findings without broadening the selector design Rejected: Add release-assets generated docs in this fix | unrelated baseline docs validation gap Rejected: Keep selector errors listing allowed skill ids | still discloses policy shape outside the requested skill Confidence: high Scope-risk: narrow Directive: Do not reintroduce skill-id-bearing unavailable errors for selector-denied load_skill calls Tested: RED/GREEN hosted empty-manifest and selector disclosure tests; focused hosted/skill suites; docs generation; fmt; lint; typecheck; unit suite Not-tested: scripts/hooks/pre-push remains blocked by pre-existing release-assets docs validation baseline --- docs/api-reference/veryfront/agent.md | 322 +++++++++--------- scripts/docs/generate-api-reference.ts | 2 +- .../hosted/runtime-essential-tools.test.ts | 22 ++ src/agent/hosted/runtime-essential-tools.ts | 24 +- src/agent/types.ts | 13 +- src/skill/tools.test.ts | 51 ++- src/skill/tools.ts | 27 +- 7 files changed, 274 insertions(+), 187 deletions(-) diff --git a/docs/api-reference/veryfront/agent.md b/docs/api-reference/veryfront/agent.md index 2d0febab4b..32fe699a74 100644 --- a/docs/api-reference/veryfront/agent.md +++ b/docs/api-reference/veryfront/agent.md @@ -157,9 +157,9 @@ Agent helper. | `resolveModelTransport?` | `ModelTransportResolver` | Optional request-aware hook for overriding the resolved model runtime and provider transport options on a per-call basis. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L212) | | `resolveRuntimeState?` | `RuntimeStateResolver` | Optional step-boundary hook for refreshing the runtime system prompt and host-owned context during a long-lived run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L217) | | `onToolResult?` | `ToolExecutionResultHandler` | Optional hook invoked after the runtime executes a configured local, registry, integration, or remote tool and before the tool result is persisted or streamed back to callers. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L223) | -| `skills?` | `true \| false \| string[]` | Select the skills advertised in this agent's system prompt. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L235) | -| `suggestions?` | `SuggestionsConfig` | Prompt starters shown on an empty chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L242) | -| `security?` | `false` | Set to false to disable the default security middleware | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L244) | +| `skills?` | `true \| false \| string[]` | Select the skills advertised in this agent's system prompt and authorized for `load_skill`. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L234) | +| `suggestions?` | `SuggestionsConfig` | Prompt starters shown on an empty chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L241) | +| `security?` | `false` | Set to false to disable the default security middleware | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L243) | **Returns:** `Agent` @@ -169,13 +169,13 @@ Run the agent and return a complete response. Accepts a string or message array | Property | Type | Description | Source | |----------|------|-------------|--------| -| `input` | `string \| Message[]` | Prompt string or message history | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L372) | -| `context?` | Record<string, unknown> | Additional context passed to the agent | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L373) | -| `model?` | `ModelString` | Override the agent's default model for this request. Must be in `allowedModels` if configured. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L375) | -| `maxOutputTokens?` | `number` | Override the maximum model output tokens for this request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L377) | -| `tools?` | `AgentGenerateToolReplacements` | Replace this agent's configured tools for this generate request only. When present, only these tools are advertised and executable. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L382) | -| `retainSkillLoaderTools?` | `boolean` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L386) | -| `abortSignal?` | `AbortSignal` | Abort signal for cooperative cancellation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L388) | +| `input` | `string \| Message[]` | Prompt string or message history | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L371) | +| `context?` | Record<string, unknown> | Additional context passed to the agent | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L372) | +| `model?` | `ModelString` | Override the agent's default model for this request. Must be in `allowedModels` if configured. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L374) | +| `maxOutputTokens?` | `number` | Override the maximum model output tokens for this request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L376) | +| `tools?` | `AgentGenerateToolReplacements` | Replace this agent's configured tools for this generate request only. When present, only these tools are advertised and executable. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L381) | +| `retainSkillLoaderTools?` | `boolean` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L385) | +| `abortSignal?` | `AbortSignal` | Abort signal for cooperative cancellation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L387) | **Returns:** Promise<AgentResponse> @@ -185,15 +185,15 @@ Run the agent and stream the response. Returns a result with `.toDataStreamRespo | Property | Type | Description | Source | |----------|------|-------------|--------| -| `input?` | `string` | Prompt string | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L392) | -| `messages?` | `Message[]` | Conversation message history | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L393) | -| `context?` | Record<string, unknown> | Additional context passed to the agent | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L394) | -| `model?` | `ModelString` | Override the agent's default model for this request. Must be in `allowedModels` if configured. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L396) | -| `maxOutputTokens?` | `number` | Override the maximum model output tokens for this request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L398) | -| `onToolCall?` | (toolCall: ToolCall) => void | Callback fired when a tool is invoked | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L399) | -| `onChunk?` | (chunk: string) => void | Callback fired for each text chunk | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L400) | -| `onFinish?` | (response: AgentResponse) => void | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L401) | -| `abortSignal?` | `AbortSignal` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L402) | +| `input?` | `string` | Prompt string | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L391) | +| `messages?` | `Message[]` | Conversation message history | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L392) | +| `context?` | Record<string, unknown> | Additional context passed to the agent | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L393) | +| `model?` | `ModelString` | Override the agent's default model for this request. Must be in `allowedModels` if configured. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L395) | +| `maxOutputTokens?` | `number` | Override the maximum model output tokens for this request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L397) | +| `onToolCall?` | (toolCall: ToolCall) => void | Callback fired when a tool is invoked | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L398) | +| `onChunk?` | (chunk: string) => void | Callback fired for each text chunk | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L399) | +| `onFinish?` | (response: AgentResponse) => void | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L400) | +| `abortSignal?` | `AbortSignal` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L401) | **Returns:** Promise<AgentStreamResult> @@ -285,8 +285,8 @@ Clear all stored messages from memory. | `PROJECT_AGENT_KINDS` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/identity-contracts.ts#L15) | | `PROJECT_STEERING_FILE_MUTATION_TOOL_NAMES` | Shared project steering file mutation tool names value. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/project/steering-mutation.ts#L8) | | `ROOT_OWNED_CHILD_RESULT_INSTRUCTION` | Shared root owned child result instruction value. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L42) | -| `RUNTIME_LOAD_SKILL_CONTINUATION_NOTE` | Legacy continuation-note fallback used when runtime tool inventory is unavailable. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L33) | -| `RUNTIME_LOAD_SKILL_DESCRIPTION` | Shared runtime load skill description value. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L37) | +| `RUNTIME_LOAD_SKILL_CONTINUATION_NOTE` | Legacy continuation-note fallback used when runtime tool inventory is unavailable. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L34) | +| `RUNTIME_LOAD_SKILL_DESCRIPTION` | Shared runtime load skill description value. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L38) | | `RuntimeAgentContextItemSchema` | Schema for runtime agent context item. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-invocation-contract.ts#L125) | | `RuntimeAgentIdSchema` | Schema for runtime agent ID. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-invocation-contract.ts#L58) | | `RuntimeAgentProjectContextSchema` | Schema for runtime agent project context. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-invocation-contract.ts#L217) | @@ -300,7 +300,7 @@ Clear all stored messages from memory. | `RuntimeAgentToolNameSchema` | Schema for runtime agent tool name. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-invocation-contract.ts#L70) | | `RuntimeAgentToolSchema` | Schema for runtime agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-invocation-contract.ts#L95) | | `RuntimeAgentValidatedClaimsSchema` | Schema for runtime agent validated claims. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-invocation-contract.ts#L231) | -| `RuntimeSkillFrontmatterSchema` | Schema for runtime skill frontmatter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L61) | +| `RuntimeSkillFrontmatterSchema` | Schema for runtime skill frontmatter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L66) | | `SLASH_COMMAND_ARTIFACT_REMINDER` | Shared slash command artifact reminder value. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L121) | | `SYNTHESIZE_DELEGATED_FINDINGS_IN_ROOT_VOICE` | Shared synthesize delegated findings in root voice value. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L15) | @@ -311,14 +311,14 @@ Clear all stored messages from memory. | `addFirstTurnStarterIntentRootOwnershipReminder` | Add first turn starter intent root ownership reminder helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L228) | | `addLoadSkillContinuationReminder` | Add load skill continuation reminder helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L292) | | `addSlashCommandArtifactReminder` | Add slash command artifact reminder helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L315) | -| `agent` | Agent helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/factory.ts#L99) | +| `agent` | Agent helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/factory.ts#L114) | | `agentAsTool` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/composition/composition.ts#L63) | | `appendAgentServiceChildMirrorChunk` | Append hosted child mirror chunk. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-mirror.ts#L180) | | `appendConversationRunEvents` | Append conversation run events. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/durable.ts#L988) | | `appendHostedChildMirrorChunk` | Append hosted child mirror chunk. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-mirror.ts#L180) | | `appendMissingChildRunToolCalls` | Append missing child run tool calls. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/child-run/final-step-support.ts#L15) | | `appendMissingChildRunToolResults` | Append missing child run tool results. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/child-run/final-step-support.ts#L31) | -| `applyAgentProjectContextChange` | Apply agent project context change helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/project/context.ts#L13) | +| `applyAgentProjectContextChange` | Apply agent project context change helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/project/context.ts#L14) | | `applyDefaultResearchArtifactPath` | Apply default research artifact path helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/artifacts/default-research-artifact-support.ts#L218) | | `applyPartToStreamedStepState` | State for apply part to streamed step. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/fork-runtime-step-state.ts#L75) | | `bootstrapAgentService` | Bootstrap agent service helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/bootstrap.ts#L64) | @@ -378,8 +378,8 @@ Clear all stored messages from memory. | `buildRootOwnedDelegatedFindingsInstruction` | Builds root owned delegated findings instruction. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L37) | | `buildRuntimeAgentControlPlaneStreamRequestFromInvocation` | Builds runtime agent control plane stream request from invocation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-invocation-contract.ts#L391) | | `buildRuntimeAvailableSkillsPromptBlock` | Builds runtime available skills prompt block. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-prompt.ts#L69) | -| `buildRuntimeLoadedSkillResponse` | Response payload for build runtime loaded skill. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L306) | -| `buildRuntimeSkillDefinition` | Definition for build runtime skill. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L255) | +| `buildRuntimeLoadedSkillResponse` | Response payload for build runtime loaded skill. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L339) | +| `buildRuntimeSkillDefinition` | Definition for build runtime skill. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L288) | | `buildScheduleTraceAttributes` | Builds schedule trigger trace attributes from schedule forwarded props. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/trace-attributes.ts#L76) | | `buildStarterIntentRootOwnershipBlockMessage` | Message shape for build starter intent root ownership block. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L113) | | `buildStarterIntentRootOwnershipReminder` | Builds starter intent root ownership reminder. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L108) | @@ -448,12 +448,12 @@ Clear all stored messages from memory. | `createConversationRunEventQueueController` | Create conversation run event queue controller. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/durable.ts#L693) | | `createConversationRunMirror` | Create conversation run mirror. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/run-mirror.ts#L83) | | `createConversationRunStreamMirror` | Create conversation run stream mirror. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/run-stream-mirror.ts#L24) | -| `createDefaultAgentServiceChatRuntime` | Create default hosted chat runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L344) | -| `createDefaultAgentServiceInvokeAgentTool` | Create default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L766) | -| `createDefaultAgentServiceProjectSteeringRefresh` | Create default hosted project steering refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L169) | -| `createDefaultHostedChatRuntime` | Create default hosted chat runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L344) | -| `createDefaultHostedInvokeAgentTool` | Create default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L766) | -| `createDefaultHostedProjectSteeringRefresh` | Create default hosted project steering refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L169) | +| `createDefaultAgentServiceChatRuntime` | Create default hosted chat runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L346) | +| `createDefaultAgentServiceInvokeAgentTool` | Create default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L767) | +| `createDefaultAgentServiceProjectSteeringRefresh` | Create default hosted project steering refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L214) | +| `createDefaultHostedChatRuntime` | Create default hosted chat runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L346) | +| `createDefaultHostedInvokeAgentTool` | Create default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L767) | +| `createDefaultHostedProjectSteeringRefresh` | Create default hosted project steering refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L214) | | `createDefaultResearchRunArtifactMirrorHandler` | Handler for create default research run artifact mirror. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/artifacts/default-research-artifact-support.ts#L362) | | `createDetachedRunShutdownLifecycle` | Create detached run shutdown lifecycle. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/detached-run-tracker.ts#L139) | | `createDetachedRunTracker` | Create detached run tracker. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/detached-run-tracker.ts#L56) | @@ -485,7 +485,7 @@ Clear all stored messages from memory. | `createHostedMirroredUiStream` | Create hosted mirrored UI stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/mirrored-tool-chunk-state.ts#L251) | | `createHostedProjectRemoteToolSource` | Create hosted project remote tool source. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-remote-tool-source.ts#L88) | | `createHostedProjectRemoteToolSources` | Create hosted project remote tool sources. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-remote-tool-source.ts#L358) | -| `createHostedProjectSteeringAdapter` | Create hosted project steering adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L138) | +| `createHostedProjectSteeringAdapter` | Create hosted project steering adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L169) | | `createHostedRootRunLifecycleRuntimeAdapter` | Create hosted root run lifecycle runtime adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/agent-run-lifecycle.ts#L162) | | `createHostedRuntimeStateResolver` | Create hosted runtime state resolver. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/runtime-state-resolver.ts#L72) | | `createHostedServiceAuth` | Create hosted service auth. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/auth.ts#L284) | @@ -495,13 +495,13 @@ Clear all stored messages from memory. | `createMemory` | Create memory. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/memory/memory.ts#L352) | | `createMirroredToolChunkState` | State for create mirrored tool chunk. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/mirrored-tool-chunk-state.ts#L40) | | `createNodeAgentServiceRuntimeInfrastructure` | Create node agent service runtime infrastructure. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-runtime-infrastructure.ts#L52) | -| `createNodeVeryfrontCloudAgentServiceRuntime` | Create node Veryfront Cloud agent service runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L190) | +| `createNodeVeryfrontCloudAgentServiceRuntime` | Create node Veryfront Cloud agent service runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L192) | | `createRedisMemory` | Create redis memory. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/memory/redis.ts#L249) | | `createRequestAuthCache` | Create request auth cache. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/request-auth-cache.ts#L19) | | `createRuntimeAgentDefinitionFromAgent` | Create runtime agent definition from agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/project/agent-runtime.ts#L169) | | `createRuntimeAgentFromMarkdownDefinition` | Definition for create runtime agent from markdown. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-markdown-adapter.ts#L8) | | `createRuntimeAgentSystemMessages` | Create runtime agent system messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-definition.ts#L256) | -| `createRuntimeLoadSkillTool` | Create runtime load skill tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L562) | +| `createRuntimeLoadSkillTool` | Create runtime load skill tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L574) | | `createRuntimeProjectFilesClient` | Create runtime project files client. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-files-client.ts#L104) | | `createRuntimeProjectSkillLoader` | Create runtime project skill loader. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-skill-loader.ts#L327) | | `createRuntimePromptBlock` | Create runtime prompt block. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/prompt-block.ts#L9) | @@ -529,8 +529,8 @@ Clear all stored messages from memory. | `evaluateSlashCommandArtifactPolicy` | Evaluate slash command artifact policy helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/artifacts/slash-command-artifact-policy.ts#L200) | | `evaluateStarterIntentTurnPolicy` | Evaluate starter intent turn policy helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L210) | | `executeAgUiDetachedStart` | Execute AG-UI detached start. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/ag-ui/detached-start.ts#L300) | -| `executeDefaultAgentServiceInvokeAgentTool` | Execute default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L565) | -| `executeDefaultHostedInvokeAgentTool` | Execute default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L565) | +| `executeDefaultAgentServiceInvokeAgentTool` | Execute default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L566) | +| `executeDefaultHostedInvokeAgentTool` | Execute default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L566) | | `executeDurableHumanInputFlow` | Execute durable human input flow. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/input/human-input.ts#L220) | | `executeHostedChildForkRunContextStream` | Execute hosted child fork run context stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-run-context.ts#L224) | | `executeHostedChildForkStream` | Execute hosted child fork stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-stream-execution.ts#L487) | @@ -545,8 +545,8 @@ Clear all stored messages from memory. | `extractLatestUserText` | Extract latest user text. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/artifacts/default-research-artifact-support.ts#L68) | | `extractStarterIntentId` | Extract starter intent ID. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/delegation-policy.ts#L192) | | `fetchConversationRecord` | Record shape for fetch conversation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/bootstrap.ts#L134) | -| `fetchDefaultAgentServiceProjectSteering` | Fetch default hosted project steering helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L69) | -| `fetchDefaultHostedProjectSteering` | Fetch default hosted project steering helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L69) | +| `fetchDefaultAgentServiceProjectSteering` | Fetch default hosted project steering helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L74) | +| `fetchDefaultHostedProjectSteering` | Fetch default hosted project steering helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L74) | | `fetchLatestConversationUserText` | Fetch latest conversation user text helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/artifacts/default-research-artifact-support.ts#L112) | | `filterAgentTraceAttributes` | Filter agent trace attributes. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/trace-attributes.ts#L61) | | `filterHostedChatRuntimeLocalTools` | Filter hosted chat runtime local tools. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-tool-assembly.ts#L187) | @@ -577,7 +577,7 @@ Clear all stored messages from memory. | `getAgUiSseStringField` | Return a string field from a parsed AG-UI SSE event record. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/ag-ui/sse-parser.ts#L61) | | `getAllAgentIds` | Return all agent IDs. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/composition/composition.ts#L226) | | `getChildRunSnapshotUsage` | Return child run snapshot usage. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/child-run/execution-snapshot.ts#L72) | -| `getConfirmedProjectContextSwitchId` | Return confirmed project context switch ID. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/project/context.ts#L58) | +| `getConfirmedProjectContextSwitchId` | Return confirmed project context switch ID. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/project/context.ts#L59) | | `getConversationRun` | Return conversation run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/durable.ts#L912) | | `getConversationRunEventJsonByteLength` | Return conversation run event JSON byte length. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/run-event-normalization.ts#L20) | | `getEmptyHostedFinalizedMessageTerminalError` | Error shape for get empty hosted finalized message terminal. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/stream-terminal-error.ts#L118) | @@ -600,13 +600,13 @@ Clear all stored messages from memory. | `getRuntimeProjectInstructions` | Return runtime project instructions. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-skill-catalog.ts#L114) | | `getRuntimeProjectSkillCatalog` | Return runtime project skill catalog. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-skill-catalog.ts#L134) | | `getRuntimeUploadUrl` | Return runtime upload URL. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/upload-url-client.ts#L38) | -| `getTextFromParts` | Return text from parts. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L328) | -| `getToolArguments` | Return tool arguments. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L346) | +| `getTextFromParts` | Return text from parts. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L327) | +| `getToolArguments` | Return tool arguments. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L345) | | `handleHostedChildForkFailure` | Process a hosted child fork failure. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-stream-execution.ts#L280) | | `handleHostedChildForkRunContextError` | Error shape for handle hosted child fork run context. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-run-context.ts#L267) | | `handleHostedChildForkStreamPart` | Process a hosted child fork stream part. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-stream-execution.ts#L318) | -| `hasArgs` | Check whether args is present. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L336) | -| `hasInput` | Input payload for has. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L341) | +| `hasArgs` | Check whether args is present. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L335) | +| `hasInput` | Input payload for has. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L340) | | `initializeNodeAgentServiceOpenTelemetry` | Initialize node agent service open telemetry. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L470) | | `initializeNodeHostedAgentServiceOpenTelemetry` | Initialize node hosted agent service open telemetry. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L416) | | `installAbortRejectionGuard` | Install abort rejection guard helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/abort-rejection-guard.ts#L114) | @@ -660,9 +660,9 @@ Clear all stored messages from memory. | `normalizeConversationRunEvents` | Normalizes conversation run events. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/run-event-normalization.ts#L82) | | `normalizeEncodedConversationRunEvents` | Normalizes encoded conversation run events. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/run-events.ts#L292) | | `normalizeHostedChildArtifactPath` | Normalizes hosted child artifact path. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-artifact-support.ts#L133) | -| `normalizeParsedAgentServiceChatRequest` | Request payload for normalize parsed hosted chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L262) | -| `normalizeParsedHostedChatRequest` | Request payload for normalize parsed hosted chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L262) | -| `normalizeRuntimeSkillReferencePath` | Normalizes runtime skill reference path. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L290) | +| `normalizeParsedAgentServiceChatRequest` | Request payload for normalize parsed hosted chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L263) | +| `normalizeParsedHostedChatRequest` | Request payload for normalize parsed hosted chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L263) | +| `normalizeRuntimeSkillReferencePath` | Normalizes runtime skill reference path. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L323) | | `parseAgentServiceChatRequestFromRequest` | Request payload for parse hosted chat request from. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L365) | | `parseAgentServiceConfig` | Configuration used by parse agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/config.ts#L150) | | `parseAgUiContextBoolean` | Parses AG-UI context boolean. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/ag-ui/forwarded-context.ts#L56) | @@ -684,15 +684,15 @@ Clear all stored messages from memory. | `parseRuntimeAgentRunInvocationAgentServiceChatRequestFromRequest` | Request payload for parse runtime agent run invocation hosted chat request from. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L394) | | `parseRuntimeAgentRunInvocationHostedChatRequestFromRequest` | Request payload for parse runtime agent run invocation hosted chat request from. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L394) | | `parseRuntimeAgentRunInvocationOrError` | Error shape for parse runtime agent run invocation or. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-invocation-contract.ts#L418) | -| `parseRuntimeSkillDocument` | Parses runtime skill document. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L219) | -| `parseRuntimeSkillMetadata` | Parses runtime skill metadata. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L247) | +| `parseRuntimeSkillDocument` | Parses runtime skill document. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L252) | +| `parseRuntimeSkillMetadata` | Parses runtime skill metadata. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L280) | | `parseToolInputObject` | Parses tool input object. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/tool-input.ts#L135) | | `persistConversationUserMessage` | Message shape for persist conversation user. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/bootstrap.ts#L211) | | `persistLatestConversationUserMessage` | Message shape for persist latest conversation user. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/bootstrap.ts#L277) | | `prepareAgentRuntimeMessagesFromUiMessages` | Prepare agent runtime messages from UI messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/message-preparation.ts#L34) | -| `prepareAgentServiceChatExecution` | Prepare hosted chat execution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L409) | -| `prepareAgentServiceChatRuntimeCreationOptions` | Options accepted by prepare hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L297) | -| `prepareAgentServiceChatRuntimeMessages` | Prepare hosted chat runtime messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L526) | +| `prepareAgentServiceChatExecution` | Prepare hosted chat execution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L403) | +| `prepareAgentServiceChatRuntimeCreationOptions` | Options accepted by prepare hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L300) | +| `prepareAgentServiceChatRuntimeMessages` | Prepare hosted chat runtime messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L520) | | `prepareAgentServiceConversationRootRunContext` | Context for prepare hosted conversation root run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/root-run-lifecycle.ts#L118) | | `prepareConversationRootRunContext` | Context for prepare conversation root run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/root-run-context.ts#L127) | | `prepareConversationRootRunLifecycle` | Prepare conversation root run lifecycle. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/root-run-lifecycle.ts#L38) | @@ -703,9 +703,9 @@ Clear all stored messages from memory. | `prepareDefaultHostedChildForkSandboxToolSources` | Prepare default hosted child fork sandbox tool sources. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-tool-sources.ts#L195) | | `prepareDefaultHostedChildForkToolAssembly` | Prepare default hosted child fork tool assembly. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-requested-tools.ts#L366) | | `prepareDefaultHostedChildForkToolSources` | Prepare default hosted child fork tool sources. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-tool-sources.ts#L87) | -| `prepareHostedChatExecution` | Prepare hosted chat execution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L409) | -| `prepareHostedChatRuntimeCreationOptions` | Options accepted by prepare hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L297) | -| `prepareHostedChatRuntimeMessages` | Prepare hosted chat runtime messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L526) | +| `prepareHostedChatExecution` | Prepare hosted chat execution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L403) | +| `prepareHostedChatRuntimeCreationOptions` | Options accepted by prepare hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L300) | +| `prepareHostedChatRuntimeMessages` | Prepare hosted chat runtime messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L520) | | `prepareHostedChatRuntimeToolAssembly` | Prepare hosted chat runtime tool assembly. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-tool-assembly.ts#L223) | | `prepareHostedChildForkRuntimeStepMessages` | Prepare hosted child fork runtime step messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-step-message-preparation.ts#L113) | | `prepareHostedConversationRootRunContext` | Context for prepare hosted conversation root run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/root-run-lifecycle.ts#L118) | @@ -774,7 +774,7 @@ Clear all stored messages from memory. | `snapshotHostedRuntimeSourceIdentity` | Capture a service-owned immutable copy of a declared runtime source. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/runtime-source-binding.ts#L18) | | `startAgentRuntimeFork` | Starts agent runtime fork. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/fork-runtime-stream.ts#L482) | | `startAgentRuntimeForkWithHostTools` | Starts agent runtime fork with host tools. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/fork-runtime-stream.ts#L159) | -| `startAgentService` | Starts agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L224) | +| `startAgentService` | Starts agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L226) | | `startAgentServiceRuntime` | Starts agent service runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/runtime.ts#L345) | | `startAgentServiceServer` | Starts agent service server. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/server.ts#L93) | | `startConversationRootRun` | Starts conversation root run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/root-run-context.ts#L62) | @@ -782,7 +782,7 @@ Clear all stored messages from memory. | `startNodeAgentService` | Starts node agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/runtime.ts#L310) | | `startNodeAgentServiceServer` | Starts node agent service server. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/server.ts#L76) | | `startNodeHostedAgentService` | Starts node hosted agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/runtime.ts#L335) | -| `startNodeVeryfrontCloudAgentService` | Starts node Veryfront Cloud agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L200) | +| `startNodeVeryfrontCloudAgentService` | Starts node Veryfront Cloud agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L202) | | `streamDataStreamEvents` | Stream data stream events helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/data-stream.ts#L49) | | `streamPreparedAgentServiceChatExecutionToAgUiResponse` | Response payload for stream prepared hosted chat execution to AG-UI. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/prepared-chat-execution.ts#L123) | | `streamPreparedHostedChatExecutionToAgUiResponse` | Response payload for stream prepared hosted chat execution to AG-UI. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/prepared-chat-execution.ts#L123) | @@ -800,8 +800,8 @@ Clear all stored messages from memory. | `updateDefaultResearchArtifacts` | Update default research artifacts helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/artifacts/default-research-artifact-support.ts#L166) | | `validateRuntimeAgentTargetSelection` | Validates runtime agent target selection. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-invocation-contract.ts#L166) | | `verifyHostedRuntimeSourceBinding` | Verify that a control-plane request addresses the exact source snapshot served here. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/runtime-source-binding.ts#L25) | -| `veryfrontApiMcpServer` | Veryfront API MCP server helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L68) | -| `veryfrontStudioMcpServer` | Veryfront Studio MCP server helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L75) | +| `veryfrontApiMcpServer` | Veryfront API MCP server helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L69) | +| `veryfrontStudioMcpServer` | Veryfront Studio MCP server helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L76) | | `waitForDurableHumanInputResolution` | Wait for durable human input resolution helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/input/human-input.ts#L307) | | `waitForHumanInput` | Input payload for wait for human. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/input/human-input.ts#L281) | | `withDefaultResearchArtifactPath` | Applies default research artifact path. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/artifacts/default-research-artifact-policy.ts#L165) | @@ -849,7 +849,7 @@ Clear all stored messages from memory. | `AbortRejectionGuardLogger` | Public API contract for abort rejection guard logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/abort-rejection-guard.ts#L2) | | `AbortRejectionProcessTarget` | Public API contract for abort rejection process target. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/abort-rejection-guard.ts#L7) | | `ActiveConversationRunStatus` | Public API contract for a conversation run status is active. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/durable-contracts.ts#L164) | -| `Agent` | Public API contract for agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L367) | +| `Agent` | Public API contract for agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L366) | | `AgentCatalogAction` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/identity-contracts.ts#L13) | | `AgentCatalogKind` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/identity-contracts.ts#L6) | | `AgentConfig` | Configuration used by agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L142) | @@ -861,7 +861,7 @@ Clear all stored messages from memory. | `AgentMcpServerConfig` | MCP server available to an agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L139) | | `AgentMcpToolPolicy` | Policy for tools exposed by one MCP server. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L95) | | `AgentMessage` | Message exchanged with an agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/schemas/agent.schema.ts#L254) | -| `AgentMiddleware` | Public API contract for agent middleware. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L321) | +| `AgentMiddleware` | Public API contract for agent middleware. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L320) | | `AgentPushRuntimeServiceRest` | Public API contract for agent push runtime service rest. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/registration.ts#L40) | | `AgentRegistry` | Public API contract for agent registry. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/definition.ts#L59) | | `AgentResponse` | Response payload for agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/schemas/agent.schema.ts#L260) | @@ -882,16 +882,16 @@ Clear all stored messages from memory. | `AgentServiceChatProjectAccessError` | Error shape for hosted chat project access. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L36) | | `AgentServiceChatProjectAccessResult` | Result returned from hosted chat project access. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L43) | | `AgentServiceChatRequestPrincipal` | Public API contract for hosted chat request principal. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L30) | -| `AgentServiceChatRuntimeAgent` | Public API contract for hosted chat runtime agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L84) | -| `AgentServiceChatRuntimeCreationOptions` | Options accepted by hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L116) | -| `AgentServiceChatRuntimeCreationResult` | Result returned from hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L91) | -| `AgentServiceChatRuntimeFinishPart` | Public API contract for hosted chat runtime finish part. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L14) | -| `AgentServiceChatRuntimeOnFinishEvent` | Event emitted for hosted chat runtime on finish. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L51) | -| `AgentServiceChatRuntimeProjectSteering` | Public API contract for hosted chat runtime project steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L99) | -| `AgentServiceChatRuntimeStreamInput` | Input payload for hosted chat runtime stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L70) | -| `AgentServiceChatRuntimeStreamResult` | Result returned from hosted chat runtime stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L76) | +| `AgentServiceChatRuntimeAgent` | Public API contract for hosted chat runtime agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L85) | +| `AgentServiceChatRuntimeCreationOptions` | Options accepted by hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L118) | +| `AgentServiceChatRuntimeCreationResult` | Result returned from hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L92) | +| `AgentServiceChatRuntimeFinishPart` | Public API contract for hosted chat runtime finish part. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L15) | +| `AgentServiceChatRuntimeOnFinishEvent` | Event emitted for hosted chat runtime on finish. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L52) | +| `AgentServiceChatRuntimeProjectSteering` | Public API contract for hosted chat runtime project steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L100) | +| `AgentServiceChatRuntimeStreamInput` | Input payload for hosted chat runtime stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L71) | +| `AgentServiceChatRuntimeStreamResult` | Result returned from hosted chat runtime stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L77) | | `AgentServiceChatRuntimeToolAssemblyResult` | Result returned from hosted chat runtime tool assembly. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-tool-assembly.ts#L63) | -| `AgentServiceChatRuntimeToUiMessageStreamOptions` | Options accepted by hosted chat runtime to UI message stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L60) | +| `AgentServiceChatRuntimeToUiMessageStreamOptions` | Options accepted by hosted chat runtime to UI message stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L61) | | `AgentServiceChildChunkMirror` | Public API contract for hosted child chunk mirror. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-mirror.ts#L5) | | `AgentServiceChildMirrorContext` | Context for hosted child mirror. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-mirror.ts#L16) | | `AgentServiceChildMirrorPart` | Public API contract for hosted child mirror part. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-mirror.ts#L86) | @@ -909,12 +909,12 @@ Clear all stored messages from memory. | `AgentServiceFormInputToolContext` | Context for hosted form input tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/form-input-tool.ts#L25) | | `AgentServiceJwtError` | Error shape for hosted service jwt. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/auth.ts#L44) | | `AgentServiceJwtResult` | Result returned from hosted service jwt. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/auth.ts#L51) | -| `AgentServiceOptions` | Options accepted by agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L128) | -| `AgentServicePreparedExecution` | Public API contract for agent service prepared execution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L133) | -| `AgentServiceProcessTarget` | Public API contract for agent service process target. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L135) | +| `AgentServiceOptions` | Options accepted by agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L129) | +| `AgentServicePreparedExecution` | Public API contract for agent service prepared execution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L134) | +| `AgentServiceProcessTarget` | Public API contract for agent service process target. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L136) | | `AgentServiceProjectAccessError` | Error shape for hosted service project access. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/auth.ts#L56) | | `AgentServiceProjectAccessResult` | Result returned from hosted service project access. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/auth.ts#L63) | -| `AgentServiceProjectSkillIdsContext` | Context for hosted project skill IDs. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L61) | +| `AgentServiceProjectSkillIdsContext` | Context for hosted project skill IDs. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L66) | | `AgentServiceProjectSteering` | Public API contract for hosted agent project steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/agent-project-steering.ts#L64) | | `AgentServiceProjectSteeringLogger` | Public API contract for hosted agent project steering logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/agent-project-steering.ts#L51) | | `AgentServiceProjectSteeringOptions` | Options accepted by hosted agent project steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/agent-project-steering.ts#L56) | @@ -942,7 +942,7 @@ Clear all stored messages from memory. | `AgentServiceTraceContext` | Context for agent service trace. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/bootstrap.ts#L8) | | `AgentServiceTraceContextGetter` | Public API contract for agent service trace context getter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/bootstrap.ts#L14) | | `AgentStatus` | Public API contract for agent status. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/schemas/agent.schema.ts#L238) | -| `AgentStreamResult` | Result returned from agent stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L358) | +| `AgentStreamResult` | Result returned from agent stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L357) | | `AgentTraceAttributes` | Public API contract for agent trace attributes. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/trace-attributes.ts#L10) | | `AgentTraceAttributeValue` | Public API contract for a value can be used as an agent trace attribute. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/trace-attributes.ts#L4) | | `AgentTraceUsage` | Public API contract for agent trace usage. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/trace-attributes.ts#L13) | @@ -1084,12 +1084,12 @@ Clear all stored messages from memory. | `CreateBootstrappedHostedChatExecutionRuntimeInput` | Input payload for create bootstrapped hosted chat execution runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-execution-runtime.ts#L130) | | `CreateConversationHostedLifecycleAdapterOptions` | Options accepted by create conversation hosted lifecycle adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/hosted-lifecycle.ts#L31) | | `CreateConversationHostedTerminalAdapterOptions` | Options accepted by create conversation hosted terminal adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/hosted-terminal.ts#L113) | -| `CreateDefaultAgentServiceChatRuntimeContextInput` | Input payload for create default hosted chat runtime context. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L99) | -| `CreateDefaultAgentServiceChatRuntimeOptions` | Options accepted by create default hosted chat runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L124) | -| `CreateDefaultAgentServiceProjectSteeringRefreshOptions` | Options accepted by create default hosted project steering refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L44) | -| `CreateDefaultHostedChatRuntimeContextInput` | Input payload for create default hosted chat runtime context. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L99) | -| `CreateDefaultHostedChatRuntimeOptions` | Options accepted by create default hosted chat runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L124) | -| `CreateDefaultHostedProjectSteeringRefreshOptions` | Options accepted by create default hosted project steering refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L44) | +| `CreateDefaultAgentServiceChatRuntimeContextInput` | Input payload for create default hosted chat runtime context. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L100) | +| `CreateDefaultAgentServiceChatRuntimeOptions` | Options accepted by create default hosted chat runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L125) | +| `CreateDefaultAgentServiceProjectSteeringRefreshOptions` | Options accepted by create default hosted project steering refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L49) | +| `CreateDefaultHostedChatRuntimeContextInput` | Input payload for create default hosted chat runtime context. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L100) | +| `CreateDefaultHostedChatRuntimeOptions` | Options accepted by create default hosted chat runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L125) | +| `CreateDefaultHostedProjectSteeringRefreshOptions` | Options accepted by create default hosted project steering refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L49) | | `CreateHostedAgentRunSpanControllerInput` | Input payload for create hosted agent run span controller. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/agent-run-lifecycle.ts#L53) | | `CreateHostedAgentServiceRuntimeOptions` | Options accepted by create hosted agent service runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/runtime.ts#L68) | | `CreateHostedChatExecutionRuntimeBootstrapInput` | Input payload for create hosted chat execution runtime bootstrap. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-execution-runtime.ts#L101) | @@ -1110,28 +1110,28 @@ Clear all stored messages from memory. | `DefaultAgentServiceChatRuntimeConfig` | Configuration used by default hosted chat runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L52) | | `DefaultAgentServiceChatRuntimeCreationOptions` | Options accepted by default hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L65) | | `DefaultAgentServiceChatRuntimeLogger` | Public API contract for default hosted chat runtime logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L60) | -| `DefaultAgentServiceChatRuntimeProjectSwitchInput` | Input payload for default hosted chat runtime project switch. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L118) | -| `DefaultAgentServiceChatRuntimeSteeringMutationInput` | Input payload for default hosted chat runtime steering mutation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L112) | -| `DefaultAgentServiceChatRuntimeSystemRefreshInput` | Input payload for default hosted chat runtime system refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L105) | +| `DefaultAgentServiceChatRuntimeProjectSwitchInput` | Input payload for default hosted chat runtime project switch. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L119) | +| `DefaultAgentServiceChatRuntimeSteeringMutationInput` | Input payload for default hosted chat runtime steering mutation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L113) | +| `DefaultAgentServiceChatRuntimeSystemRefreshInput` | Input payload for default hosted chat runtime system refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L106) | | `DefaultAgentServiceChatRuntimeTaskContext` | Context for default hosted chat runtime task. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L75) | | `DefaultAgentServiceInvokeAgentConfig` | Configuration used by default hosted invoke agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L93) | | `DefaultAgentServiceInvokeAgentContext` | Context for default hosted invoke agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L79) | -| `DefaultAgentServiceInvokeAgentInput` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L233) | -| `DefaultAgentServiceInvokeAgentLogger` | Public API contract for default hosted invoke agent logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L118) | -| `DefaultAgentServiceInvokeAgentProjectRefresh` | Public API contract for default hosted invoke agent project refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L143) | -| `DefaultAgentServiceInvokeAgentToolOptions` | Options accepted by default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L150) | -| `DefaultAgentServiceInvokeAgentToolResult` | Result returned from default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L138) | -| `DefaultAgentServiceInvokeAgentTrace` | Public API contract for default hosted invoke agent trace. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L132) | -| `DefaultAgentServiceInvokeAgentTraceAttributes` | Public API contract for default hosted invoke agent trace attributes. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L126) | -| `DefaultAgentServiceProjectSteeringFetchers` | Public API contract for default hosted project steering fetchers. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L34) | -| `DefaultAgentServiceProjectSteeringRefreshLogger` | Public API contract for default hosted project steering refresh logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L22) | -| `DefaultAgentServiceProjectSteeringRefreshLookup` | Public API contract for default hosted project steering refresh lookup. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L27) | +| `DefaultAgentServiceInvokeAgentInput` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L234) | +| `DefaultAgentServiceInvokeAgentLogger` | Public API contract for default hosted invoke agent logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L119) | +| `DefaultAgentServiceInvokeAgentProjectRefresh` | Public API contract for default hosted invoke agent project refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L144) | +| `DefaultAgentServiceInvokeAgentToolOptions` | Options accepted by default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L151) | +| `DefaultAgentServiceInvokeAgentToolResult` | Result returned from default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L139) | +| `DefaultAgentServiceInvokeAgentTrace` | Public API contract for default hosted invoke agent trace. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L133) | +| `DefaultAgentServiceInvokeAgentTraceAttributes` | Public API contract for default hosted invoke agent trace attributes. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L127) | +| `DefaultAgentServiceProjectSteeringFetchers` | Public API contract for default hosted project steering fetchers. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L39) | +| `DefaultAgentServiceProjectSteeringRefreshLogger` | Public API contract for default hosted project steering refresh logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L27) | +| `DefaultAgentServiceProjectSteeringRefreshLookup` | Public API contract for default hosted project steering refresh lookup. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L32) | | `DefaultHostedChatRuntimeConfig` | Configuration used by default hosted chat runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L52) | | `DefaultHostedChatRuntimeCreationOptions` | Options accepted by default hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L65) | | `DefaultHostedChatRuntimeLogger` | Public API contract for default hosted chat runtime logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L60) | -| `DefaultHostedChatRuntimeProjectSwitchInput` | Input payload for default hosted chat runtime project switch. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L118) | -| `DefaultHostedChatRuntimeSteeringMutationInput` | Input payload for default hosted chat runtime steering mutation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L112) | -| `DefaultHostedChatRuntimeSystemRefreshInput` | Input payload for default hosted chat runtime system refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L105) | +| `DefaultHostedChatRuntimeProjectSwitchInput` | Input payload for default hosted chat runtime project switch. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L119) | +| `DefaultHostedChatRuntimeSteeringMutationInput` | Input payload for default hosted chat runtime steering mutation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L113) | +| `DefaultHostedChatRuntimeSystemRefreshInput` | Input payload for default hosted chat runtime system refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L106) | | `DefaultHostedChatRuntimeTaskContext` | Context for default hosted chat runtime task. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-chat-runtime.ts#L75) | | `DefaultHostedChildForkRuntimeToolPreparationResult` | Result returned from default hosted child fork runtime tool preparation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-requested-tools.ts#L169) | | `DefaultHostedChildForkToolAssemblyResult` | Result returned from default hosted child fork tool assembly. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-requested-tools.ts#L194) | @@ -1139,16 +1139,16 @@ Clear all stored messages from memory. | `DefaultHostedChildForkToolSourcesResult` | Result returned from default hosted child fork tool sources. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-tool-sources.ts#L64) | | `DefaultHostedInvokeAgentConfig` | Configuration used by default hosted invoke agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L93) | | `DefaultHostedInvokeAgentContext` | Context for default hosted invoke agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L79) | -| `DefaultHostedInvokeAgentInput` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L233) | -| `DefaultHostedInvokeAgentLogger` | Public API contract for default hosted invoke agent logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L118) | -| `DefaultHostedInvokeAgentProjectRefresh` | Public API contract for default hosted invoke agent project refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L143) | -| `DefaultHostedInvokeAgentToolOptions` | Options accepted by default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L150) | -| `DefaultHostedInvokeAgentToolResult` | Result returned from default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L138) | -| `DefaultHostedInvokeAgentTrace` | Public API contract for default hosted invoke agent trace. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L132) | -| `DefaultHostedInvokeAgentTraceAttributes` | Public API contract for default hosted invoke agent trace attributes. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L126) | -| `DefaultHostedProjectSteeringFetchers` | Public API contract for default hosted project steering fetchers. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L34) | -| `DefaultHostedProjectSteeringRefreshLogger` | Public API contract for default hosted project steering refresh logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L22) | -| `DefaultHostedProjectSteeringRefreshLookup` | Public API contract for default hosted project steering refresh lookup. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L27) | +| `DefaultHostedInvokeAgentInput` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L234) | +| `DefaultHostedInvokeAgentLogger` | Public API contract for default hosted invoke agent logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L119) | +| `DefaultHostedInvokeAgentProjectRefresh` | Public API contract for default hosted invoke agent project refresh. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L144) | +| `DefaultHostedInvokeAgentToolOptions` | Options accepted by default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L151) | +| `DefaultHostedInvokeAgentToolResult` | Result returned from default hosted invoke agent tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L139) | +| `DefaultHostedInvokeAgentTrace` | Public API contract for default hosted invoke agent trace. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L133) | +| `DefaultHostedInvokeAgentTraceAttributes` | Public API contract for default hosted invoke agent trace attributes. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L127) | +| `DefaultHostedProjectSteeringFetchers` | Public API contract for default hosted project steering fetchers. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L39) | +| `DefaultHostedProjectSteeringRefreshLogger` | Public API contract for default hosted project steering refresh logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L27) | +| `DefaultHostedProjectSteeringRefreshLookup` | Public API contract for default hosted project steering refresh lookup. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L32) | | `DefaultResearchArtifactContext` | Context for default research artifact. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/artifacts/default-research-artifact-support.ts#L16) | | `DefaultResearchArtifactLogger` | Public API contract for default research artifact logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/artifacts/default-research-artifact-support.ts#L24) | | `DefaultResearchArtifactPaths` | Public API contract for default research artifact paths. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/artifacts/default-research-artifact-policy.ts#L67) | @@ -1182,8 +1182,8 @@ Clear all stored messages from memory. | `ExternalAgentWorkerRequestSnapshot` | Public API contract for external agent worker request snapshot. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/external-worker-client.ts#L21) | | `ExternalAgentWorkerRun` | Public API contract for external agent worker run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/external-worker-client.ts#L50) | | `ExternalAgentWorkerSession` | Public API contract for external agent worker session. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/external-worker-client.ts#L36) | -| `FetchDefaultAgentServiceProjectSteeringInput` | Input payload for fetch default hosted project steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L55) | -| `FetchDefaultHostedProjectSteeringInput` | Input payload for fetch default hosted project steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L55) | +| `FetchDefaultAgentServiceProjectSteeringInput` | Input payload for fetch default hosted project steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L60) | +| `FetchDefaultHostedProjectSteeringInput` | Input payload for fetch default hosted project steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-project-steering-refresh.ts#L60) | | `FinalizedMessageState` | State for finalized message. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/finalized-message.ts#L37) | | `FinalizeHostedChildForkRunContextResourcesInput` | Input payload for finalize hosted child fork run context resources. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-run-context.ts#L119) | | `FinalizeHostedDetachedOptions` | Options accepted by finalize hosted detached. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/stream-finalization.ts#L53) | @@ -1226,9 +1226,9 @@ Clear all stored messages from memory. | `HostedAgentServiceStreamExecutionInput` | Input payload for hosted agent service stream execution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/routes.ts#L56) | | `HostedAgUiChatForwardedConfig` | Configuration used by hosted AG-UI chat forwarded. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/ag-ui-chat-request.ts#L44) | | `HostedChatExecutionLifecycleAdapter` | Public API contract for hosted chat execution lifecycle adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-execution-lifecycle-types.ts#L5) | -| `HostedChatExecutionPreparationInput` | Input payload for hosted chat execution preparation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L207) | -| `HostedChatExecutionPreparationResult` | Result returned from hosted chat execution preparation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L246) | -| `HostedChatExecutionPreparationRootRunOptions` | Options accepted by hosted chat execution preparation root run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L186) | +| `HostedChatExecutionPreparationInput` | Input payload for hosted chat execution preparation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L208) | +| `HostedChatExecutionPreparationResult` | Result returned from hosted chat execution preparation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L247) | +| `HostedChatExecutionPreparationRootRunOptions` | Options accepted by hosted chat execution preparation root run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L187) | | `HostedChatExecutionRootStreamWatchdog` | Public API contract for hosted chat execution root stream watchdog. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-execution-runtime.ts#L86) | | `HostedChatExecutionRunContext` | Context for hosted chat execution run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-execution-runtime.ts#L80) | | `HostedChatExecutionRuntime` | Public API contract for hosted chat execution runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-execution-runtime.ts#L67) | @@ -1239,26 +1239,26 @@ Clear all stored messages from memory. | `HostedChatRequest` | Request payload for hosted chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request.ts#L468) | | `HostedChatRequestInput` | Input payload for hosted chat request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request.ts#L470) | | `HostedChatRequestPrincipal` | Public API contract for hosted chat request principal. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L30) | -| `HostedChatRuntimeAgent` | Public API contract for hosted chat runtime agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L84) | +| `HostedChatRuntimeAgent` | Public API contract for hosted chat runtime agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L85) | | `HostedChatRuntimeAgentAdapterInput` | Input payload for hosted chat runtime agent adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-agent-adapter.ts#L25) | | `HostedChatRuntimeAgentAdapterRunner` | Public API contract for hosted chat runtime agent adapter runner. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-agent-adapter.ts#L14) | | `HostedChatRuntimeAgentAdapterWarning` | Public API contract for hosted chat runtime agent adapter warning. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-agent-adapter.ts#L19) | | `HostedChatRuntimeAllowedToolNames` | Public API contract for hosted chat runtime allowed tool names. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-tool-assembly.ts#L60) | -| `HostedChatRuntimeCreationOptions` | Options accepted by hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L116) | -| `HostedChatRuntimeCreationPreparationInput` | Input payload for hosted chat runtime creation preparation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L92) | -| `HostedChatRuntimeCreationPreparationResult` | Result returned from hosted chat runtime creation preparation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L127) | -| `HostedChatRuntimeCreationResult` | Result returned from hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L91) | -| `HostedChatRuntimeFinishPart` | Public API contract for hosted chat runtime finish part. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L14) | -| `HostedChatRuntimeInstructionsInput` | Input payload for hosted chat runtime instructions. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L81) | -| `HostedChatRuntimeOnFinishEvent` | Event emitted for hosted chat runtime on finish. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L51) | -| `HostedChatRuntimePreparationRootRunContext` | Context for hosted chat runtime preparation root run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L67) | -| `HostedChatRuntimePreparationSteering` | Public API contract for hosted chat runtime preparation steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L75) | -| `HostedChatRuntimeProjectSteering` | Public API contract for hosted chat runtime project steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L99) | -| `HostedChatRuntimeStreamInput` | Input payload for hosted chat runtime stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L70) | -| `HostedChatRuntimeStreamResult` | Result returned from hosted chat runtime stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L76) | +| `HostedChatRuntimeCreationOptions` | Options accepted by hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L118) | +| `HostedChatRuntimeCreationPreparationInput` | Input payload for hosted chat runtime creation preparation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L93) | +| `HostedChatRuntimeCreationPreparationResult` | Result returned from hosted chat runtime creation preparation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L128) | +| `HostedChatRuntimeCreationResult` | Result returned from hosted chat runtime creation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L92) | +| `HostedChatRuntimeFinishPart` | Public API contract for hosted chat runtime finish part. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L15) | +| `HostedChatRuntimeInstructionsInput` | Input payload for hosted chat runtime instructions. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L82) | +| `HostedChatRuntimeOnFinishEvent` | Event emitted for hosted chat runtime on finish. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L52) | +| `HostedChatRuntimePreparationRootRunContext` | Context for hosted chat runtime preparation root run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L68) | +| `HostedChatRuntimePreparationSteering` | Public API contract for hosted chat runtime preparation steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L76) | +| `HostedChatRuntimeProjectSteering` | Public API contract for hosted chat runtime project steering. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L100) | +| `HostedChatRuntimeStreamInput` | Input payload for hosted chat runtime stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L71) | +| `HostedChatRuntimeStreamResult` | Result returned from hosted chat runtime stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L77) | | `HostedChatRuntimeToolAssemblyContext` | Context for hosted chat runtime tool assembly. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-tool-assembly.ts#L46) | | `HostedChatRuntimeToolAssemblyResult` | Result returned from hosted chat runtime tool assembly. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-tool-assembly.ts#L63) | -| `HostedChatRuntimeToUiMessageStreamOptions` | Options accepted by hosted chat runtime to UI message stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L60) | +| `HostedChatRuntimeToUiMessageStreamOptions` | Options accepted by hosted chat runtime to UI message stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-contract.ts#L61) | | `HostedChildChunkMirror` | Public API contract for hosted child chunk mirror. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-mirror.ts#L5) | | `HostedChildConversationBodyInput` | Input payload for hosted child conversation body. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-bootstrap.ts#L6) | | `HostedChildExecutionLifecycleOptions` | Options accepted by hosted child execution lifecycle. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-lifecycle.ts#L106) | @@ -1355,10 +1355,10 @@ Clear all stored messages from memory. | `HostedProjectRemoteToolSourcePrepareToolInput` | Input payload for hosted project remote tool source prepare tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-remote-tool-source.ts#L42) | | `HostedProjectRemoteToolSourceProjectSwitchHandler` | Handler for hosted project remote tool source project switch. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-remote-tool-source.ts#L37) | | `HostedProjectRemoteToolSourceRetryPolicy` | Public API contract for hosted project remote tool source retry policy. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-remote-tool-source.ts#L49) | -| `HostedProjectSkillIdsContext` | Context for hosted project skill IDs. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L61) | -| `HostedProjectSteeringAdapter` | Public API contract for hosted project steering adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L73) | -| `HostedProjectSteeringAdapterOptions` | Options accepted by hosted project steering adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L48) | -| `HostedProjectSteeringLogger` | Public API contract for hosted project steering logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L43) | +| `HostedProjectSkillIdsContext` | Context for hosted project skill IDs. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L66) | +| `HostedProjectSteeringAdapter` | Public API contract for hosted project steering adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L79) | +| `HostedProjectSteeringAdapterOptions` | Options accepted by hosted project steering adapter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L53) | +| `HostedProjectSteeringLogger` | Public API contract for hosted project steering logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/project-steering-adapter.ts#L48) | | `HostedResponseFinalizationState` | State for hosted response finalization. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/stream-finalization.ts#L14) | | `HostedResponseStreamHeartbeat` | Public API contract for hosted response stream heartbeat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/response-stream.ts#L15) | | `HostedResponseStreamHeartbeatState` | State for hosted response stream heartbeat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/response-stream.ts#L9) | @@ -1419,8 +1419,8 @@ Clear all stored messages from memory. | `MirroredToolChunkState` | State for mirrored tool chunk. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/mirrored-tool-chunk-state.ts#L30) | | `ModelProvider` | Public API contract for model provider. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/schemas/agent.schema.ts#L236) | | `ModelString` | Model configuration string format: "provider/model-name" Examples: "openai/gpt-4", "anthropic/claude-3-5-sonnet" | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L43) | -| `ModelTransportRequest` | Request payload for model transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L251) | -| `ModelTransportResolver` | Public API contract for model transport resolver. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L275) | +| `ModelTransportRequest` | Request payload for model transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L250) | +| `ModelTransportResolver` | Public API contract for model transport resolver. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L274) | | `MonitorHostedChildRunStatusInput` | Input payload for monitor hosted child run status. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-status.ts#L135) | | `MutableAgentProjectContext` | Context for mutable agent project. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/project/context.ts#L2) | | `NodeAgentServiceInstrumentationConfig` | Configuration used by node agent service instrumentation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L23) | @@ -1436,13 +1436,13 @@ Clear all stored messages from memory. | `NodeHostedAgentServiceTelemetryEnv` | Public API contract for node hosted agent service telemetry env. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L14) | | `NodeHostedAgentServiceTelemetryLogger` | Public API contract for node hosted agent service telemetry logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L66) | | `NodeHostedAgentServiceTelemetryProcessTarget` | Public API contract for node hosted agent service telemetry process target. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L72) | -| `NodeVeryfrontCloudAgentServiceMcpServer` | Public API contract for node Veryfront Cloud agent service MCP server. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L65) | -| `NodeVeryfrontCloudAgentServiceOptions` | Options accepted by node Veryfront Cloud agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L82) | +| `NodeVeryfrontCloudAgentServiceMcpServer` | Public API contract for node Veryfront Cloud agent service MCP server. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L66) | +| `NodeVeryfrontCloudAgentServiceOptions` | Options accepted by node Veryfront Cloud agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L83) | | `NodeVeryfrontCloudAgentServicePreparedExecution` | Full type of a prepared cloud agent chat execution, ready to stream or detach. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/cloud-agent-chat-execution.ts#L71) | -| `NodeVeryfrontCloudAgentServiceProcessTarget` | Public API contract for node Veryfront Cloud agent service process target. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L54) | -| `NormalizedAgentServiceChatRequest` | Request payload for normalized hosted chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L44) | +| `NodeVeryfrontCloudAgentServiceProcessTarget` | Public API contract for node Veryfront Cloud agent service process target. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L55) | +| `NormalizedAgentServiceChatRequest` | Request payload for normalized hosted chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L45) | | `NormalizedAgentServiceContract` | Public API contract for normalized agent service contract. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/definition.ts#L114) | -| `NormalizedHostedChatRequest` | Request payload for normalized hosted chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L44) | +| `NormalizedHostedChatRequest` | Request payload for normalized hosted chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L45) | | `OpenToolCalls` | Public API contract for open tool calls. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/mirrored-tool-chunk-state.ts#L110) | | `ParseAgentServiceChatRequestOptions` | Options accepted by parse hosted chat request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L71) | | `ParseAgUiSseResponseOptions` | Options for `parseAgUiSseResponse()`. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/ag-ui/sse-parser.ts#L50) | @@ -1451,13 +1451,13 @@ Clear all stored messages from memory. | `ParsedAgUiSseRun` | Parsed AG-UI SSE response summary for evals, canaries, and host tests. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/ag-ui/sse-parser.ts#L30) | | `ParsedHostedAgUiRequest` | Request payload for parsed hosted AG-UI. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/ag-ui-chat-request.ts#L60) | | `ParsedHostedChatRequest` | Request payload for parsed hosted chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L48) | -| `ParsedRuntimeSkillDocument` | Public API contract for parsed runtime skill document. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L191) | +| `ParsedRuntimeSkillDocument` | Public API contract for parsed runtime skill document. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L224) | | `ParseHostedChatRequestOptions` | Options accepted by parse hosted chat request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L71) | | `ParseRuntimeAgentMarkdownDefinitionInput` | Input payload for parse runtime agent markdown definition. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/agent-definition.ts#L102) | | `ParseRuntimeAgentRunInvocationHostedChatRequestOptions` | Options accepted when parsing a signed control-plane runtime invocation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-request-parser.ts#L80) | | `PersistConversationUserMessageFailure` | Public API contract for persist conversation user message failure. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/bootstrap.ts#L70) | | `PrepareAgentRuntimeMessagesFromUiMessagesOptions` | Options accepted by prepare agent runtime messages from UI messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/message-preparation.ts#L22) | -| `PrepareAgentServiceChatRuntimeMessagesOptions` | Options accepted by prepare hosted chat runtime messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L51) | +| `PrepareAgentServiceChatRuntimeMessagesOptions` | Options accepted by prepare hosted chat runtime messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L52) | | `PrepareAgentServiceConversationRootRunContextInput` | Input payload for prepare hosted conversation root run context. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/root-run-lifecycle.ts#L75) | | `PrepareConversationRootRunLifecycleOptions` | Options accepted by prepare conversation root run lifecycle. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/root-run-lifecycle.ts#L23) | | `PreparedAgentServiceChatExecution` | Public API contract for prepared hosted chat execution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/prepared-chat-execution.ts#L13) | @@ -1470,7 +1470,7 @@ Clear all stored messages from memory. | `PreparedHostedChatExecutionDetachedInput` | Input payload for prepared hosted chat execution detached. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/prepared-chat-execution.ts#L64) | | `PreparedHostedChatExecutionRuntimeOptions` | Options accepted by prepared hosted chat execution runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/prepared-chat-execution.ts#L34) | | `PreparedHostedChatExecutionStreamInput` | Input payload for prepared hosted chat execution stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/prepared-chat-execution.ts#L56) | -| `PrepareHostedChatRuntimeMessagesOptions` | Options accepted by prepare hosted chat runtime messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L51) | +| `PrepareHostedChatRuntimeMessagesOptions` | Options accepted by prepare hosted chat runtime messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-preparation.ts#L52) | | `PrepareHostedChatRuntimeToolAssemblyInput` | Input payload for prepare hosted chat runtime tool assembly. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/chat-runtime-tool-assembly.ts#L77) | | `PrepareHostedChildForkRuntimeStepMessagesInput` | Input payload for prepare hosted child fork runtime step messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-step-message-preparation.ts#L22) | | `PrepareHostedConversationRootRunContextInput` | Input payload for prepare hosted conversation root run context. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/root-run-lifecycle.ts#L75) | @@ -1498,11 +1498,11 @@ Clear all stored messages from memory. | `RequestAuthCache` | Public API contract for request auth cache. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/request-auth-cache.ts#L14) | | `ResolveAgentServiceRegistrationInputOptions` | Options accepted by resolve agent service registration input. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/registration.ts#L182) | | `ResolveConversationHostedTerminalStateInput` | Input payload for resolve conversation hosted terminal state. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/hosted-terminal.ts#L29) | -| `ResolvedAgentConfig` | Configuration used by resolved agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L248) | +| `ResolvedAgentConfig` | Configuration used by resolved agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L247) | | `ResolvedAgentServiceRegistrationInput` | Input payload for resolved agent service registration. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/registration.ts#L24) | | `ResolvedHostedRuntimeRequestConfig` | Configuration used by resolved hosted runtime request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/runtime-request-config.ts#L42) | -| `ResolvedModelTransport` | Public API contract for resolved model transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L267) | -| `ResolvedRuntimeState` | State for resolved runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L290) | +| `ResolvedModelTransport` | Public API contract for resolved model transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L266) | +| `ResolvedRuntimeState` | State for resolved runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L289) | | `ResolveHostedChildForkRuntimeConfigInput` | Input payload for resolve hosted child fork runtime config. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-tool-input.ts#L200) | | `ResolveHostedRuntimeRequestConfigInput` | Input payload for resolve hosted runtime request config. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/runtime-request-config.ts#L32) | | `ResolveNodeAgentServiceTelemetryConfigOptions` | Options accepted by resolve node agent service telemetry config. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L62) | @@ -1534,16 +1534,16 @@ Clear all stored messages from memory. | `RuntimeFileUrlResolverInput` | Input payload for runtime file URL resolver. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/message-file-url-refresh.ts#L13) | | `RuntimeGetProjectFileOptions` | Options accepted by runtime get project file. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-files-client.ts#L62) | | `RuntimeLoadedProjectSkill` | Public API contract for runtime loaded project skill. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-skill-loader.ts#L30) | -| `RuntimeLoadedSkillResponse` | Response payload for runtime loaded skill. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L149) | -| `RuntimeLoadedSkillResponseMessages` | Public API contract for runtime loaded skill response messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L140) | -| `RuntimeLoadSkillBuiltinStore` | Public API contract for runtime load skill builtin store. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L118) | -| `RuntimeLoadSkillErrorOutput` | Output from runtime load skill error. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L172) | -| `RuntimeLoadSkillReferenceFileOutput` | Output from runtime load skill reference file. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L165) | -| `RuntimeLoadSkillToolContext` | Context for runtime load skill tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L108) | -| `RuntimeLoadSkillToolInput` | Input payload for runtime load skill tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L160) | -| `RuntimeLoadSkillToolMessages` | Public API contract for runtime load skill tool messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L125) | -| `RuntimeLoadSkillToolOptions` | Options accepted by runtime load skill tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L128) | -| `RuntimeLoadSkillToolOutput` | Output from runtime load skill tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L177) | +| `RuntimeLoadedSkillResponse` | Response payload for runtime loaded skill. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L182) | +| `RuntimeLoadedSkillResponseMessages` | Public API contract for runtime loaded skill response messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L173) | +| `RuntimeLoadSkillBuiltinStore` | Public API contract for runtime load skill builtin store. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L120) | +| `RuntimeLoadSkillErrorOutput` | Output from runtime load skill error. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L174) | +| `RuntimeLoadSkillReferenceFileOutput` | Output from runtime load skill reference file. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L167) | +| `RuntimeLoadSkillToolContext` | Context for runtime load skill tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L109) | +| `RuntimeLoadSkillToolInput` | Input payload for runtime load skill tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L162) | +| `RuntimeLoadSkillToolMessages` | Public API contract for runtime load skill tool messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L127) | +| `RuntimeLoadSkillToolOptions` | Options accepted by runtime load skill tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L130) | +| `RuntimeLoadSkillToolOutput` | Output from runtime load skill tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/load-skill-tool.ts#L179) | | `RuntimeProjectFile` | Public API contract for runtime project file. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-files-client.ts#L48) | | `RuntimeProjectFileListItem` | Public API contract for runtime project file list item. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-files-client.ts#L50) | | `RuntimeProjectFilesApiOptions` | Options accepted by runtime project files API. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-files-client.ts#L55) | @@ -1559,11 +1559,11 @@ Clear all stored messages from memory. | `RuntimeProjectSkillLoaderOptions` | Options accepted by runtime project skill loader. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-skill-loader.ts#L41) | | `RuntimeProjectSteeringLookup` | Public API contract for runtime project steering lookup. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-skill-catalog.ts#L25) | | `RuntimePromptBlockOptions` | Options accepted by runtime prompt block. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/prompt-block.ts#L2) | -| `RuntimeSkillDefinition` | Definition for runtime skill. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L64) | -| `RuntimeSkillFrontmatter` | Public API contract for runtime skill frontmatter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L21) | -| `RuntimeSkillMetadataLogger` | Public API contract for runtime skill metadata logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L167) | -| `RuntimeStateRequest` | Request payload for runtime state. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L280) | -| `RuntimeStateResolver` | Public API contract for runtime state resolver. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L296) | +| `RuntimeSkillDefinition` | Definition for runtime skill. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L69) | +| `RuntimeSkillFrontmatter` | Public API contract for runtime skill frontmatter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L26) | +| `RuntimeSkillMetadataLogger` | Public API contract for runtime skill metadata logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L200) | +| `RuntimeStateRequest` | Request payload for runtime state. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L279) | +| `RuntimeStateResolver` | Public API contract for runtime state resolver. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L295) | | `RuntimeUploadUrlClientOptions` | Options accepted by runtime upload URL client. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/upload-url-client.ts#L24) | | `RuntimeUploadUrlFetch` | Public API contract for runtime upload URL fetch. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/upload-url-client.ts#L21) | | `RuntimeUploadUrlOptions` | Options accepted by runtime upload URL. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/upload-url-client.ts#L31) | @@ -1598,7 +1598,7 @@ Clear all stored messages from memory. | `ToolExecutionDataEventPublisher` | Public API contract for tool execution data event publisher. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/tool-execution-data-event-bridge.ts#L5) | | `ToolResultPart` | Agent message part for a tool result. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/schemas/agent.schema.ts#L250) | | `VeryfrontCloudAgentServiceChatExecutionPreparationLogger` | Public API contract for Veryfront Cloud hosted chat execution preparation logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/cloud-chat-execution-preparation.ts#L20) | -| `VeryfrontCloudAgentServiceOptions` | Options accepted by Veryfront Cloud agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L126) | +| `VeryfrontCloudAgentServiceOptions` | Options accepted by Veryfront Cloud agent service. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L127) | | `VeryfrontCloudHostedChatExecutionPreparationLogger` | Public API contract for Veryfront Cloud hosted chat execution preparation logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/cloud-chat-execution-preparation.ts#L20) | | `WaitForDurableHumanInputResolutionOptions` | Options accepted by wait for durable human input resolution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/input/human-input.ts#L185) | | `WaitForHumanInputOptions` | Options accepted by wait for human input. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/input/human-input.ts#L193) | @@ -1618,8 +1618,8 @@ Clear all stored messages from memory. | `agUiSseEventTypes` | AG-UI runtime event type constants normalized from browser-wire SSE events. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/ag-ui/sse-parser.ts#L5) | | `conversationRunEventTypes` | Shared conversation run event types value. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/run-events.ts#L7) | | `createNodeHostedAgentServiceRuntimeInfrastructure` | Create node hosted agent service runtime infrastructure. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-runtime-infrastructure.ts#L96) | -| `defaultHostedInvokeAgentInputSchema` | Schema for default hosted invoke agent input. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L225) | -| `defaultHostedInvokeAgentSelectionSchema` | Schema for default hosted invoke agent selection. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L214) | +| `defaultHostedInvokeAgentInputSchema` | Schema for default hosted invoke agent input. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L226) | +| `defaultHostedInvokeAgentSelectionSchema` | Schema for default hosted invoke agent selection. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/default-invoke-agent-tool.ts#L215) | | `getAgUiRuntimeContextItemSchema` | Zod schema for get AG-UI runtime context item. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/ag-ui-contract.ts#L60) | | `getAgUiRuntimeInjectedToolSchema` | Zod schema for get AG-UI runtime injected tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/ag-ui-contract.ts#L46) | | `getAgUiRuntimeMessageSchema` | Zod schema for get AG-UI runtime message. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/ag-ui-contract.ts#L172) | diff --git a/scripts/docs/generate-api-reference.ts b/scripts/docs/generate-api-reference.ts index 4c2a6115ce..6437a5bc5a 100644 --- a/scripts/docs/generate-api-reference.ts +++ b/scripts/docs/generate-api-reference.ts @@ -1848,7 +1848,7 @@ const PROPERTY_DESCRIPTIONS: Record> = { allowedModels: 'Restrict runtime model overrides to these "provider/model" strings', skills: - "Enable all discovered skills (`true`) or only selected skill IDs (`string[]`)", + "Select skills advertised in prompts and authorized for `load_skill`", }, SandboxOptions: { apiUrl: diff --git a/src/agent/hosted/runtime-essential-tools.test.ts b/src/agent/hosted/runtime-essential-tools.test.ts index 1e2ee770b6..b37aa8869f 100644 --- a/src/agent/hosted/runtime-essential-tools.test.ts +++ b/src/agent/hosted/runtime-essential-tools.test.ts @@ -63,6 +63,28 @@ describe("resolveHostedRuntimeAllowedToolNames", () => { assertEquals(result, null); }); + it("removes skill infrastructure from allow-all tools when the known skill manifest is empty", () => { + const result = resolveHostedRuntimeAllowedToolNames({ + allowedToolNames: null, + localToolNames: [ + "search_tools", + "load_tools", + "sleep", + "load_skill", + "load_skill_reference", + "execute_skill_script", + ], + availableSkillIds: [], + }); + + assertEquals(result?.has("search_tools"), true); + assertEquals(result?.has("load_tools"), true); + assertEquals(result?.has("sleep"), true); + assertEquals(result?.has("load_skill"), false); + assertEquals(result?.has("load_skill_reference"), false); + assertEquals(result?.has("execute_skill_script"), false); + }); + it("returns empty set unchanged when allowedToolNames is empty", () => { const result = resolveHostedRuntimeAllowedToolNames({ allowedToolNames: new Set(), diff --git a/src/agent/hosted/runtime-essential-tools.ts b/src/agent/hosted/runtime-essential-tools.ts index 4c15266039..ff5f833fdd 100644 --- a/src/agent/hosted/runtime-essential-tools.ts +++ b/src/agent/hosted/runtime-essential-tools.ts @@ -43,17 +43,27 @@ export function resolveHostedRuntimeAllowedToolNames( input: ResolveHostedRuntimeAllowedToolNamesInput, ): ReadonlySet | null { const allowedToolNames = normalizeHostedRuntimeAllowedToolNames(input.allowedToolNames); - if ( - !allowedToolNames || - (allowedToolNames.size === 0 && !input.includeRuntimeEssentialToolsWhenEmpty) - ) { + const localToolNames = new Set(input.localToolNames); + const hasKnownSkillManifest = input.availableSkillIds !== undefined; + const hasAuthorizedSkills = (input.availableSkillIds?.length ?? 0) > 0; + + if (!allowedToolNames) { + if (!hasKnownSkillManifest || hasAuthorizedSkills) { + return null; + } + + const resolvedToolNames = new Set(localToolNames); + for (const toolName of EMPTY_SKILL_MANIFEST_TOOL_NAMES) { + resolvedToolNames.delete(toolName); + } + return resolvedToolNames; + } + + if (allowedToolNames.size === 0 && !input.includeRuntimeEssentialToolsWhenEmpty) { return allowedToolNames; } - const localToolNames = new Set(input.localToolNames); const resolvedToolNames = new Set(allowedToolNames); - const hasKnownSkillManifest = input.availableSkillIds !== undefined; - const hasAuthorizedSkills = (input.availableSkillIds?.length ?? 0) > 0; if (hasKnownSkillManifest && !hasAuthorizedSkills) { for (const toolName of EMPTY_SKILL_MANIFEST_TOOL_NAMES) { diff --git a/src/agent/types.ts b/src/agent/types.ts index 609eb58e72..cea2600fba 100644 --- a/src/agent/types.ts +++ b/src/agent/types.ts @@ -222,15 +222,14 @@ export interface AgentConfig { */ onToolResult?: ToolExecutionResultHandler; /** - * Select the skills advertised in this agent's system prompt. + * Select the skills advertised in this agent's system prompt and authorized + * for `load_skill`. * - omitted or true: include every discovered skill visible to this agent - * - string[] or false: include only the listed skill IDs; use [] or false to advertise none + * - string[]: include and authorize only the listed visible skill IDs + * - [] or false: advertise no skills and do not authorize project or + * configured skills for `load_skill` * - * This selects the prompt catalog only. It does not restrict which - * owner-visible skills `load_skill` can resolve by id. - * - * Discovery happens at startup via discoverAll(). `load_skill` remains - * available to every agent regardless of this catalog selection. + * Discovery happens at startup via discoverAll(). */ skills?: true | false | string[]; /** diff --git a/src/skill/tools.test.ts b/src/skill/tools.test.ts index fbec79b7e2..2ae15aa844 100644 --- a/src/skill/tools.test.ts +++ b/src/skill/tools.test.ts @@ -1,5 +1,5 @@ import "#veryfront/schemas/_test-setup.ts"; -import { assertEquals, assertRejects } from "#veryfront/testing/assert.ts"; +import { assert, assertEquals, assertRejects } from "#veryfront/testing/assert.ts"; import { beforeEach, describe, it } from "#veryfront/testing/bdd.ts"; import { registerSkill, skillRegistry } from "./registry.ts"; import { @@ -146,6 +146,55 @@ Do work.`, assertEquals(readCount, 0); }); + it("load_skill should not disclose selector-disallowed skills in unavailable errors", async () => { + const allowedAdapter = createSkillTestAdapter({ + "/project/skills/allowed-skill/SKILL.md": `--- +name: allowed-skill +description: Allowed skill +--- +# Instructions +Allowed work.`, + }); + const hiddenAdapter = createSkillTestAdapter({ + "/project/skills/hidden-skill/SKILL.md": `--- +name: hidden-skill +description: Hidden skill +--- +# Instructions +Hidden work.`, + }); + registerSkill("allowed-skill", createNamedTestSkill("allowed-skill", allowedAdapter)); + registerSkill("hidden-skill", createNamedTestSkill("hidden-skill", hiddenAdapter)); + + const tool = createLoadSkillTool(); + + const error = await assertRejects( + () => + tool.execute({ skillId: "missing-skill" }, { + agentId: "agent", + allowedSkillIds: ["allowed-skill"], + }), + Error, + ); + + assert(error instanceof Error); + assertEquals(error.message.includes("hidden-skill"), false); + assertEquals(error.message.includes("allowed-skill"), false); + + const hiddenError = await assertRejects( + () => + tool.execute({ skillId: "hidden-skill" }, { + agentId: "agent", + allowedSkillIds: ["allowed-skill"], + }), + Error, + ); + + assert(hiddenError instanceof Error); + assertEquals(hiddenError.message.includes("hidden-skill"), false); + assertEquals(hiddenError.message.includes("allowed-skill"), false); + }); + it("load_skill should omit prompt notes for unavailable file tools", async () => { const fsAdapter = createSkillTestAdapter({ "/project/skills/my-skill/SKILL.md": `--- diff --git a/src/skill/tools.ts b/src/skill/tools.ts index db0c78a857..4d994e69b5 100644 --- a/src/skill/tools.ts +++ b/src/skill/tools.ts @@ -95,8 +95,13 @@ function resolveVisibleSkillOrThrow( options: SkillSelectorToolOptions = {}, ): Skill { const scope = { agentId: context?.agentId }; + const allowedSkillIds = getSelectorAllowedSkillIds(context, options); const skill = skillRegistry.resolveVisibleSkill(skillId, scope); if (!skill) { + if (allowedSkillIds !== undefined) { + throw createSkillUnavailableError(); + } + const visible = skillRegistry.getVisibleSkillIds(scope).join(", "); throw toError( createError({ @@ -105,10 +110,19 @@ function resolveVisibleSkillOrThrow( }), ); } - assertSkillAllowedBySelector(skill, context, options); + assertSkillAllowedBySelector(skill, allowedSkillIds); return skill; } +function createSkillUnavailableError(): Error { + return toError( + createError({ + type: "agent", + message: "Skill is not available to this agent.", + }), + ); +} + function getSelectorAllowedSkillIds( context: ToolExecutionContext | undefined, options: SkillSelectorToolOptions, @@ -128,20 +142,13 @@ function getSelectorAllowedSkillIds( function assertSkillAllowedBySelector( skill: Skill, - context: ToolExecutionContext | undefined, - options: SkillSelectorToolOptions, + allowedSkillIds: readonly string[] | undefined, ): void { - const allowedSkillIds = getSelectorAllowedSkillIds(context, options); if (allowedSkillIds === undefined || allowedSkillIds.includes(skill.id)) { return; } - throw toError( - createError({ - type: "agent", - message: `Skill "${skill.id}" is not available to this agent.`, - }), - ); + throw createSkillUnavailableError(); } function hasRuntimeSkillBoundary( From 2116e46f9a91920101660cff733484c939c3f7e7 Mon Sep 17 00:00:00 2001 From: Koji Wakayama Date: Wed, 29 Jul 2026 21:14:06 +0200 Subject: [PATCH 8/8] Clarify skill selector short-name contract Public agent configuration docs now match the selector contract: explicit skills entries can resolve either visible skill IDs or the configuring agent's own skill short names. This updates the generated API reference source and regenerated output without changing runtime behavior. Constraint: Re-review confirmed resolveSkillSelector resolves own short names before visible IDs. Rejected: Add behavior test | prose-only correction and selector behavior is already covered. Confidence: high Scope-risk: narrow Directive: Keep AgentConfig.skills prose aligned with resolveSkillSelector short-name resolution. Tested: deno task docs; docs generator test; focused hosted and skill suite; deno task typecheck; git diff --check. Not-tested: scripts/hooks/pre-push remains blocked by the pre-existing release-assets docs validation baseline. --- docs/api-reference/veryfront/agent.md | 66 +++++++++++++------------- scripts/docs/generate-api-reference.ts | 2 +- src/agent/types.ts | 7 +-- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/docs/api-reference/veryfront/agent.md b/docs/api-reference/veryfront/agent.md index 32fe699a74..85496f3062 100644 --- a/docs/api-reference/veryfront/agent.md +++ b/docs/api-reference/veryfront/agent.md @@ -157,9 +157,9 @@ Agent helper. | `resolveModelTransport?` | `ModelTransportResolver` | Optional request-aware hook for overriding the resolved model runtime and provider transport options on a per-call basis. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L212) | | `resolveRuntimeState?` | `RuntimeStateResolver` | Optional step-boundary hook for refreshing the runtime system prompt and host-owned context during a long-lived run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L217) | | `onToolResult?` | `ToolExecutionResultHandler` | Optional hook invoked after the runtime executes a configured local, registry, integration, or remote tool and before the tool result is persisted or streamed back to callers. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L223) | -| `skills?` | `true \| false \| string[]` | Select the skills advertised in this agent's system prompt and authorized for `load_skill`. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L234) | -| `suggestions?` | `SuggestionsConfig` | Prompt starters shown on an empty chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L241) | -| `security?` | `false` | Set to false to disable the default security middleware | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L243) | +| `skills?` | `true \| false \| string[]` | Select visible skill IDs or this agent's own skill short names advertised in this agent's system prompt and authorized for `load_skill`. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L235) | +| `suggestions?` | `SuggestionsConfig` | Prompt starters shown on an empty chat. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L242) | +| `security?` | `false` | Set to false to disable the default security middleware | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L244) | **Returns:** `Agent` @@ -169,13 +169,13 @@ Run the agent and return a complete response. Accepts a string or message array | Property | Type | Description | Source | |----------|------|-------------|--------| -| `input` | `string \| Message[]` | Prompt string or message history | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L371) | -| `context?` | Record<string, unknown> | Additional context passed to the agent | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L372) | -| `model?` | `ModelString` | Override the agent's default model for this request. Must be in `allowedModels` if configured. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L374) | -| `maxOutputTokens?` | `number` | Override the maximum model output tokens for this request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L376) | -| `tools?` | `AgentGenerateToolReplacements` | Replace this agent's configured tools for this generate request only. When present, only these tools are advertised and executable. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L381) | -| `retainSkillLoaderTools?` | `boolean` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L385) | -| `abortSignal?` | `AbortSignal` | Abort signal for cooperative cancellation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L387) | +| `input` | `string \| Message[]` | Prompt string or message history | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L372) | +| `context?` | Record<string, unknown> | Additional context passed to the agent | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L373) | +| `model?` | `ModelString` | Override the agent's default model for this request. Must be in `allowedModels` if configured. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L375) | +| `maxOutputTokens?` | `number` | Override the maximum model output tokens for this request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L377) | +| `tools?` | `AgentGenerateToolReplacements` | Replace this agent's configured tools for this generate request only. When present, only these tools are advertised and executable. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L382) | +| `retainSkillLoaderTools?` | `boolean` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L386) | +| `abortSignal?` | `AbortSignal` | Abort signal for cooperative cancellation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L388) | **Returns:** Promise<AgentResponse> @@ -185,15 +185,15 @@ Run the agent and stream the response. Returns a result with `.toDataStreamRespo | Property | Type | Description | Source | |----------|------|-------------|--------| -| `input?` | `string` | Prompt string | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L391) | -| `messages?` | `Message[]` | Conversation message history | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L392) | -| `context?` | Record<string, unknown> | Additional context passed to the agent | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L393) | -| `model?` | `ModelString` | Override the agent's default model for this request. Must be in `allowedModels` if configured. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L395) | -| `maxOutputTokens?` | `number` | Override the maximum model output tokens for this request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L397) | -| `onToolCall?` | (toolCall: ToolCall) => void | Callback fired when a tool is invoked | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L398) | -| `onChunk?` | (chunk: string) => void | Callback fired for each text chunk | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L399) | -| `onFinish?` | (response: AgentResponse) => void | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L400) | -| `abortSignal?` | `AbortSignal` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L401) | +| `input?` | `string` | Prompt string | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L392) | +| `messages?` | `Message[]` | Conversation message history | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L393) | +| `context?` | Record<string, unknown> | Additional context passed to the agent | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L394) | +| `model?` | `ModelString` | Override the agent's default model for this request. Must be in `allowedModels` if configured. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L396) | +| `maxOutputTokens?` | `number` | Override the maximum model output tokens for this request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L398) | +| `onToolCall?` | (toolCall: ToolCall) => void | Callback fired when a tool is invoked | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L399) | +| `onChunk?` | (chunk: string) => void | Callback fired for each text chunk | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L400) | +| `onFinish?` | (response: AgentResponse) => void | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L401) | +| `abortSignal?` | `AbortSignal` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L402) | **Returns:** Promise<AgentStreamResult> @@ -600,13 +600,13 @@ Clear all stored messages from memory. | `getRuntimeProjectInstructions` | Return runtime project instructions. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-skill-catalog.ts#L114) | | `getRuntimeProjectSkillCatalog` | Return runtime project skill catalog. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/project-skill-catalog.ts#L134) | | `getRuntimeUploadUrl` | Return runtime upload URL. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/upload-url-client.ts#L38) | -| `getTextFromParts` | Return text from parts. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L327) | -| `getToolArguments` | Return tool arguments. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L345) | +| `getTextFromParts` | Return text from parts. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L328) | +| `getToolArguments` | Return tool arguments. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L346) | | `handleHostedChildForkFailure` | Process a hosted child fork failure. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-stream-execution.ts#L280) | | `handleHostedChildForkRunContextError` | Error shape for handle hosted child fork run context. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-run-context.ts#L267) | | `handleHostedChildForkStreamPart` | Process a hosted child fork stream part. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-fork-stream-execution.ts#L318) | -| `hasArgs` | Check whether args is present. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L335) | -| `hasInput` | Input payload for has. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L340) | +| `hasArgs` | Check whether args is present. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L336) | +| `hasInput` | Input payload for has. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L341) | | `initializeNodeAgentServiceOpenTelemetry` | Initialize node agent service open telemetry. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L470) | | `initializeNodeHostedAgentServiceOpenTelemetry` | Initialize node hosted agent service open telemetry. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L416) | | `installAbortRejectionGuard` | Install abort rejection guard helper. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/abort-rejection-guard.ts#L114) | @@ -849,7 +849,7 @@ Clear all stored messages from memory. | `AbortRejectionGuardLogger` | Public API contract for abort rejection guard logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/abort-rejection-guard.ts#L2) | | `AbortRejectionProcessTarget` | Public API contract for abort rejection process target. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/abort-rejection-guard.ts#L7) | | `ActiveConversationRunStatus` | Public API contract for a conversation run status is active. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/durable-contracts.ts#L164) | -| `Agent` | Public API contract for agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L366) | +| `Agent` | Public API contract for agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L367) | | `AgentCatalogAction` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/identity-contracts.ts#L13) | | `AgentCatalogKind` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/identity-contracts.ts#L6) | | `AgentConfig` | Configuration used by agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L142) | @@ -861,7 +861,7 @@ Clear all stored messages from memory. | `AgentMcpServerConfig` | MCP server available to an agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L139) | | `AgentMcpToolPolicy` | Policy for tools exposed by one MCP server. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L95) | | `AgentMessage` | Message exchanged with an agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/schemas/agent.schema.ts#L254) | -| `AgentMiddleware` | Public API contract for agent middleware. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L320) | +| `AgentMiddleware` | Public API contract for agent middleware. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L321) | | `AgentPushRuntimeServiceRest` | Public API contract for agent push runtime service rest. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/registration.ts#L40) | | `AgentRegistry` | Public API contract for agent registry. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/definition.ts#L59) | | `AgentResponse` | Response payload for agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/schemas/agent.schema.ts#L260) | @@ -942,7 +942,7 @@ Clear all stored messages from memory. | `AgentServiceTraceContext` | Context for agent service trace. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/bootstrap.ts#L8) | | `AgentServiceTraceContextGetter` | Public API contract for agent service trace context getter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/bootstrap.ts#L14) | | `AgentStatus` | Public API contract for agent status. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/schemas/agent.schema.ts#L238) | -| `AgentStreamResult` | Result returned from agent stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L357) | +| `AgentStreamResult` | Result returned from agent stream. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L358) | | `AgentTraceAttributes` | Public API contract for agent trace attributes. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/trace-attributes.ts#L10) | | `AgentTraceAttributeValue` | Public API contract for a value can be used as an agent trace attribute. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/trace-attributes.ts#L4) | | `AgentTraceUsage` | Public API contract for agent trace usage. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/trace-attributes.ts#L13) | @@ -1419,8 +1419,8 @@ Clear all stored messages from memory. | `MirroredToolChunkState` | State for mirrored tool chunk. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/mirrored-tool-chunk-state.ts#L30) | | `ModelProvider` | Public API contract for model provider. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/schemas/agent.schema.ts#L236) | | `ModelString` | Model configuration string format: "provider/model-name" Examples: "openai/gpt-4", "anthropic/claude-3-5-sonnet" | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L43) | -| `ModelTransportRequest` | Request payload for model transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L250) | -| `ModelTransportResolver` | Public API contract for model transport resolver. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L274) | +| `ModelTransportRequest` | Request payload for model transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L251) | +| `ModelTransportResolver` | Public API contract for model transport resolver. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L275) | | `MonitorHostedChildRunStatusInput` | Input payload for monitor hosted child run status. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-status.ts#L135) | | `MutableAgentProjectContext` | Context for mutable agent project. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/project/context.ts#L2) | | `NodeAgentServiceInstrumentationConfig` | Configuration used by node agent service instrumentation. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L23) | @@ -1498,11 +1498,11 @@ Clear all stored messages from memory. | `RequestAuthCache` | Public API contract for request auth cache. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/request-auth-cache.ts#L14) | | `ResolveAgentServiceRegistrationInputOptions` | Options accepted by resolve agent service registration input. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/registration.ts#L182) | | `ResolveConversationHostedTerminalStateInput` | Input payload for resolve conversation hosted terminal state. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/conversation/hosted-terminal.ts#L29) | -| `ResolvedAgentConfig` | Configuration used by resolved agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L247) | +| `ResolvedAgentConfig` | Configuration used by resolved agent. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L248) | | `ResolvedAgentServiceRegistrationInput` | Input payload for resolved agent service registration. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/registration.ts#L24) | | `ResolvedHostedRuntimeRequestConfig` | Configuration used by resolved hosted runtime request. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/runtime-request-config.ts#L42) | -| `ResolvedModelTransport` | Public API contract for resolved model transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L266) | -| `ResolvedRuntimeState` | State for resolved runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L289) | +| `ResolvedModelTransport` | Public API contract for resolved model transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L267) | +| `ResolvedRuntimeState` | State for resolved runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L290) | | `ResolveHostedChildForkRuntimeConfigInput` | Input payload for resolve hosted child fork runtime config. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/child-tool-input.ts#L200) | | `ResolveHostedRuntimeRequestConfigInput` | Input payload for resolve hosted runtime request config. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/runtime-request-config.ts#L32) | | `ResolveNodeAgentServiceTelemetryConfigOptions` | Options accepted by resolve node agent service telemetry config. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-telemetry.ts#L62) | @@ -1562,8 +1562,8 @@ Clear all stored messages from memory. | `RuntimeSkillDefinition` | Definition for runtime skill. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L69) | | `RuntimeSkillFrontmatter` | Public API contract for runtime skill frontmatter. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L26) | | `RuntimeSkillMetadataLogger` | Public API contract for runtime skill metadata logger. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/skill-metadata.ts#L200) | -| `RuntimeStateRequest` | Request payload for runtime state. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L279) | -| `RuntimeStateResolver` | Public API contract for runtime state resolver. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L295) | +| `RuntimeStateRequest` | Request payload for runtime state. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L280) | +| `RuntimeStateResolver` | Public API contract for runtime state resolver. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/types.ts#L296) | | `RuntimeUploadUrlClientOptions` | Options accepted by runtime upload URL client. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/upload-url-client.ts#L24) | | `RuntimeUploadUrlFetch` | Public API contract for runtime upload URL fetch. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/upload-url-client.ts#L21) | | `RuntimeUploadUrlOptions` | Options accepted by runtime upload URL. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/runtime/upload-url-client.ts#L31) | diff --git a/scripts/docs/generate-api-reference.ts b/scripts/docs/generate-api-reference.ts index 6437a5bc5a..ea55ad58bf 100644 --- a/scripts/docs/generate-api-reference.ts +++ b/scripts/docs/generate-api-reference.ts @@ -1848,7 +1848,7 @@ const PROPERTY_DESCRIPTIONS: Record> = { allowedModels: 'Restrict runtime model overrides to these "provider/model" strings', skills: - "Select skills advertised in prompts and authorized for `load_skill`", + "Select visible skill IDs or this agent's own short names for prompts and `load_skill`", }, SandboxOptions: { apiUrl: diff --git a/src/agent/types.ts b/src/agent/types.ts index cea2600fba..1c2b0a82ad 100644 --- a/src/agent/types.ts +++ b/src/agent/types.ts @@ -222,10 +222,11 @@ export interface AgentConfig { */ onToolResult?: ToolExecutionResultHandler; /** - * Select the skills advertised in this agent's system prompt and authorized - * for `load_skill`. + * Select visible skill IDs or this agent's own skill short names advertised + * in this agent's system prompt and authorized for `load_skill`. * - omitted or true: include every discovered skill visible to this agent - * - string[]: include and authorize only the listed visible skill IDs + * - string[]: include and authorize only listed visible skill IDs or this + * agent's own skill short names * - [] or false: advertise no skills and do not authorize project or * configured skills for `load_skill` *