From c282e0aa7a04c952095705a461d396701387fc54 Mon Sep 17 00:00:00 2001 From: schickling-assistant <261620128+schickling-assistant@users.noreply.github.com> Date: Sat, 7 Nov 2026 10:00:21 +0100 Subject: [PATCH 1/2] fix(chatgpt): use Fractal adapter identity and telemetry agent-identity: agent/compoundingtech/st-chatgpt agent-persona: generalist agent-supervisor: unavailable agent-tool: OMP agent-tool-version: 18.12.5 agent-runtime: OMP 18.12.5 agent-model: openai-codex/gpt-6.1-sol --- crates/fractal-chatgpt/Cargo.toml | 3 +- crates/fractal-chatgpt/src/lib.rs | 6 ++-- crates/fractal-chatgpt/src/main.rs | 10 +++++- crates/fractal-chatgpt/src/tests.rs | 13 ++++++-- docs/site/reference/mcp-server.md | 31 +++++++++++++------ docs/st3/chatgpt-app.md | 12 +++---- docs/st3/client-v0/README.md | 6 ++-- .../fractal-chatgpt-surface-rename.json | 11 +++++++ 8 files changed, 66 insertions(+), 26 deletions(-) create mode 100644 release-notes/fractal-chatgpt-surface-rename.json diff --git a/crates/fractal-chatgpt/Cargo.toml b/crates/fractal-chatgpt/Cargo.toml index 85efd3dc54a..662086305d5 100644 --- a/crates/fractal-chatgpt/Cargo.toml +++ b/crates/fractal-chatgpt/Cargo.toml @@ -24,7 +24,8 @@ parking_lot = "0.12" name = "fractal-chatgpt" path = "src/main.rs" -# Keep the existing Nix package buildable until its package-name cutover. +# LIVE-MIGRATION BRIDGE arn:lmig:smalltalk:2026-11-01-fractal-chatgpt-alias — DELETE at contraction — https://github.com/compoundingtech/fractal/issues/3032 [[bin]] name = "st-chatgpt" path = "src/main.rs" +# LIVE-MIGRATION END arn:lmig:smalltalk:2026-11-01-fractal-chatgpt-alias diff --git a/crates/fractal-chatgpt/src/lib.rs b/crates/fractal-chatgpt/src/lib.rs index f50f4263f7c..9a4f2ce9ac8 100644 --- a/crates/fractal-chatgpt/src/lib.rs +++ b/crates/fractal-chatgpt/src/lib.rs @@ -235,7 +235,7 @@ impl CallObservation { } impl Drop for CallObservation { fn drop(&mut self) { - tracing::info!(target: "st_chatgpt::call", + tracing::info!(target: "fractal_chatgpt::call", tool = self.tool, outcome = self.outcome, code = self.code, daemon_request_id = self.request_id.as_deref(), duration_ms = self.started.elapsed().as_secs_f64() * 1000.0, @@ -287,7 +287,7 @@ async fn dispatch(adapter: &Adapter, name: &str, args: &Value, observation: &mut let query: Vec<_> = object.iter().map(|(k,v)| (k.as_str(),v.as_str().map(str::to_owned).unwrap_or_else(||v.to_string()))).collect(); adapter.client.get(url).query(&query) }; - let response = request.bearer_auth(&adapter.bearer).send().await.map_err(|_| json!({"code":"unavailable","message":"The paired st daemon is unavailable."}))?; + let response = request.bearer_auth(&adapter.bearer).send().await.map_err(|_| json!({"code":"unavailable","message":"The paired Fractal daemon is unavailable."}))?; let status = response.status(); // reqwest's JSON decoder already buffers the body. Reuse that exact buffer // for byte accounting, without reserializing content or trusting Content-Length. @@ -300,7 +300,7 @@ async fn dispatch(adapter: &Adapter, name: &str, args: &Value, observation: &mut impl ServerHandler for Adapter { fn get_info(&self) -> ServerConfig { ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) - .with_server_info(Implementation::new("st-chatgpt",env!("CARGO_PKG_VERSION"))) + .with_server_info(Implementation::new("fractal-chatgpt",env!("CARGO_PKG_VERSION"))) } async fn list_tools(&self, _:Option, _:RequestContext) -> Result { Ok(ListToolsResult { tools: tool_definitions(), ..Default::default() }) diff --git a/crates/fractal-chatgpt/src/main.rs b/crates/fractal-chatgpt/src/main.rs index 5177e5e668e..b5df7998c58 100644 --- a/crates/fractal-chatgpt/src/main.rs +++ b/crates/fractal-chatgpt/src/main.rs @@ -6,6 +6,7 @@ use std::{net::SocketAddr, path::PathBuf}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer}; #[derive(Parser)] +#[command(name = "fractal-chatgpt")] struct Args { #[arg(long)] socket: PathBuf, @@ -27,7 +28,14 @@ async fn main() -> Result<(), Box> { tracing_subscriber::registry() .with(tracing_subscriber::fmt::layer().with_ansi(false) .with_writer(std::io::stderr) - .with_filter(tracing_subscriber::filter::filter_fn(|metadata| metadata.target() == "st_chatgpt::call")) + .with_filter(tracing_subscriber::filter::filter_fn(|metadata| { + let permitted = metadata.target() == "fractal_chatgpt::call"; + // LIVE-MIGRATION BRIDGE arn:lmig:smalltalk:2026-11-07-fractal-chatgpt-telemetry-target — DELETE at contraction — https://github.com/compoundingtech/fractal/issues/3032 + // Deployments must enable both targets in RUST_LOG before upgrading. + let permitted = permitted || metadata.target() == "st_chatgpt::call"; + // LIVE-MIGRATION END arn:lmig:smalltalk:2026-11-07-fractal-chatgpt-telemetry-target + permitted + })) .with_filter(filter)) .try_init()?; if !args.listen.ip().is_loopback() { diff --git a/crates/fractal-chatgpt/src/tests.rs b/crates/fractal-chatgpt/src/tests.rs index c20a22e5cb1..e1c6f23a1b5 100644 --- a/crates/fractal-chatgpt/src/tests.rs +++ b/crates/fractal-chatgpt/src/tests.rs @@ -5,11 +5,16 @@ use tower::ServiceExt; fn disconnected_adapter() -> Adapter { Adapter { - client: reqwest::Client::builder().unix_socket("/nonexistent/st-chatgpt-tests/st3-client.sock").build().unwrap(), + client: reqwest::Client::builder().unix_socket("/nonexistent/fractal-chatgpt-tests/fractal-client.sock").build().unwrap(), bearer: "test-only-not-a-daemon-credential".into(), } } +#[test] +fn server_identity_uses_fractal_chatgpt() { + assert_eq!(disconnected_adapter().get_info().server_info.name, "fractal-chatgpt"); +} + #[test] fn adapter_accepts_only_paired_gateway_socket_names() { let dir = tempfile::tempdir().unwrap(); @@ -511,7 +516,7 @@ async fn dispatch_telemetry_is_correlated_and_content_free() { .with_writer(logs.clone()).finish(); let _capture = tracing::subscriber::set_default(subscriber); let dir = tempfile::tempdir().unwrap(); - let socket = dir.path().join("st3-client.sock"); + let socket = dir.path().join("fractal-client.sock"); let listener = tokio::net::UnixListener::bind(&socket).unwrap(); let id = "request/12345678-1234-1234-1234-123456789abc"; let body = json!({"request_id":id,"value":{"content":"RESPONSE_CONTENT_SENTINEL"}}).to_string(); @@ -537,6 +542,8 @@ async fn dispatch_telemetry_is_correlated_and_content_free() { assert_eq!(call(&adapter, "fractal_alert_get", &json!({})).await.unwrap_err()["code"], "invalid-arguments"); let text = logs.text(); assert_eq!(text.matches("ChatGPT tool call finished").count(), 4, "{text}"); + assert_eq!(text.matches("fractal_chatgpt::call").count(), 4, "{text}"); + assert!(!text.contains("st_chatgpt::call"), "{text}"); for field in ["tool=\"fractal_alerts_list\"", "outcome=\"success\"", "code=\"ok\"", "outcome=\"error\"", "code=\"daemon-denied\"", "code=\"invalid-arguments\"", "tool=\"unknown\"", "duration_ms=", id, &format!("response_bytes={expected_bytes}")] { @@ -554,7 +561,7 @@ async fn dispatch_telemetry_records_decode_transport_and_cancelled_outcomes() { .with_writer(logs.clone()).finish(); let _capture = tracing::subscriber::set_default(subscriber); let dir = tempfile::tempdir().unwrap(); - let socket = dir.path().join("st3-client.sock"); + let socket = dir.path().join("fractal-client.sock"); let listener = tokio::net::UnixListener::bind(&socket).unwrap(); let entered = std::sync::Arc::new(tokio::sync::Notify::new()); let notify = entered.clone(); diff --git a/docs/site/reference/mcp-server.md b/docs/site/reference/mcp-server.md index 1fb3d6d6f28..fdae1782734 100644 --- a/docs/site/reference/mcp-server.md +++ b/docs/site/reference/mcp-server.md @@ -8,7 +8,7 @@ order: 6 The Fractal MCP server connects an MCP client to the daemon's paired-client gateway. It exposes a closed set of tools for alerts, missions, agents, conversations, and explicitly scoped writes. It does not expose daemon control or give an assistant person authority. -The Rust package is `fractal-chatgpt`; its binary is `fractal-chatgpt` (`st-chatgpt` kept as an alias binary). The supported assistant principal is `assistant/chatgpt`, including when a generic MCP client uses the adapter. Pairing requires the configured owning person's explicit consent to disclose assistant reads to OpenAI. Do not use a person credential for this server. +The Rust package and binary are `fractal-chatgpt`. The supported assistant principal is `assistant/chatgpt`, including when a generic MCP client uses the adapter. Pairing requires the configured owning person's explicit consent to disclose assistant reads to OpenAI. Do not use a person credential for this server. The [external-assistant contract](https://github.com/compoundingtech/fractal/blob/main/docs/st3/external-assistants.md) defines the authority and wire behavior. Remote access is a restricted dogfood path, not an authenticated public service. @@ -19,16 +19,29 @@ Install and configure [Fractal](../getting-started/install.md) first. The daemon Build the adapter from the repository root with Rust and the repository's build prerequisites: ```sh -cargo build --release -p fractal-chatgpt --bin st-chatgpt +cargo build --release -p fractal-chatgpt --bin fractal-chatgpt ``` -The binary is `target/release/st-chatgpt`. The source package also provides the canonical `fractal-chatgpt` binary via `--bin fractal-chatgpt`; the examples here use `st-chatgpt`, which both installation methods provide. Alternatively, obtain the current Nix package: +The binary is `target/release/fractal-chatgpt`. Alternatively, obtain the current Nix package: ```sh -nix build github:compoundingtech/fractal#st-chatgpt +nix build github:compoundingtech/fractal#fractal-chatgpt ``` -That binary is `result/bin/st-chatgpt`. Use a matching Fractal revision. The shell examples below assume `st-chatgpt` is on `PATH`; in the MCP client configuration, use the absolute path to the binary produced by your chosen installation method. +That binary is `result/bin/fractal-chatgpt`. Use a matching Fractal revision. The shell examples below assume `fractal-chatgpt` is on `PATH`; in the MCP client configuration, use the absolute path to the binary produced by your chosen installation method. + + +The legacy executable `st-chatgpt` and Nix attribute `packages.st-chatgpt` remain bridged aliases for existing deployments. New installations use `fractal-chatgpt`. + + +Call telemetry uses the `fractal_chatgpt::call` target and is disabled by default. Set +`RUST_LOG=off,fractal_chatgpt::call=info` to enable content-free call summaries on stderr. + +During a mixed-version rollout, set +`RUST_LOG=off,fractal_chatgpt::call=info,st_chatgpt::call=info` before upgrading. +The hard allowlist accepts both targets, but this release emits only `fractal_chatgpt::call`; +an old-target-only environment filter does not enable the renamed events. + ## Pair and prepare a grant @@ -83,7 +96,7 @@ CLI pairing selects `caller-bound-allow`: reads within the sealed scopes, consen Use stdio when the MCP client launches a local process: ```sh -st-chatgpt --transport stdio \ +fractal-chatgpt --transport stdio \ --socket /PATH/TO/fractal-client.sock \ --credential-file /PATH/TO/mcp-private/credential ``` @@ -99,7 +112,7 @@ A generic MCP client configuration looks like this; adapt the outer configuratio { "mcpServers": { "fractal": { - "command": "/PATH/TO/st-chatgpt", + "command": "/PATH/TO/fractal-chatgpt", "args": [ "--transport", "stdio", "--socket", "/PATH/TO/fractal-client.sock", @@ -115,7 +128,7 @@ A generic MCP client configuration looks like this; adapt the outer configuratio HTTP is the default transport. The default listen address is `127.0.0.1:8787`: ```sh -st-chatgpt --transport http --listen 127.0.0.1:8787 \ +fractal-chatgpt --transport http --listen 127.0.0.1:8787 \ --socket /PATH/TO/fractal-client.sock \ --credential-file /PATH/TO/mcp-private/credential ``` @@ -150,7 +163,7 @@ The table summarizes the descriptions published by the adapter. A scope listed h | `fractal_conversation_read` | `read.assistant.conversation` | Read | Read one bounded revision-pinned conversation content range. | | `fractal_message_send` | `write.assistant.messages` | Write | Send one exact message to any existing agent; person recipients are refused. | -The corresponding `st_*` tool names are hidden, deprecated aliases for cached clients and are removed in `fractal-chatgpt` 0.2.0. Refresh client tool discovery and use `fractal_*`; aliases do not grant additional authority. This tool-name deprecation is separate from the `st-chatgpt` alias binary. +The corresponding `st_*` tool names are hidden, deprecated aliases for cached clients and are removed in `fractal-chatgpt` 0.2.0. Refresh client tool discovery and use `fractal_*`; aliases do not grant additional authority. This tool-name deprecation is separate from the bridged executable alias. ## Limits and grant lifecycle diff --git a/docs/st3/chatgpt-app.md b/docs/st3/chatgpt-app.md index 222dfc1704b..6f44d520d50 100644 --- a/docs/st3/chatgpt-app.md +++ b/docs/st3/chatgpt-app.md @@ -1,8 +1,8 @@ # ChatGPT app: composition and install/use -Design for [#2172](https://github.com/compoundingtech/smalltalk/issues/2172), building on the assistant -principal and grants in [#2290](https://github.com/compoundingtech/smalltalk/pull/2290) and the assistant -routes in [#2541](https://github.com/compoundingtech/smalltalk/pull/2541). This is a standalone Fractal app. +Design for [#2172](https://github.com/compoundingtech/fractal/issues/2172), building on the assistant +principal and grants in [#2290](https://github.com/compoundingtech/fractal/pull/2290) and the assistant +routes in [#2541](https://github.com/compoundingtech/fractal/pull/2541). This is a standalone Fractal app. ## Composition @@ -13,7 +13,7 @@ ChatGPT web (developer-mode app) ChatGPT desktop (local plugin) │ │ MCP Streamable HTTP │ └───────────────┬─────────────────────┘ - st-chatgpt adapter + fractal-chatgpt adapter │ typed client-v0 assistant operations paired Fractal gateway │ @@ -24,7 +24,7 @@ ChatGPT web (developer-mode app) ChatGPT desktop (local plugin) | --- | --- | --- | | fractal daemon (`crates/st3`) | Principal, grants, scopes, owner approvals, fences and action receipts | Speak MCP or expose the control socket to ChatGPT | | Typed client (`crates/st3-client`) | Client-v0 assistant operations | Widen authority | -| Adapter (`crates/st-chatgpt`) | Both MCP transports, closed tool list, annotations and pluggable authentication | Run arbitrary commands, expose `fractal.sock` or return credentials | +| Adapter (`crates/fractal-chatgpt`) | Both MCP transports, closed tool list, annotations and pluggable authentication | Run arbitrary commands, expose `fractal.sock` or return credentials | | ChatGPT app or plugin record | Connection to the adapter | Hold Fractal credentials | | Downstream deployment | Adapter and tunnel services for one installation | Fork the authority model or tool list | @@ -75,7 +75,7 @@ For an example owner, `person/ada`: 2. For web, create a Secure MCP Tunnel in the owner's OpenAI Platform organization, associate it with the ChatGPT workspace, and run its tunnel client against the adapter's HTTP endpoint. The owner creates the developer-mode ChatGPT app record and selects the tunnel. -3. For desktop, configure the local plugin to launch `st-chatgpt` with the stdio transport in a Work/Codex +3. For desktop, configure the local plugin to launch `fractal-chatgpt` with the stdio transport in a Work/Codex thread. It uses the same authority model and tool list as HTTP. 4. Ask what needs attention, read the bounded alert or transcript, or send a message to an allowlisted agent. For a person-ask answer, request the exact option, approve the action in Fractal, then execute it once. diff --git a/docs/st3/client-v0/README.md b/docs/st3/client-v0/README.md index 48a9189c468..92025876269 100644 --- a/docs/st3/client-v0/README.md +++ b/docs/st3/client-v0/README.md @@ -1883,9 +1883,9 @@ frontend-auth boundary and does not alter tool arguments or daemon grants. No ge runner, daemon-control socket, or terminal scraping is exposed. `GET /mcp` is 405; unknown paths return an empty 404. -The flake's `packages.st-chatgpt` builds only the adapter executable with the shared locked -Cargo dependencies. Run `nix build .#st-chatgpt` from a checkout, or -`nix build github:compoundingtech/fractal/#st-chatgpt` at a published commit. +The flake's `packages.fractal-chatgpt` builds only the adapter executable with the shared locked +Cargo dependencies. Run `nix build .#fractal-chatgpt` from a checkout, or +`nix build github:compoundingtech/fractal/#fractal-chatgpt` at a published commit. The shared `checks.st3` suite includes the adapter's Rust tests. Rerun the scratch exercise (no live daemon or ChatGPT application is touched): diff --git a/release-notes/fractal-chatgpt-surface-rename.json b/release-notes/fractal-chatgpt-surface-rename.json new file mode 100644 index 00000000000..60db8568846 --- /dev/null +++ b/release-notes/fractal-chatgpt-surface-rename.json @@ -0,0 +1,11 @@ +{ + "version": 1, + "summary": "Use fractal-chatgpt for MCP server identity, adapter documentation, and call telemetry.", + "replay": {"status": "none", "detail": "No stored history changes."}, + "schema": {"status": "none", "detail": "No database or daemon wire schema changes."}, + "rules": {"status": "none", "detail": "No coordination rules change."}, + "compatibility": {"status": "changed", "detail": "MCP serverInfo and CLI help identify fractal-chatgpt. Call events emit only fractal_chatgpt::call; the hard allowlist temporarily accepts st_chatgpt::call too. The legacy st-chatgpt executable remains a bridged alias. Cached tool aliases and paired-socket compatibility are unchanged."}, + "downtime": {"status": "none", "detail": "No daemon downtime required."}, + "manual": {"status": "required", "detail": "Before upgrading, configure mixed-version deployments with RUST_LOG=off,fractal_chatgpt::call=info,st_chatgpt::call=info. An old-target-only filter does not enable new-target events. New installations use packages.fractal-chatgpt and bin/fractal-chatgpt."}, + "recovery": {"status": "none", "detail": "The dual-target environment filter supports the previous adapter binary on rollback."} +} From 7f3d4ac6edad7789c5a8d0c1762b2ed90957a70d Mon Sep 17 00:00:00 2001 From: schickling-assistant <261620128+schickling-assistant@users.noreply.github.com> Date: Sat, 7 Nov 2026 10:09:48 +0100 Subject: [PATCH 2/2] fix(chatgpt): validate release impact and cover telemetry allowlist agent-identity: agent/compoundingtech/st-chatgpt agent-persona: generalist agent-supervisor: unavailable agent-tool: OMP agent-tool-version: 18.12.5 agent-runtime: OMP 18.12.5 agent-model: openai-codex/gpt-6.1-sol --- crates/fractal-chatgpt/src/main.rs | 25 ++++++++++++++----- .../fractal-chatgpt-surface-rename.json | 2 +- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/crates/fractal-chatgpt/src/main.rs b/crates/fractal-chatgpt/src/main.rs index b5df7998c58..8dbcaafbd8a 100644 --- a/crates/fractal-chatgpt/src/main.rs +++ b/crates/fractal-chatgpt/src/main.rs @@ -18,6 +18,24 @@ struct Args { listen: SocketAddr, } +fn is_call_telemetry_target(target: &str) -> bool { + let permitted = target == "fractal_chatgpt::call"; + // LIVE-MIGRATION BRIDGE arn:lmig:smalltalk:2026-11-07-fractal-chatgpt-telemetry-target — DELETE at contraction — https://github.com/compoundingtech/fractal/issues/3032 + // Deployments must enable both targets in RUST_LOG before upgrading. + let permitted = permitted || target == "st_chatgpt::call"; + // LIVE-MIGRATION END arn:lmig:smalltalk:2026-11-07-fractal-chatgpt-telemetry-target + permitted +} + +#[test] +fn call_telemetry_target_allowlist() { + assert!(is_call_telemetry_target("fractal_chatgpt::call")); + // LIVE-MIGRATION BRIDGE arn:lmig:smalltalk:2026-11-07-fractal-chatgpt-telemetry-target — DELETE at contraction — https://github.com/compoundingtech/fractal/issues/3032 + assert!(is_call_telemetry_target("st_chatgpt::call")); + // LIVE-MIGRATION END arn:lmig:smalltalk:2026-11-07-fractal-chatgpt-telemetry-target + assert!(!is_call_telemetry_target("reqwest::connect")); +} + #[tokio::main] async fn main() -> Result<(), Box> { let args = Args::parse(); @@ -29,12 +47,7 @@ async fn main() -> Result<(), Box> { .with(tracing_subscriber::fmt::layer().with_ansi(false) .with_writer(std::io::stderr) .with_filter(tracing_subscriber::filter::filter_fn(|metadata| { - let permitted = metadata.target() == "fractal_chatgpt::call"; - // LIVE-MIGRATION BRIDGE arn:lmig:smalltalk:2026-11-07-fractal-chatgpt-telemetry-target — DELETE at contraction — https://github.com/compoundingtech/fractal/issues/3032 - // Deployments must enable both targets in RUST_LOG before upgrading. - let permitted = permitted || metadata.target() == "st_chatgpt::call"; - // LIVE-MIGRATION END arn:lmig:smalltalk:2026-11-07-fractal-chatgpt-telemetry-target - permitted + is_call_telemetry_target(metadata.target()) })) .with_filter(filter)) .try_init()?; diff --git a/release-notes/fractal-chatgpt-surface-rename.json b/release-notes/fractal-chatgpt-surface-rename.json index 60db8568846..501a40308c4 100644 --- a/release-notes/fractal-chatgpt-surface-rename.json +++ b/release-notes/fractal-chatgpt-surface-rename.json @@ -6,6 +6,6 @@ "rules": {"status": "none", "detail": "No coordination rules change."}, "compatibility": {"status": "changed", "detail": "MCP serverInfo and CLI help identify fractal-chatgpt. Call events emit only fractal_chatgpt::call; the hard allowlist temporarily accepts st_chatgpt::call too. The legacy st-chatgpt executable remains a bridged alias. Cached tool aliases and paired-socket compatibility are unchanged."}, "downtime": {"status": "none", "detail": "No daemon downtime required."}, - "manual": {"status": "required", "detail": "Before upgrading, configure mixed-version deployments with RUST_LOG=off,fractal_chatgpt::call=info,st_chatgpt::call=info. An old-target-only filter does not enable new-target events. New installations use packages.fractal-chatgpt and bin/fractal-chatgpt."}, + "manual": {"status": "changed", "detail": "Before upgrading, configure mixed-version deployments with RUST_LOG=off,fractal_chatgpt::call=info,st_chatgpt::call=info. An old-target-only filter does not enable new-target events. New installations use packages.fractal-chatgpt and bin/fractal-chatgpt."}, "recovery": {"status": "none", "detail": "The dual-target environment filter supports the previous adapter binary on rollback."} }