Skip to content
Closed
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
14 changes: 7 additions & 7 deletions docs/interactive-capabilities/capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "OpenBitFun Playbook",
"origin": "https://playbook.openbitfun.com",
"source": "src/shared/interactive-capabilities/catalog.json",
"digest": "d44141ce2d62548faf2f081b7319fc529b5ed791891a68cd6733dcd4341508c1",
"digest": "44e81d200d46b1ffb031427d4812fbcaa388aa556d82408fa78f8e6193fdcf22",
"ownerDigest": "df28e5eed6e3190741899ebf236bc5fb426d2469187e437839a349a7136c7c34",
"searchAcceptance": [
{
Expand Down Expand Up @@ -29795,8 +29795,8 @@
},
{
"id": "tool-presentation",
"titleZh": "选择模型使用原生模式还是 PTC TypeScript 模式",
"titleEn": "Choose native mode or PTC TypeScript mode for the model",
"titleZh": "选择模型使用原生模式还是 PTC TypeScript / Python 模式",
"titleEn": "Choose native mode or PTC TypeScript / Python mode for the model",
"destination": {
"kind": "settings",
"pageId": "tools.execution",
Expand Down Expand Up @@ -29992,8 +29992,8 @@
"id": "tool-presentation-mode",
"titleZh": "工具呈现模式",
"titleEn": "Tool presentation mode",
"descriptionZh": "选择模型直接调用原生工具,或通过 PTC TypeScript 调用工具。",
"descriptionEn": "Choose whether the model calls native tools directly or uses PTC TypeScript.",
"descriptionZh": "选择模型直接调用原生工具,或通过 PTC TypeScript / Python 调用工具。",
"descriptionEn": "Choose whether the model calls native tools directly or uses PTC TypeScript / Python.",
"valueSchema": {
"type": "string",
"enum": [
Expand Down Expand Up @@ -30129,8 +30129,8 @@
"Review, revoke, or clear remembered project permission grants and audit records",
"按需延迟加载工具定义以减少智能体初始上下文",
"Deferred tool loading on demand to reduce initial agent context",
"选择模型使用原生模式还是 PTC TypeScript 模式",
"Choose native mode or PTC TypeScript mode for the model",
"选择模型使用原生模式还是 PTC TypeScript / Python 模式",
"Choose native mode or PTC TypeScript / Python mode for the model",
"设置子智能体与 Swarm 最大并发及安全、强制并行或串行策略",
"Set subagent and swarm concurrency plus safe-only, forced-parallel, or serial policy",
"设置或取消单次工具调用超时",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Manage agent tool permissions, presentation mode, concurrency, timeouts, deferre
- Review, revoke, or clear remembered project permission grants and audit records
- **智能体可直接控制 / Direct Agent control** · 按需延迟加载工具定义以减少智能体初始上下文
- Deferred tool loading on demand to reduce initial agent context
- **智能体可直接控制 / Direct Agent control** · 选择模型使用原生模式还是 PTC TypeScript 模式
- Choose native mode or PTC TypeScript mode for the model
- **智能体可直接控制 / Direct Agent control** · 选择模型使用原生模式还是 PTC TypeScript / Python 模式
- Choose native mode or PTC TypeScript / Python mode for the model
- **智能体可直接控制 / Direct Agent control** · 设置子智能体与 Swarm 最大并发及安全、强制并行或串行策略
- Set subagent and swarm concurrency plus safe-only, forced-parallel, or serial policy
- **智能体可直接控制 / Direct Agent control** · 设置或取消单次工具调用超时
Expand Down Expand Up @@ -69,7 +69,7 @@ Manage agent tool permissions, presentation mode, concurrency, timeouts, deferre
| --- | --- | --- | --- |
| 显示权限模式选择器 / Show permission-mode selector | `boolean` | 在聊天输入框下方显示或隐藏权限模式快捷选择器。 | Show or hide the permission-mode shortcut below the chat composer. |
| 延迟加载工具 / Deferred tool loading | `boolean` | 按需加载大型工具定义,减少智能体初始上下文。 | Load large tool definitions on demand to reduce initial agent context. |
| 工具呈现模式 / Tool presentation mode | `native` / `ptc` | 选择模型直接调用原生工具,或通过 PTC TypeScript 调用工具。 | Choose whether the model calls native tools directly or uses PTC TypeScript. |
| 工具呈现模式 / Tool presentation mode | `native` / `ptc` | 选择模型直接调用原生工具,或通过 PTC TypeScript / Python 调用工具。 | Choose whether the model calls native tools directly or uses PTC TypeScript / Python. |
| 子智能体批量执行策略 / Subagent batch policy | `safe_only` / `force_parallel` / `serial` | 选择仅安全任务并行、强制并行或串行执行。 | Choose safe-only parallelism, forced parallelism, or serial execution. |
| 子智能体最大并发 / Maximum subagent concurrency | `integer` (1–32) | 限制同时运行的子智能体数量。 | Limit the number of subagents that may run at once. |
| Swarm 最大并发 / Maximum swarm concurrency | `integer` (1–64) | 限制 Swarm 中同时执行的工作单元。 | Limit concurrent work units inside a swarm. |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"generatedFrom": "src/shared/interactive-capabilities/catalog.json",
"catalogDigest": "d44141ce2d62548faf2f081b7319fc529b5ed791891a68cd6733dcd4341508c1",
"catalogDigest": "44e81d200d46b1ffb031427d4812fbcaa388aa556d82408fa78f8e6193fdcf22",
"count": 207,
"reasonCounts": {
"externalAuth": 4,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schemaVersion": 2,
"generatedFrom": "src/shared/interactive-capabilities/catalog.json",
"catalogDigest": "d44141ce2d62548faf2f081b7319fc529b5ed791891a68cd6733dcd4341508c1",
"catalogDigest": "44e81d200d46b1ffb031427d4812fbcaa388aa556d82408fa78f8e6193fdcf22",
"commandCount": 709,
"coverage": {
"commandCount": 709,
Expand Down
33 changes: 33 additions & 0 deletions src/apps/desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,39 @@
For development commands, see [AGENTS.md](AGENTS.md) and the repository
[contribution guide](../../../CONTRIBUTING.md).

## Programmatic tool calling (PTC)

In **Settings → Tools → Execution & Permissions**, select PTC tool presentation
for new sessions. `RunCode` accepts an async function body and an optional
`language`: `typescript` (the backward-compatible default) or `python`.
TypeScript needs Node.js 22.6+; Python needs Python 3.8+ and does not need Node.js.
Install the selected runtime on the execution host: the SSH workspace target,
peer host, or detached-dispatch target, not just the controller computer.
An unavailable runtime returns an explicit error without local fallback or
silently translating the source into another language.

For example, a Python `RunCode` request is:

```json
{
"language": "python",
"code": "import asyncio\nresults = await asyncio.gather(tools.Read({'file_path': 'a.py'}), tools.Read({'file_path': 'b.py'}))\nreturn results"
}
```

Use the actual argument names from the session's generated tool guide.
`tools.<name>(arguments)` and `tools["name"](arguments)` are awaitable; arguments
are dictionaries and JSON results are Python dictionaries/lists. Load deferred
tools with `await tools.getToolSpec("name")`, inspect the returned `input_schema`
and `output_schema`, then call `await tools.callTool("name", arguments)`.
`print` output is captured in `logs`; return a JSON-serializable value.

Both languages share tool permissions, execution deadlines, output limits and
process-tree cancellation. Pending nested calls are cancelled when the program
ends. These workers provide failure containment, **not an OS sandbox**:
Python imports and direct filesystem/network operations are not governed by
nested-tool permissions. Do not run untrusted code on this boundary.

## Model selection and smart routing

**Release availability:** smart mode is not available in this release. The GUI
Expand Down
8 changes: 8 additions & 0 deletions src/crates/assembly/core/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,14 @@ presentation, check both the native baseline and the optional PTC owner:
cargo test --locked -p openbitfun-core --no-default-features --features agent-runtime,git --lib agentic::tools::product_runtime::
cargo test --locked -p openbitfun-core --no-default-features --features ptc-runtime --lib agentic::tools::product_runtime::
```

For RunCode language validation, TypeScript/Python nested pipeline dispatch, and
PTC manifest/guide compatibility:

```bash
cargo test --locked -p openbitfun-core --no-default-features --features ptc-runtime --lib ptc_
```

For config-backed product-control option defaults, typed binding, and persisted readback:

```bash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl Tool for PtcRunCodeTool {
}

fn short_description(&self) -> String {
"Execute a TypeScript program with available tools".to_string()
"Execute a TypeScript or Python program with available tools".to_string()
}

fn input_schema(&self) -> Value {
Expand Down Expand Up @@ -87,6 +87,17 @@ impl Tool for PtcRunCodeTool {
input: &Value,
_context: Option<&ToolUseContext>,
) -> ValidationResult {
if input
.get("language")
.is_some_and(|language| !matches!(language.as_str(), Some("typescript" | "python")))
{
return ValidationResult {
result: false,
message: Some("RunCode language must be typescript or python".to_string()),
error_code: Some(400),
meta: None,
};
}
if input
.get("code")
.and_then(Value::as_str)
Expand All @@ -112,3 +123,29 @@ impl Tool for PtcRunCodeTool {
))
}
}

#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;

#[tokio::test]
async fn ptc_validates_python_and_legacy_typescript_inputs() {
let tool = PtcRunCodeTool::new();
for input in [
json!({"code": "return 1;"}),
json!({"code": "return 1;", "language": "typescript"}),
json!({"code": "return 1", "language": "python"}),
] {
assert!(tool.validate_input(&input, None).await.result);
}
for input in [
json!({"code": "return 1", "language": "ruby"}),
json!({"code": "return 1", "language": null}),
json!({"code": "return 1", "language": 1}),
json!({"code": " ", "language": "python"}),
] {
assert!(!tool.validate_input(&input, None).await.result);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ use openbitfun_runtime_ports::{
PtcToolCallHandler, PtcToolSpecRequest,
};
#[cfg(feature = "ptc-runtime")]
use openbitfun_services_integrations::ptc_runtime::NodePtcRuntime;
use openbitfun_services_integrations::ptc_runtime::{PtcScriptLanguage, ScriptPtcRuntime};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::{Instant, SystemTime};
Expand Down Expand Up @@ -889,8 +889,8 @@ impl PtcToolCallHandler for PtcPipelineToolCallHandler {
let typescript_sdk = render_ptc_loaded_tool_typescript_sdk(&PtcToolContract::new(
tool_name.clone(),
description,
input_schema,
output_schema,
input_schema.clone(),
output_schema.clone(),
));

let mut loaded = self.loaded_deferred_tool_specs.lock().await;
Expand All @@ -904,6 +904,8 @@ impl PtcToolCallHandler for PtcPipelineToolCallHandler {
}
Ok(serde_json::json!({
"typescript_sdk": typescript_sdk,
"input_schema": input_schema,
"output_schema": output_schema,
}))
}
}
Expand Down Expand Up @@ -2974,6 +2976,20 @@ impl ToolPipeline {
.effective_arguments()
.get("timeout_ms")
.and_then(serde_json::Value::as_u64);
let language = match task.effective_arguments().get("language") {
None => PtcScriptLanguage::TypeScript,
Some(serde_json::Value::String(language)) if language == "typescript" => {
PtcScriptLanguage::TypeScript
}
Some(serde_json::Value::String(language)) if language == "python" => {
PtcScriptLanguage::Python
}
_ => {
return Err(OpenBitFunError::Validation(
"RunCode language must be typescript or python".to_string(),
))
}
};
let runtime = if context.is_remote() {
#[cfg(feature = "ssh-remote")]
{
Expand Down Expand Up @@ -3002,7 +3018,7 @@ impl ToolPipeline {
"Remote SSH services are not initialized".to_string(),
)
})?;
NodePtcRuntime::for_remote(ssh_manager, connection_id)
ScriptPtcRuntime::for_remote(ssh_manager, connection_id)
}
#[cfg(not(feature = "ssh-remote"))]
{
Expand All @@ -3011,14 +3027,20 @@ impl ToolPipeline {
));
}
} else {
NodePtcRuntime::discover()
};
ScriptPtcRuntime::discover()
}
.with_language(language);
let operation_id = task.tool_call.tool_id.clone();
let nested_cancellation = context
.cancellation_token()
.map(CancellationToken::child_token)
.unwrap_or_default();
let _nested_cancellation_guard = nested_cancellation.clone().drop_guard();
let handler = Arc::new(PtcPipelineToolCallHandler {
pipeline: Arc::new(self.clone()),
context: task.context.clone(),
options: ToolExecutionOptions {
parent_cancellation_token: context.cancellation_token().cloned(),
parent_cancellation_token: Some(nested_cancellation),
..task.options.clone()
},
parent_tool_id: operation_id.clone(),
Expand Down Expand Up @@ -4362,6 +4384,17 @@ mod tests {
#[cfg(feature = "ptc-runtime")]
#[tokio::test]
async fn ptc_run_code_dispatches_nested_calls_through_the_pipeline() {
assert_ptc_pipeline_dispatch(None).await;
}

#[cfg(feature = "ptc-runtime")]
#[tokio::test]
async fn ptc_python_run_code_dispatches_nested_calls_through_the_pipeline() {
assert_ptc_pipeline_dispatch(Some("python")).await;
}

#[cfg(feature = "ptc-runtime")]
async fn assert_ptc_pipeline_dispatch(language: Option<&str>) {
let pipeline = test_tool_pipeline();
register_static_test_tool(&pipeline, "Read", json!({"content": "ok"}), 0).await;
let workspace_root = std::env::temp_dir().join("openbitfun-ptc-pipeline-test");
Expand All @@ -4374,14 +4407,22 @@ mod tests {
serde_json::to_string(&vec!["Read"]).expect("nested allowlist should serialize"),
);

let mut arguments = json!({
"code": if language == Some("python") {
"return await tools.Read({'path': 'src/lib.rs'})"
} else {
"return await tools.Read({ path: 'src/lib.rs' });"
}
});
if let Some(language) = language {
arguments["language"] = json!(language);
}
let results = pipeline
.execute_tools(
vec![ToolCall {
tool_id: "outer-ptc".to_string(),
tool_name: openbitfun_agent_tools::PTC_RUN_CODE_TOOL_NAME.to_string(),
arguments: json!({
"code": "return await tools.Read({ path: 'src/lib.rs' });"
}),
arguments,
..ToolCall::default()
}],
context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "OpenBitFun Playbook",
"origin": "https://playbook.openbitfun.com",
"source": "src/shared/interactive-capabilities/catalog.json",
"digest": "d44141ce2d62548faf2f081b7319fc529b5ed791891a68cd6733dcd4341508c1",
"digest": "44e81d200d46b1ffb031427d4812fbcaa388aa556d82408fa78f8e6193fdcf22",
"ownerDigest": "df28e5eed6e3190741899ebf236bc5fb426d2469187e437839a349a7136c7c34",
"searchAcceptance": [
{
Expand Down Expand Up @@ -30177,8 +30177,8 @@
},
{
"id": "tool-presentation",
"titleZh": "选择模型使用原生模式还是 PTC TypeScript 模式",
"titleEn": "Choose native mode or PTC TypeScript mode for the model",
"titleZh": "选择模型使用原生模式还是 PTC TypeScript / Python 模式",
"titleEn": "Choose native mode or PTC TypeScript / Python mode for the model",
"destination": {
"kind": "settings",
"pageId": "tools.execution",
Expand Down Expand Up @@ -30381,8 +30381,8 @@
"id": "tool-presentation-mode",
"titleZh": "工具呈现模式",
"titleEn": "Tool presentation mode",
"descriptionZh": "选择模型直接调用原生工具,或通过 PTC TypeScript 调用工具。",
"descriptionEn": "Choose whether the model calls native tools directly or uses PTC TypeScript.",
"descriptionZh": "选择模型直接调用原生工具,或通过 PTC TypeScript / Python 调用工具。",
"descriptionEn": "Choose whether the model calls native tools directly or uses PTC TypeScript / Python.",
"valueSchema": {
"type": "string",
"enum": [
Expand Down Expand Up @@ -30550,8 +30550,8 @@
"Review, revoke, or clear remembered project permission grants and audit records",
"按需延迟加载工具定义以减少智能体初始上下文",
"Deferred tool loading on demand to reduce initial agent context",
"选择模型使用原生模式还是 PTC TypeScript 模式",
"Choose native mode or PTC TypeScript mode for the model",
"选择模型使用原生模式还是 PTC TypeScript / Python 模式",
"Choose native mode or PTC TypeScript / Python mode for the model",
"设置子智能体与 Swarm 最大并发及安全、强制并行或串行策略",
"Set subagent and swarm concurrency plus safe-only, forced-parallel, or serial policy",
"设置或取消单次工具调用超时",
Expand Down
Loading
Loading