You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by wy...@apache.org on 2026/09/22 16:48:08 UTC
(maka) branch feat/runtime-host-rust updated: fix(plugins): restore ordinary roots from durable creation provenance
This is an automated email from the ASF dual-hosted git repository.
M4n5ter pushed a commit to branch feat/runtime-host-rust
in repository https://gitbox.apache.org/repos/asf/maka.git
The following commit(s) were added to refs/heads/feat/runtime-host-rust by this push:
new a4d71c8944 fix(plugins): restore ordinary roots from durable creation provenance
a4d71c8944 is described below
commit a4d71c89444dd2f9edb642732a59225f39673944
Author: Wang <m4...@gmail.com>
AuthorDate: Wed Sep 23 00:48:00 2026 +0800
fix(plugins): restore ordinary roots from durable creation provenance
Generated-by: Codex
---
crates/event-log/migrations/0001_runtime.sql | 5 +-
crates/event-log/src/sessions.rs | 37 ++++++-------
.../src/sessions/{manager.rs => origin.rs} | 23 ++++++--
.../{session_manager.rs => plugin_sessions.rs} | 61 +++++++++++++++++-----
crates/plugins/src/execution.rs | 2 +-
crates/runtime-host/src/execution/plugins.rs | 2 +-
crates/runtime-host/src/execution/plugins/root.rs | 48 +++++++++--------
.../tests/fixtures/background-plugin.mjs | 21 ++++++++
.../tests/integration/plugin_commands/boundary.rs | 49 ++++++++++++++++-
crates/workhub/src/assignment.rs | 16 +++++-
packages/plugin-sdk/README.md | 2 +-
packages/plugin-sdk/README.zh-CN.md | 2 +-
packages/plugin-sdk/src/execution.ts | 2 +-
13 files changed, 201 insertions(+), 69 deletions(-)
diff --git a/crates/event-log/migrations/0001_runtime.sql b/crates/event-log/migrations/0001_runtime.sql
index e264dc1df4..c7cb20ee86 100644
--- a/crates/event-log/migrations/0001_runtime.sql
+++ b/crates/event-log/migrations/0001_runtime.sql
@@ -301,11 +301,12 @@ CREATE TABLE model_request_compositions (
CREATE INDEX model_request_composition_digest ON model_request_compositions(digest);
-CREATE TABLE session_managers (
+CREATE TABLE plugin_sessions (
session_id TEXT PRIMARY KEY NOT NULL,
package_id TEXT NOT NULL,
scope_id TEXT NOT NULL,
- fingerprint TEXT NOT NULL
+ fingerprint TEXT NOT NULL,
+ managed INTEGER NOT NULL CHECK(managed IN (0, 1))
);
CREATE TABLE host_effects (
diff --git a/crates/event-log/src/sessions.rs b/crates/event-log/src/sessions.rs
index 1bf526179d..ac33a12cc3 100644
--- a/crates/event-log/src/sessions.rs
+++ b/crates/event-log/src/sessions.rs
@@ -21,9 +21,9 @@
mod activity;
mod execution;
-mod manager;
mod metadata;
-pub use manager::ManagedSession;
+mod origin;
+pub use origin::PluginSession;
pub(crate) mod read_state;
pub(crate) use activity::{initialize_execution, project_execution, register_functions};
pub(crate) use execution::advance_revision;
@@ -106,23 +106,23 @@ impl EventLog {
.await
}
- /// Manager ownership and Session creation share the same transaction.
+ /// Creator identity, optional management and Session creation share one transaction.
/// Only Host admission supplies the bound package/scope.
- pub async fn create_managed_session<T: Serialize + DeserializeOwned + Send + 'static>(
+ pub async fn create_plugin_session<T: Serialize + DeserializeOwned + Send + 'static>(
&self,
- claim: &ManagedSession,
+ claim: &PluginSession,
configuration: &T,
now: u64,
) -> Result<SessionRecord<T>, StoreError> {
- if claim.manager.scope() == &maka_plugins::composition::Scope::DesktopUi {
- return Err(invalid("Desktop UI cannot manage Host Sessions"));
+ if claim.creator.scope() == &maka_plugins::composition::Scope::DesktopUi {
+ return Err(invalid("Desktop UI cannot create Host Sessions"));
}
self.create_session_owned(
&claim.session_id,
&claim.fingerprint,
configuration,
now,
- Some(claim.manager.clone()),
+ Some((claim.creator.clone(), claim.managed)),
)
.await
}
@@ -133,7 +133,7 @@ impl EventLog {
fingerprint: &str,
configuration: &T,
now: u64,
- manager: Option<maka_plugins::storage::Namespace>,
+ origin: Option<(maka_plugins::storage::Namespace, bool)>,
) -> Result<SessionRecord<T>, StoreError> {
self.validate_root()?;
validate_id(id)?;
@@ -148,23 +148,24 @@ impl EventLog {
Box::pin(async move {
let mut tx = connection.begin_with("BEGIN IMMEDIATE").await?;
if let Some(record) = probe(&mut tx, &id, &fingerprint).await? {
- let stored: Option<(String, String)> = sqlx::query_as(
- "SELECT package_id, scope_id FROM session_managers WHERE session_id = ?",
+ let stored: Option<(String, String, bool)> = sqlx::query_as(
+ "SELECT package_id, scope_id, managed FROM plugin_sessions WHERE session_id = ?",
).bind(&id).fetch_optional(&mut *tx).await?;
- let requested = manager.as_ref().map(|owner| (
- owner.package().to_owned(), String::from(owner.scope().clone()),
+ let requested = origin.as_ref().map(|(owner, managed)| (
+ owner.package().to_owned(), String::from(owner.scope().clone()), *managed,
));
if stored != requested {
return Err(StoreError::SessionConflict);
}
return Ok(record);
}
- if let Some(manager) = manager {
- sqlx::query("INSERT INTO session_managers VALUES (?, ?, ?, ?)")
+ if let Some((creator, managed)) = origin {
+ sqlx::query("INSERT INTO plugin_sessions VALUES (?, ?, ?, ?, ?)")
.bind(&id)
- .bind(manager.package())
- .bind(String::from(manager.scope().clone()))
+ .bind(creator.package())
+ .bind(String::from(creator.scope().clone()))
.bind(&fingerprint)
+ .bind(managed)
.execute(&mut *tx)
.await?;
}
@@ -329,7 +330,7 @@ pub(crate) async fn insert(
return Err(invalid("session configuration exceeds 64 KiB"));
}
let reserved: Option<String> =
- sqlx::query_scalar("SELECT fingerprint FROM session_managers WHERE session_id = ?")
+ sqlx::query_scalar("SELECT fingerprint FROM plugin_sessions WHERE session_id = ?")
.bind(id)
.fetch_optional(&mut *tx)
.await?;
diff --git a/crates/event-log/src/sessions/manager.rs b/crates/event-log/src/sessions/origin.rs
similarity index 75%
rename from crates/event-log/src/sessions/manager.rs
rename to crates/event-log/src/sessions/origin.rs
index 369fd68c42..cededb8176 100644
--- a/crates/event-log/src/sessions/manager.rs
+++ b/crates/event-log/src/sessions/origin.rs
@@ -24,15 +24,28 @@ use sqlx::Row;
/// Host-bound creation identity, not a caller-provided authorization credential.
#[derive(Clone, Debug)]
-pub struct ManagedSession {
+pub struct PluginSession {
pub session_id: String,
- pub manager: Namespace,
- /// Stable identity of the managed creation request.
+ pub creator: Namespace,
+ /// Stable identity of the original creation request.
pub fingerprint: String,
+ pub managed: bool,
}
impl EventLog {
pub async fn session_manager(&self, session_id: &str) -> Result<Option<Namespace>, StoreError> {
+ self.session_plugin(session_id, true).await
+ }
+
+ pub async fn session_creator(&self, session_id: &str) -> Result<Option<Namespace>, StoreError> {
+ self.session_plugin(session_id, false).await
+ }
+
+ async fn session_plugin(
+ &self,
+ session_id: &str,
+ managed_only: bool,
+ ) -> Result<Option<Namespace>, StoreError> {
self.validate_root()?;
validate_id(session_id)?;
let session_id = session_id.to_owned();
@@ -40,9 +53,11 @@ impl EventLog {
.run(move |connection| {
Box::pin(async move {
sqlx::query(
- "SELECT package_id, scope_id FROM session_managers WHERE session_id = ?",
+ "SELECT package_id, scope_id FROM plugin_sessions
+ WHERE session_id = ? AND (NOT ? OR managed = 1)",
)
.bind(session_id)
+ .bind(managed_only)
.fetch_optional(connection)
.await?
.map(|row| {
diff --git a/crates/event-log/tests/session_manager.rs b/crates/event-log/tests/plugin_sessions.rs
similarity index 59%
rename from crates/event-log/tests/session_manager.rs
rename to crates/event-log/tests/plugin_sessions.rs
index e4735a0a19..eed42cf6ac 100644
--- a/crates/event-log/tests/session_manager.rs
+++ b/crates/event-log/tests/plugin_sessions.rs
@@ -17,27 +17,28 @@
* under the License.
*/
-use maka_event_log::{EventLog, StoreError, sessions::ManagedSession};
+use maka_event_log::{EventLog, StoreError, sessions::PluginSession};
use maka_plugins::{composition::Scope, storage::Namespace};
use serde_json::{Value, json};
#[tokio::test]
-async fn managed_creation_is_atomic_and_cannot_adopt_or_reassign_existing_sessions() {
+async fn plugin_creation_is_atomic_and_cannot_adopt_or_reassign_existing_sessions() {
let directory = tempfile::tempdir().unwrap();
let path = directory.path().join("events.sqlite");
- let claim = ManagedSession {
+ let claim = PluginSession {
session_id: "example-inbox".into(),
- manager: Namespace::new("example.workflow", Scope::Profile).unwrap(),
+ creator: Namespace::new("example.workflow", Scope::Profile).unwrap(),
fingerprint: "owned-creation".into(),
+ managed: true,
};
let log = EventLog::open(&path).await.unwrap();
let configuration = json!({"name":"inbox"});
let record = log
- .create_managed_session(&claim, &configuration, 1)
+ .create_plugin_session(&claim, &configuration, 1)
.await
.unwrap();
assert_eq!(
- log.create_managed_session(&claim, &configuration, 2)
+ log.create_plugin_session(&claim, &configuration, 2)
.await
.unwrap(),
record
@@ -47,20 +48,20 @@ async fn managed_creation_is_atomic_and_cannot_adopt_or_reassign_existing_sessio
.await,
Err(StoreError::SessionConflict)
));
- for manager in [
+ for creator in [
Namespace::new("other.workflow", Scope::Profile).unwrap(),
Namespace::new("example.workflow", Scope::Session("other".into())).unwrap(),
] {
- let wrong = ManagedSession {
- manager,
+ let wrong = PluginSession {
+ creator,
..claim.clone()
};
assert!(matches!(
- log.create_managed_session(&wrong, &configuration, 2).await,
+ log.create_plugin_session(&wrong, &configuration, 2).await,
Err(StoreError::SessionConflict)
));
}
- let ordinary = ManagedSession {
+ let ordinary = PluginSession {
session_id: "ordinary".into(),
..claim.clone()
};
@@ -68,16 +69,50 @@ async fn managed_creation_is_atomic_and_cannot_adopt_or_reassign_existing_sessio
.await
.unwrap();
assert!(matches!(
- log.create_managed_session(&ordinary, &configuration, 2)
+ log.create_plugin_session(&ordinary, &configuration, 2)
.await,
Err(StoreError::SessionConflict)
));
assert_eq!(log.session_manager("ordinary").await.unwrap(), None);
+ assert_eq!(log.session_creator("ordinary").await.unwrap(), None);
+ let delegated = PluginSession {
+ session_id: "delegated".into(),
+ managed: false,
+ ..claim.clone()
+ };
+ log.create_plugin_session(&delegated, &configuration, 2)
+ .await
+ .unwrap();
+ for conflicting in [
+ PluginSession {
+ managed: true,
+ ..delegated.clone()
+ },
+ PluginSession {
+ fingerprint: "changed".into(),
+ ..delegated.clone()
+ },
+ PluginSession {
+ creator: Namespace::new("other", Scope::Profile).unwrap(),
+ ..delegated.clone()
+ },
+ ] {
+ assert!(matches!(
+ log.create_plugin_session(&conflicting, &configuration, 3)
+ .await,
+ Err(StoreError::SessionConflict)
+ ));
+ }
log.close().await.unwrap();
let log = EventLog::open(&path).await.unwrap();
assert_eq!(
log.session_manager(&claim.session_id).await.unwrap(),
- Some(claim.manager)
+ Some(claim.creator.clone())
+ );
+ assert_eq!(log.session_manager("delegated").await.unwrap(), None);
+ assert_eq!(
+ log.session_creator("delegated").await.unwrap(),
+ Some(claim.creator)
);
assert_eq!(
log.get_session::<Value>(&claim.session_id).await.unwrap(),
diff --git a/crates/plugins/src/execution.rs b/crates/plugins/src/execution.rs
index fecd041a41..78b164c9b1 100644
--- a/crates/plugins/src/execution.rs
+++ b/crates/plugins/src/execution.rs
@@ -510,7 +510,7 @@ pub trait Commands: Send + Sync + std::any::Any {
&self,
request: CreateRoot,
) -> futures_util::future::BoxFuture<'_, Result<ChildSession, CommandError>>;
- /// Recover this namespace's managed root without replaying its creation settings.
+ /// Recover a root created by this namespace without replaying its creation settings.
/// Rechecks the current workspace and source ceilings. Absence is only an
/// observation: a concurrent creation may still commit with this operation ID.
fn restore_root(
diff --git a/crates/runtime-host/src/execution/plugins.rs b/crates/runtime-host/src/execution/plugins.rs
index 39d2a89622..6b20164d82 100644
--- a/crates/runtime-host/src/execution/plugins.rs
+++ b/crates/runtime-host/src/execution/plugins.rs
@@ -690,7 +690,7 @@ impl Commands for BoundCommands {
&self,
operation_id: String,
) -> BoxFuture<'_, Result<Option<ChildSession>, Error>> {
- Box::pin(self.restore_managed_root(operation_id))
+ Box::pin(self.restore_created_root(operation_id))
}
fn boundaries(&self) -> Result<Vec<maka_plugins::execution::SessionBoundary>, Error> {
let _lease = self.context.resource_call().map_err(|_| Error::Revoked)?;
diff --git a/crates/runtime-host/src/execution/plugins/root.rs b/crates/runtime-host/src/execution/plugins/root.rs
index 5484630746..7d8600a971 100644
--- a/crates/runtime-host/src/execution/plugins/root.rs
+++ b/crates/runtime-host/src/execution/plugins/root.rs
@@ -78,7 +78,7 @@ impl BoundCommands {
Ok(format!("plugin-root-{:x}", Sha256::digest(bytes)))
}
- pub(super) async fn restore_managed_root(
+ pub(super) async fn restore_created_root(
&self,
operation_id: String,
) -> Result<Option<ChildSession>, Error> {
@@ -107,7 +107,7 @@ impl BoundCommands {
};
if host
.log
- .session_manager(&id)
+ .session_creator(&id)
.await
.map_err(storage)?
.as_ref()
@@ -299,26 +299,20 @@ impl BoundCommands {
if submission_stop.is_cancelled() {
return Err(Error::Revoked);
}
- let record = if request.managed {
- worker
- .log
- .create_managed_session(
- &maka_event_log::sessions::ManagedSession {
- session_id: id.clone(),
- manager: namespace.clone(),
- fingerprint: fingerprint.clone(),
- },
- &expected,
- now()?,
- )
- .await
- } else {
- worker
- .log
- .create_session(&id, &fingerprint, &expected, now()?)
- .await
- }
- .map_err(storage)?;
+ let record = worker
+ .log
+ .create_plugin_session(
+ &maka_event_log::sessions::PluginSession {
+ session_id: id.clone(),
+ creator: namespace.clone(),
+ fingerprint: fingerprint.clone(),
+ managed: request.managed,
+ },
+ &expected,
+ now()?,
+ )
+ .await
+ .map_err(storage)?;
if worker.catalog.publish_session(&id).await.is_err() {
worker.begin_drain();
}
@@ -326,7 +320,15 @@ impl BoundCommands {
}
};
let manager = worker.log.session_manager(&id).await.map_err(storage)?;
- if manager.as_ref() != request.managed.then_some(&namespace) {
+ if manager.as_ref() != request.managed.then_some(&namespace)
+ || worker
+ .log
+ .session_creator(&id)
+ .await
+ .map_err(storage)?
+ .as_ref()
+ != Some(&namespace)
+ {
return Err(Error::Denied);
}
let current = &record.configuration;
diff --git a/crates/runtime-host/tests/fixtures/background-plugin.mjs b/crates/runtime-host/tests/fixtures/background-plugin.mjs
index f9c569edea..06e6b8d106 100644
--- a/crates/runtime-host/tests/fixtures/background-plugin.mjs
+++ b/crates/runtime-host/tests/fixtures/background-plugin.mjs
@@ -239,6 +239,27 @@ export default async function (ctx) {
throw new Error('managed root recovery lost its identity or configuration');
if ((await restoredCommands.restoreRoot('uncreated-root')) !== null)
throw new Error('managed root recovery created a Session');
+ const ordinaryRequest = { ...request, operationId: 'ordinary-root', managed: false };
+ const ordinary = await commands.createRoot(ordinaryRequest);
+ const ordinaryView = await commands.session(ordinary.sessionId);
+ const changed = await commands.configure({
+ sessionId: ordinary.sessionId,
+ expectedRevision: ordinaryView.revision,
+ target: {
+ kind: 'executor',
+ executorId: 'example.background',
+ settings: { model: 'replacement' },
+ },
+ });
+ if (changed.kind !== 'committed') throw new Error('ordinary root configuration failed');
+ const restoredOrdinary = await restoredCommands.restoreRoot('ordinary-root');
+ const restoredTarget = (await restoredCommands.session(ordinary.sessionId)).target;
+ if (
+ restoredOrdinary?.sessionId !== ordinary.sessionId ||
+ restoredTarget.kind !== 'executor' ||
+ restoredTarget.settings?.model !== 'replacement'
+ )
+ throw new Error('ordinary root recovery replayed old settings');
} finally {
await restoredCommands.close();
}
diff --git a/crates/runtime-host/tests/integration/plugin_commands/boundary.rs b/crates/runtime-host/tests/integration/plugin_commands/boundary.rs
index c175ebb3cf..df1d6a17b4 100644
--- a/crates/runtime-host/tests/integration/plugin_commands/boundary.rs
+++ b/crates/runtime-host/tests/integration/plugin_commands/boundary.rs
@@ -171,7 +171,7 @@ pub(super) async fn verify(
.authorize_plugin_root_execution(
fiber.context(),
RootApproval {
- template,
+ template: template.clone(),
source: None,
},
)
@@ -208,7 +208,7 @@ pub(super) async fn verify(
roots
.create_root(CreateRoot {
name: "different".into(),
- ..request
+ ..request.clone()
})
.await,
Err(CommandError::Conflict)
@@ -261,6 +261,51 @@ pub(super) async fn verify(
commands.session(root.session_id).await,
Err(CommandError::Denied)
));
+ let ordinary_request = CreateRoot {
+ managed: false,
+ operation_id: "ordinary-root".into(),
+ ..request
+ };
+ let ordinary = if reopened {
+ roots
+ .restore_root(ordinary_request.operation_id.clone())
+ .await
+ .unwrap()
+ .unwrap()
+ } else {
+ roots.create_root(ordinary_request.clone()).await.unwrap()
+ };
+ let ordinary_access = host
+ .authorize_plugin_execution(
+ foreign.context(),
+ std::slice::from_ref(&ordinary.session_id),
+ )
+ .await
+ .unwrap();
+ assert!(
+ ordinary_access
+ .session(ordinary.session_id.clone())
+ .await
+ .is_ok(),
+ "creation provenance must not impose exclusive management"
+ );
+ let fresh = host
+ .authorize_plugin_root_execution(
+ fiber.context(),
+ RootApproval {
+ template,
+ source: None,
+ },
+ )
+ .unwrap();
+ assert_eq!(
+ fresh
+ .restore_root(ordinary_request.operation_id)
+ .await
+ .unwrap(),
+ Some(ordinary)
+ );
+ assert_eq!(fresh.restore_root("absent".into()).await.unwrap(), None);
foreign
.shutdown(tokio::time::Instant::now() + Duration::from_secs(1))
.await
diff --git a/crates/workhub/src/assignment.rs b/crates/workhub/src/assignment.rs
index 1ce47d1080..97eec85b8b 100644
--- a/crates/workhub/src/assignment.rs
+++ b/crates/workhub/src/assignment.rs
@@ -92,6 +92,18 @@ impl Assignment {
}
}
+async fn restore_or_create_root(
+ commands: &dyn Commands,
+ request: &CreateRoot,
+) -> Result<String, Error> {
+ Ok(
+ match commands.restore_root(request.operation_id.clone()).await? {
+ Some(session) => session.session_id,
+ None => commands.create_root(request.clone()).await?.session_id,
+ },
+ )
+}
+
pub struct Assignments {
pub coordinator: Arc<crate::Coordinator>,
pub repository: Arc<Repository>,
@@ -150,7 +162,7 @@ impl Assignments {
let session = match &assignment.request.target {
Target::Existing { session_id } => session_id.clone(),
Target::Create { request } => {
- commands.create_root(*request.clone()).await?.session_id
+ restore_or_create_root(commands.as_ref(), request).await?
}
};
let mut content = assignment.request.content.clone();
@@ -249,7 +261,7 @@ impl Assignments {
match &assignment.request.target {
Target::Create { request } => {
// Reattach exactly the root this grant created, including after a restart.
- commands.create_root(*request.clone()).await?;
+ restore_or_create_root(commands.as_ref(), request).await?;
}
Target::Existing { session_id } => {
commands.session(session_id.clone()).await?;
diff --git a/packages/plugin-sdk/README.md b/packages/plugin-sdk/README.md
index 9df9b571d8..7848520bf4 100644
--- a/packages/plugin-sdk/README.md
+++ b/packages/plugin-sdk/README.md
@@ -124,7 +124,7 @@ Native endpoints accepting caller-supplied Host paths declare `Endpoint::requiri
`ctx.models.search({ query })` returns enabled chat choices with supported and default thinking levels, at most 50 entries / 48 KiB; refine the query when `complete` is false. `ctx.models.resolve({ kind: 'named', connectionSlug, model })` returns the same choice shape for an exact model; `{ kind: 'default' }` resolves the current default. Neither grants execution authority or promises provider readiness.
-`restoreRoot(operationId)` recovers this package/scope's managed root under current workspace and source ceilings, independently of its original model. `restoreChild` requires the original child creation request. Neither creates anything; absence does not exclude a concurrent creation. `configure` advances the Session revision on every committed choice, including identical values, fencing older configuration CASes without changing event history.
+`restoreRoot(operationId)` recovers a root created by this package/scope under current workspace and source ceilings, independently of its original model. Creation provenance does not make an ordinary root exclusively managed. `restoreChild` requires the original child creation request. Neither creates anything; absence does not exclude a concurrent creation. `configure` advances the Session revision on every committed choice, including identical values, fencing older configuration CASes [...]
## Model adapters
diff --git a/packages/plugin-sdk/README.zh-CN.md b/packages/plugin-sdk/README.zh-CN.md
index df2c299b0a..7c1ac21f49 100644
--- a/packages/plugin-sdk/README.zh-CN.md
+++ b/packages/plugin-sdk/README.zh-CN.md
@@ -128,7 +128,7 @@ Remote 回调可以抛出携带 `RemoteFailure.code` 的 `Error`。`outcome_unkn
`ctx.models.search({ query })` 返回已启用的聊天模型、支持的思考程度及默认值,每页最多 50 项/48 KiB;`complete` 为 false 时应缩小搜索范围。`ctx.models.resolve({ kind: 'named', connectionSlug, model })` 返回相同结构的精确模型选择,`{ kind: 'default' }` 解析当前默认模型。两者均不授予执行权限,也不保证提供商当前可用。
-`restoreRoot(operationId)` 按当前工作区和来源上限恢复本包/作用域托管的根会话,不依赖原模型。`restoreChild` 要求原始子会话创建请求。两者均不创建资源;不存在的观察不能排除并发创建。`configure` 每次成功选择都会推进 Session revision,包括相同值,以阻止较早的配置 CAS 覆盖它,不修改事件历史。
+`restoreRoot(operationId)` 按当前工作区和来源上限恢复本包/作用域创建的根会话,不依赖原模型;创建记录不会使普通根会话变成独占托管会话。`restoreChild` 要求原始子会话创建请求。两者均不创建资源;不存在的观察不能排除并发创建。`configure` 每次成功选择都会推进 Session revision,包括相同值,以阻止较早的配置 CAS 覆盖它,不修改事件历史。
## 模型适配器
diff --git a/packages/plugin-sdk/src/execution.ts b/packages/plugin-sdk/src/execution.ts
index 7a85673be3..351f2cc651 100644
--- a/packages/plugin-sdk/src/execution.ts
+++ b/packages/plugin-sdk/src/execution.ts
@@ -231,7 +231,7 @@ export interface Executions {
instructions?: string | null;
};
}): Promise<{ sessionId: string }>;
- /** Recover this package/scope's managed root under current workspace/source ceilings.
+ /** Recover a root created by this package/scope under current workspace/source ceilings.
* Never creates or reconfigures it; null does not rule out a concurrent creation.
*/
restoreRoot(operationId: string): Promise<{ sessionId: string } | null>;
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org