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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 55 additions & 47 deletions crates/webcodex-tool-contracts/src/tool_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ pub enum ToolAuditSemanticResultPolicy {

impl ToolAuditPolicy {
/// Existing audit-safe behavior for tools without a narrower result
/// projection. Every ToolDefinition must opt in explicitly through `def(...)`;
/// projection. Every ToolDefinition must declare its audit policy explicitly;
/// there is intentionally no implicit/missing-policy default.
pub const TYPED_CANONICAL: Self = Self {
request: ToolAuditRequestPolicy::Typed,
Expand Down Expand Up @@ -1179,7 +1179,10 @@ pub struct ToolManifestIntent {
pub tools: &'static [&'static str],
}

const fn def(
// Required inputs to the canonical definition. Keep these named and without a
// Default: visibility, audit, authority, capability and effect flags must be
// deliberate at each declaration (or supplied by a narrow shared constructor).
struct ToolDeclaration {
name: &'static str,
audit: ToolAuditPolicy,
visibility: ToolVisibility,
Expand All @@ -1193,39 +1196,42 @@ const fn def(
destructive: bool,
shell_like: bool,
session_evidence: ToolSessionEvidencePolicy,
) -> ToolDefinition {
ToolDefinition {
name,
audit,
model_spec: None,
adaptive_runtime_direct: None,
}

operator_extension_family: None,
execution: None,
composition: ToolCompositionPolicy::Denied,
host_orchestration: ToolHostOrchestrationHint::UNSPECIFIED,
visibility,
category,
metadata: make_tool_metadata(
name,
provider_id,
semantic,
required_scope,
requires_project,
path_hint,
destructive,
shell_like,
),
policy: ToolDefinitionPolicy::DEFAULT,
activity: ToolActivityPolicy::DEFAULT,
session_evidence,
runner_capability,
impl ToolDeclaration {
const fn define(self) -> ToolDefinition {
ToolDefinition {
name: self.name,
audit: self.audit,
model_spec: None,
adaptive_runtime_direct: None,
operator_extension_family: None,
execution: None,
composition: ToolCompositionPolicy::Denied,
host_orchestration: ToolHostOrchestrationHint::UNSPECIFIED,
visibility: self.visibility,
category: self.category,
metadata: make_tool_metadata(
self.name,
self.provider_id,
self.semantic,
self.required_scope,
self.requires_project,
self.path_hint,
self.destructive,
self.shell_like,
),
policy: ToolDefinitionPolicy::DEFAULT,
activity: ToolActivityPolicy::DEFAULT,
session_evidence: self.session_evidence,
runner_capability: self.runner_capability,
}
}
}

// Runner-backed Project reads share this authority/effect contract. Audit
// redaction, capability, path selection, and Session evidence stay explicit
// at each declaration; mutation and execution tools continue to use `def`.
// at each declaration; mutation and execution tools use a full ToolDeclaration.
const fn runner_project_read_def(
name: &'static str,
audit: ToolAuditPolicy,
Expand All @@ -1234,26 +1240,27 @@ const fn runner_project_read_def(
path_hint: ToolPathHint,
session_evidence: ToolSessionEvidencePolicy,
) -> ToolDefinition {
def(
ToolDeclaration {
name,
audit,
ToolVisibility::ModelVisible,
visibility: ToolVisibility::ModelVisible,
category,
Some(runner_capability),
TOOL_PROVIDER_RUNNER,
ToolSemanticContract {
runner_capability: Some(runner_capability),
provider_id: TOOL_PROVIDER_RUNNER,
semantic: ToolSemanticContract {
effect: ToolEffect::Observe,
risk: ToolRisk::Read,
approval: ToolApprovalPolicy::None,
idempotency: ToolIdempotency::PureRead,
},
Some(PROJECT_READ),
true,
required_scope: Some(PROJECT_READ),
requires_project: true,
path_hint,
false,
false,
destructive: false,
shell_like: false,
session_evidence,
)
}
.define()
}

// Server-owned Control operations without a Project, path, Runner dispatch, or
Expand All @@ -1268,21 +1275,22 @@ const fn control_def(
required_scope: Option<&'static str>,
destructive: bool,
) -> ToolDefinition {
def(
ToolDeclaration {
name,
audit,
visibility,
category,
None,
TOOL_PROVIDER_CONTROL,
runner_capability: None,
provider_id: TOOL_PROVIDER_CONTROL,
semantic,
required_scope,
false,
NoPath,
requires_project: false,
path_hint: NoPath,
destructive,
false,
ToolSessionEvidencePolicy::NONE,
)
shell_like: false,
session_evidence: ToolSessionEvidencePolicy::NONE,
}
.define()
}

const fn model_spec(definition: ToolDefinition, description: &'static str) -> ToolDefinition {
Expand Down
72 changes: 36 additions & 36 deletions crates/webcodex-tool-contracts/src/tool_definition/agent_tasks.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::RunnerCapabilityRequirement::CodingAgentRuns;
use super::ToolVisibility::ModelVisible;
use super::{
control_def, def, model_spec, permission_risk, require_all_scopes, ToolDefinition,
control_def, model_spec, permission_risk, require_all_scopes, ToolDeclaration, ToolDefinition,
PERMISSION_RISK_JOB, PERMISSION_RISK_WRITE, TOOL_CATEGORY_AGENT_TASK,
};
use crate::metadata::{
Expand Down Expand Up @@ -210,26 +210,26 @@ pub(super) const DEFINITIONS: &[ToolDefinition] = &[
permission_risk(
model_spec(
require_all_scopes(
def(
"start_agent_task_coding_run",
CODING_RUN_AUDIT,
ModelVisible,
TOOL_CATEGORY_AGENT_TASK,
Some(CodingAgentRuns),
TOOL_PROVIDER_RUNNER,
super::ToolSemanticContract {
ToolDeclaration {
name: "start_agent_task_coding_run",
audit: CODING_RUN_AUDIT,
visibility: ModelVisible,
category: TOOL_CATEGORY_AGENT_TASK,
runner_capability: Some(CodingAgentRuns),
provider_id: TOOL_PROVIDER_RUNNER,
semantic: super::ToolSemanticContract {
effect: super::ToolEffect::Execute,
risk: JobRun,
approval: super::ToolApprovalPolicy::Standard,
idempotency: super::ToolIdempotency::FencedReplay,
},
Some(CODING_AGENT_RUN),
true,
NoPath,
true,
false,
super::ToolSessionEvidencePolicy::NONE,
),
required_scope: Some(CODING_AGENT_RUN),
requires_project: true,
path_hint: NoPath,
destructive: true,
shell_like: false,
session_evidence: super::ToolSessionEvidencePolicy::NONE,
}.define(),
&[
COMMUNICATION_READ,
COMMUNICATION_MANAGE,
Expand All @@ -244,26 +244,26 @@ pub(super) const DEFINITIONS: &[ToolDefinition] = &[
permission_risk(
model_spec(
require_all_scopes(
def(
"reconcile_agent_task_coding_run",
CODING_RUN_AUDIT,
ModelVisible,
TOOL_CATEGORY_AGENT_TASK,
None,
TOOL_PROVIDER_RUNNER,
super::ToolSemanticContract {
effect: super::ToolEffect::Mutate,
risk: WorkflowManage,
approval: super::ToolApprovalPolicy::None,
idempotency: super::ToolIdempotency::DesiredState,
},
Some(COMMUNICATION_MANAGE),
false,
NoPath,
true,
false,
super::ToolSessionEvidencePolicy::NONE,
),
ToolDeclaration {
name: "reconcile_agent_task_coding_run",
audit: CODING_RUN_AUDIT,
visibility: ModelVisible,
category: TOOL_CATEGORY_AGENT_TASK,
runner_capability: None,
provider_id: TOOL_PROVIDER_RUNNER,
semantic: super::ToolSemanticContract {
effect: super::ToolEffect::Mutate,
risk: WorkflowManage,
approval: super::ToolApprovalPolicy::None,
idempotency: super::ToolIdempotency::DesiredState,
},
required_scope: Some(COMMUNICATION_MANAGE),
requires_project: false,
path_hint: NoPath,
destructive: true,
shell_like: false,
session_evidence: super::ToolSessionEvidencePolicy::NONE,
}.define(),
&[COMMUNICATION_READ, COMMUNICATION_MANAGE, CODING_AGENT_RUN],
),
"Reconcile only the exact CodingAgentRun already durably bound to one owned AgentTaskAttempt. It never starts execution, chooses a provider, changes intent, or requires the old browser's Attempt fence. Authoritative Completed/Failed/Cancelled truth can terminalize the exact latest bound Attempt even after its ordinary lease expires; Lost remains outcome_unknown.",
Expand Down
Loading
Loading