feat: extract task tools builtin module

This commit is contained in:
Keisuke Hirata 2026-06-05 08:59:08 +09:00
parent 9721c81eb0
commit f394f15ba5
No known key found for this signature in database
4 changed files with 137 additions and 56 deletions

View File

@ -9,7 +9,7 @@ use session_store::Store;
use tokio::sync::{broadcast, mpsc, oneshot};
use crate::discovery::{PodDiscovery, list_pods_tool, restore_pod_tool, send_to_peer_pod_tool};
use crate::feature::{FeatureRegistryBuilder, builtin::task_feature};
use crate::feature::{FeatureRegistryBuilder, builtin::task_tools_feature};
use crate::ipc::alerter::Alerter;
use crate::ipc::notify_buffer::NotifyBuffer;
use crate::ipc::server::SocketServer;
@ -522,7 +522,7 @@ where
));
let mut feature_registry = FeatureRegistryBuilder::new();
feature_registry.add_module(task_feature(task_store));
feature_registry.add_module(task_tools_feature(task_store));
let _feature_install_report = pod.install_features(feature_registry);
let worker = pod.worker_mut();

View File

@ -1256,59 +1256,7 @@ pub enum FeatureInstallError {
Install(String),
}
/// Builtin task tools feature used to prove existing builtin tool registration
/// through the feature registry without changing tool names, schemas, or
/// permission behavior.
pub mod builtin {
use super::*;
pub fn task_feature(task_store: tools::TaskStore) -> impl FeatureModule {
TaskFeature { task_store }
}
struct TaskFeature {
task_store: tools::TaskStore,
}
impl FeatureModule for TaskFeature {
fn descriptor(&self) -> FeatureDescriptor {
FeatureDescriptor::builtin("task-tools", "Task tools")
.with_description("Session-lifetime task tracking builtin tools")
.with_tool(ToolDeclaration::new(
"TaskCreate",
"Create a session-lifetime user-visible task",
))
.with_tool(ToolDeclaration::new(
"TaskUpdate",
"Update a session-lifetime user-visible task",
))
.with_tool(ToolDeclaration::new(
"TaskGet",
"Get one session-lifetime user-visible task",
))
.with_tool(ToolDeclaration::new(
"TaskList",
"List session-lifetime user-visible tasks",
))
}
fn install(
&self,
context: &mut FeatureInstallContext<'_>,
) -> Result<(), FeatureInstallError> {
let names = ["TaskCreate", "TaskList", "TaskGet", "TaskUpdate"];
for (name, definition) in names
.into_iter()
.zip(tools::task_tools(self.task_store.clone()))
{
context
.tools()
.register(ToolContribution::new(name, definition))?;
}
Ok(())
}
}
}
pub mod builtin;
#[cfg(test)]
mod tests {
@ -1797,13 +1745,76 @@ mod tests {
assert!(report.reports[0].skipped.is_empty());
}
#[test]
fn builtin_internal_task_feature_descriptor_has_exact_tools_and_no_authorities() {
let descriptor = builtin::task_tools_feature(tools::TaskStore::new()).descriptor();
let tool_names: Vec<_> = descriptor
.tools
.iter()
.map(|tool| tool.name.as_str())
.collect();
assert_eq!(descriptor.id.as_str(), "builtin:task-tools");
assert_eq!(descriptor.runtime, FeatureRuntimeKind::Builtin);
assert!(descriptor.requested_authorities.is_empty());
assert!(descriptor.hooks.is_empty());
assert!(descriptor.background_tasks.is_empty());
assert!(descriptor.provides_services.is_empty());
assert!(descriptor.requires_services.is_empty());
assert_eq!(
tool_names,
vec!["TaskCreate", "TaskUpdate", "TaskGet", "TaskList"]
);
}
#[test]
fn builtin_internal_task_feature_installs_declared_tools_without_host_authorities() {
let task_store = tools::TaskStore::new();
let mut hook_builder = HookRegistryBuilder::default();
let mut pending_tools = Vec::new();
let mut builder = FeatureRegistryBuilder::new();
builder.add_module(builtin::task_tools_feature(task_store));
let mut declared_names: Vec<_> = builder.descriptors()[0]
.tools
.iter()
.map(|tool| tool.name.clone())
.collect();
let report = builder.install_into_pending(&mut pending_tools, &mut hook_builder);
let pending_names: Vec<_> = pending_tools
.iter()
.map(|definition| definition().0.name)
.collect();
let installed_names = report.installed_tool_names();
let mut sorted_installed_names = installed_names.clone();
declared_names.sort();
sorted_installed_names.sort();
assert_eq!(report.reports.len(), 1);
assert!(report.reports[0].installed);
assert_eq!(
report.reports[0].granted_authorities,
AuthorityGrantSet::empty()
);
assert!(report.reports[0].skipped.is_empty());
assert!(report.reports[0].diagnostics.is_empty());
assert_eq!(declared_names, sorted_installed_names);
assert_eq!(
installed_names,
vec!["TaskCreate", "TaskList", "TaskGet", "TaskUpdate"]
);
assert_eq!(
pending_names,
vec!["TaskCreate", "TaskList", "TaskGet", "TaskUpdate"]
);
}
#[test]
fn builtin_task_feature_installs_through_worker_tool_path() {
let task_store = tools::TaskStore::new();
let mut worker = Worker::new(DummyClient);
let mut hook_builder = HookRegistryBuilder::default();
let report = FeatureRegistryBuilder::new()
.with_module(builtin::task_feature(task_store))
.with_module(builtin::task_tools_feature(task_store))
.install_into_worker(&mut worker, &mut hook_builder);
worker.tool_server_handle().flush_pending();

View File

@ -0,0 +1,9 @@
//! Built-in internal feature modules.
//!
//! These modules are compiled into the Pod host and contribute through the
//! same descriptor-approved registry path used by feature modules. They are not
//! an external plugin-loading surface.
pub mod task;
pub use task::task_tools_feature;

View File

@ -0,0 +1,61 @@
//! Task tools built-in feature module.
//!
//! This is the reference path for extracting an internal built-in module into
//! the feature contribution boundary. The Pod host still owns the Pod-lifetime
//! [`tools::TaskStore`] and passes the shared handle in at construction time;
//! the module requests no sandbox/external-plugin host authorities.
use crate::feature::{
FeatureDescriptor, FeatureInstallContext, FeatureInstallError, FeatureModule, ToolContribution,
ToolDeclaration,
};
/// Construct the built-in Task tool feature module.
///
/// The returned module contributes only `TaskCreate`, `TaskUpdate`, `TaskGet`,
/// and `TaskList` through descriptor-approved tool registration. It does not
/// request host authorities; normal ToolRegistry and PreToolCall permission
/// policy still applies at call time.
pub fn task_tools_feature(task_store: tools::TaskStore) -> impl FeatureModule {
TaskToolsFeature { task_store }
}
struct TaskToolsFeature {
task_store: tools::TaskStore,
}
impl FeatureModule for TaskToolsFeature {
fn descriptor(&self) -> FeatureDescriptor {
FeatureDescriptor::builtin("task-tools", "Task tools")
.with_description("Session-lifetime task tracking builtin tools")
.with_tool(ToolDeclaration::new(
"TaskCreate",
"Create a session-lifetime user-visible task",
))
.with_tool(ToolDeclaration::new(
"TaskUpdate",
"Update a session-lifetime user-visible task",
))
.with_tool(ToolDeclaration::new(
"TaskGet",
"Get one session-lifetime user-visible task",
))
.with_tool(ToolDeclaration::new(
"TaskList",
"List session-lifetime user-visible tasks",
))
}
fn install(&self, context: &mut FeatureInstallContext<'_>) -> Result<(), FeatureInstallError> {
let names = ["TaskCreate", "TaskList", "TaskGet", "TaskUpdate"];
for (name, definition) in names
.into_iter()
.zip(tools::task_tools(self.task_store.clone()))
{
context
.tools()
.register(ToolContribution::new(name, definition))?;
}
Ok(())
}
}