docs: consolidate design documentation and standardize crate English
This commit is contained in:
+7
-103
@@ -1,107 +1,11 @@
|
||||
# worker
|
||||
|
||||
## Role
|
||||
`worker` builds an `agen` Engine from a resolved Manifest and connects Worker input handling, Feature registration, Session persistence, and protocol handling. It uses scoped capabilities supplied by the Host rather than owning Workspace business records or managing physical Workdirs.
|
||||
|
||||
`worker` turns an `agen` Engine into a named runtime entity with manifest configuration, scoped tools, session persistence, protocol handling, and Worker metadata integration.
|
||||
## Main entry points
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- Worker lifecycle and socket protocol serving
|
||||
- Engine construction around a resolved Manifest
|
||||
- session-store and session-store worker metadata coordination
|
||||
- built-in tool registration under scope/policy
|
||||
- spawned-child orchestration hooks
|
||||
|
||||
Does not own:
|
||||
|
||||
- provider-specific wire formats (`provider` / `agen` clients)
|
||||
- product CLI parsing (`yoi`)
|
||||
- TUI display authority (`tui`)
|
||||
- current-state storage schema outside Worker metadata (`session-store` worker metadata)
|
||||
|
||||
## Design notes
|
||||
|
||||
A Worker is runtime authority, not UI state. It should commit model-visible events through history/session paths and keep current Worker-name state in Worker metadata rather than in transient runtime files.
|
||||
|
||||
## Workspace config WIP Feature
|
||||
|
||||
Enable the opt-in surface in a Worker profile/manifest:
|
||||
|
||||
```toml
|
||||
[feature.workspace_config]
|
||||
enabled = true
|
||||
```
|
||||
|
||||
This setting accepts **only** `enabled` and defaults to false. Controller
|
||||
registration additionally requires Workspace identity, an available injected
|
||||
Workspace Client and WIP mode. It creates no grant. An owner must explicitly
|
||||
create a Backend grant bound to Workspace/Runtime/Worker and read-only or
|
||||
read-write access; standalone/Tools-mode config compatibility tools do not exist.
|
||||
|
||||
`feature::builtin::workspace_config` registers `/workspace-config` through the
|
||||
common structured invocation registry (T696). Its optional `access` argument is
|
||||
`effective` (default), `read_only`, or `read_write`; explicit write never silently
|
||||
downgrades. Feature preparation attaches before the request reaches the assistant
|
||||
and reports the logical target and effective access. Native self-attach uses the
|
||||
same `WorkspaceConfigFeature::attach` path. Backend alias attachment is idempotent
|
||||
under the session lock; neither entry point blindly retries uncertain outcomes.
|
||||
|
||||
The canonical alias is `workspace-config`; the content entrance is WIP
|
||||
`/workspace-config`. Attachment identity and conditional detach use the existing
|
||||
T697 Backend ledger. No OS mount, Workdir session, Bash capability, local config
|
||||
copy or second attachment inventory is created. The registry source is logical,
|
||||
not a Runtime filesystem target.
|
||||
|
||||
The request-time provider maps `/workspace-config` to the Backend's empty root
|
||||
path and descendants to canonical relative source paths, for example
|
||||
`/workspace-config/main.dcdl` -> `main.dcdl`. Observations return structure,
|
||||
Backend validators and current operations, **not bodies**. Files publish `read`;
|
||||
read-write grants additionally publish `write`, unique-substring `edit`, and
|
||||
`delete`. Directories and explicit absence observations publish `create` where
|
||||
Backend metadata permits it. The root publishes `apply_changes` for atomic
|
||||
create/update/delete/rename sets, allowing new imported
|
||||
sources and their references to be saved together. Empty directory creation,
|
||||
commands and generic POSIX operations are not exposed. The adapter uses the typed
|
||||
`workers/self/workspace-config` GET/attach/observe/read/commit API; it never calls
|
||||
an owner config API as a bypass.
|
||||
|
||||
Client-managed PATH validators are compared against freshly resolved Host
|
||||
metadata. Each mutation captures the ROOT validator, whole-tree content digest,
|
||||
canonical entrypoints and node state from that same observation. Backend resolves
|
||||
the live grant/link and validates canonical changes/CAS under its execution lock.
|
||||
No operation accepts LLM-supplied validators or `expected_digest` fields. Returned
|
||||
post-call path validators are refreshed from the exact committed content, not
|
||||
substituted with root validators or later unrelated state. Content changes and
|
||||
reattachment to a new connection lifetime reject stale calls, even when the new
|
||||
attachment exposes the same bytes. Old descriptors cannot
|
||||
bypass read-only restrictions, detach, revocation or Workspace boundaries.
|
||||
|
||||
Bounds: depth 0..8, 256 observed nodes/changes, 512-byte relative paths, 256 KiB
|
||||
aggregate changed text/read content, and 30-second retrieval/operation deadlines.
|
||||
Path traversal and malformed parameters fail before content calls. Typed
|
||||
`NotCommitted` failures remain distinct from `Unknown`; timeouts/lost mutation
|
||||
responses are unknown and never automatically retried. Raw transport/provider
|
||||
errors are not copied into model/UI output. Retrieval protocol failures are
|
||||
reported as failures after recording their Client cache state, not as fresh
|
||||
successful observations. Saving commits the canonical Workspace configuration
|
||||
and existing projection/activation pipeline; it does **not** rewrite the resolved
|
||||
manifests of already-running Workers.
|
||||
|
||||
Public integration seams: `WorkspaceConfigFeature::for_workspace`,
|
||||
`workspace_config::wip::mount_workspace_config_wip`, and
|
||||
`WipRuntime::from_mounts` plus `tree`/`inspect`/`invoke`. Worker tests exercise the
|
||||
real Feature registry, native mount and WIP Client/Host through a typed in-process
|
||||
contract router: deep discovery, slash/native attach, read/edit/write,
|
||||
create/delete, atomic multi-source updates, grant restrictions/revocation,
|
||||
detach, cross-Workspace isolation, concurrent/same-bytes stale observations,
|
||||
invalid/oversize/save/unknown failures and no retries. Those contract-router tests
|
||||
are not a substitute for Backend canonical persistence, semantic validation,
|
||||
projection activation or full Web-to-Backend acceptance tests.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/worker-session-state.md`](../../docs/design/worker-session-state.md)
|
||||
- [`../../docs/design/context-history.md`](../../docs/design/context-history.md)
|
||||
- [`../../docs/design/tool-permissions-scope.md`](../../docs/design/tool-permissions-scope.md)
|
||||
- [`Worker`](src/worker.rs): Input admission, execution, and Session restoration.
|
||||
- [`controller`](src/controller.rs): Worker control and protocol intake.
|
||||
- [`feature`](src/feature.rs): Feature declarations, registration, and structured invocation.
|
||||
- [`wip`](src/wip.rs): WIP namespaces, native/compatibility projections, and the Client/Host boundary.
|
||||
- [`spawn`](src/spawn/mod.rs): Delegation and control of parent-owned Internal SubWorkers.
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
//! Provider-owned request-time checkout projection. No filesystem path is opened
|
||||
//! by the Worker: every observation and operation goes through the live router.
|
||||
//! Provider-owned, request-time checkout projection entered through current attachments.
|
||||
//!
|
||||
//! Worker delegates observation and execution to the live router and WorkdirSession without opening
|
||||
//! host pathnames. This surface is separate from Repository/Workdir inventory; it neither copies
|
||||
//! file trees nor maintains resident registrations. Only `/checkouts` and attachment entrances are
|
||||
//! indexable; descendants are merely addressable. Deeper observation does not List content, and
|
||||
//! empty children mean "no indexable children", not "empty directory". List/Glob/Grep discover
|
||||
//! content; Inspect checks selected coordinates.
|
||||
//!
|
||||
//! Interfaces/validators bind the projection incarnation, attachment generation, and provider state
|
||||
//! so alias reuse after restore or detach cannot transfer old observations to a new connection.
|
||||
//! Publication also checks the generation recorded at acquisition. Possessing an Interface grants
|
||||
//! no authority: route-reconstructed permission inputs and current provider capability/scope are
|
||||
//! rechecked, as are the existing Write/Edit prior-Read and content-hash preconditions.
|
||||
//!
|
||||
//! Search-result entry coordinates come from typed provider paths without reobserving every result,
|
||||
//! so later disappearance does not undo successful discovery. Losing the connection after a
|
||||
//! successful Write/Edit/Create, or exceeding a mutation deadline, can leave the outcome unknown.
|
||||
//! Interrupting a future does not roll back side effects.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
//! Prune integration — wires the Engine's prune projection to the Worker's
|
||||
//! usage-history-backed token accounting.
|
||||
//!
|
||||
//! Engine 自身がコンテキスト射影を行う(`worker.rs` の `request_context` 構築
|
||||
//! 直後)。Engine は usage 履歴を知らないので、`min_savings` 判定に使う savings
|
||||
//! の見積もりはコールバックで外部から注入する。このモジュールはそのコールバック
|
||||
//! を組み立てて Engine に差し込むための `impl Worker` を提供する。
|
||||
//! The Engine projects context itself, immediately after constructing `request_context` in
|
||||
//! `worker.rs`. Since it has no usage history, a callback supplies the savings estimate used
|
||||
//! to evaluate `min_savings`. This module provides `impl Worker` to assemble and install it.
|
||||
//!
|
||||
//! 同じ経路で `PruneObserver` も install し、評価のたびに `prune.fire` /
|
||||
//! `prune.skip` metric を `MetricsTracker` に積む。`Fired` 時は uuid を
|
||||
//! `UsageTracker` にも stash しておき、後続の `LlmUsage` と組で
|
||||
//! `prune.post_request` を吐けるようにする。
|
||||
//! The same path installs a `PruneObserver` that adds `prune.fire` / `prune.skip` metrics to
|
||||
//! `MetricsTracker` on each evaluation. On `Fired`, it also stashes a uuid in `UsageTracker`
|
||||
//! to pair with the subsequent `LlmUsage` and emit `prune.post_request`.
|
||||
|
||||
use agen::Item;
|
||||
use agen::llm_client::client::LlmClient;
|
||||
@@ -42,9 +40,9 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
/// prevents the prune projection from firing until usage data exists.
|
||||
///
|
||||
/// Also installs a [`PruneObserver`] that pushes `prune.fire` /
|
||||
/// `prune.skip` metrics into the shared [`MetricsTracker`]. On `Fired`
|
||||
/// `prune.skip` metrics into the shared `MetricsTracker`. On `Fired`
|
||||
/// the observer additionally stashes a fresh correlation_id in
|
||||
/// [`UsageTracker`] so the next `LlmUsage` can be paired with a
|
||||
/// `UsageTracker` so the next `LlmUsage` can be paired with a
|
||||
/// `prune.post_request` metric carrying the same id.
|
||||
pub fn attach_prune(&mut self, config: PruneConfig) {
|
||||
let usage_history_for_tokens = self.usage_history_handle();
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
//! Compact / prune 専用のトークン会計補助。
|
||||
//! Token-accounting helpers specific to compact / prune.
|
||||
//!
|
||||
//! 汎用部分(`prefix_bytes`, `tokens_at`, `total_tokens`, `total_tokens_at`)は
|
||||
//! [`agen::token_counter`] にあり、`UsageRecord` の列と現在の history から
|
||||
//! pure に推定する。本モジュールは compact / prune 固有のロジック
|
||||
//! (`split_for_retained`, `savings_for_prune`)と、Worker 上の公開 API に
|
||||
//! 限定する。
|
||||
//! Generic functions (`prefix_bytes`, `tokens_at`, `total_tokens`, `total_tokens_at`) live in
|
||||
//! [`agen::token_counter`] and compute pure estimates from `UsageRecord` sequences and current
|
||||
//! history. This module is limited to compact / prune logic (`split_for_retained`,
|
||||
//! `savings_for_prune`) and public Worker APIs.
|
||||
//!
|
||||
//! # 方針
|
||||
//! # Policy
|
||||
//!
|
||||
//! - ローカルトークナイザは持たない。実測値があればそれを採用し、
|
||||
//! measurement 間はバイト数で按分、最新 measurement より先は byte/4 で外挿する
|
||||
//! - Compact の retained split では、request-time pruning / projection 後の
|
||||
//! `UsageRecord` を persisted history prefix の単調系列として扱わない。
|
||||
//! 現在の prompt occupancy 推定を raw serialized bytes に配分し、末尾の
|
||||
//! persisted tail サイズで cut を決める。
|
||||
//! - 推定の出どころは [`EstimateSource`] で呼び出し側に明示する。
|
||||
//! 課金判断には使えないが、compact / prune の閾値判定には十分な精度
|
||||
//! - No local tokenizer. Use measurements when available, interpolate between them by byte count,
|
||||
//! and extrapolate beyond the latest measurement at byte/4.
|
||||
//! - Compact's retained split does not treat request-time pruned/projected `UsageRecord`s as a
|
||||
//! monotonic series of persisted history prefixes. It distributes current prompt-occupancy
|
||||
//! estimates over raw serialized bytes and chooses the cut by persisted tail size.
|
||||
//! - [`EstimateSource`] exposes estimate provenance to callers. Accuracy is sufficient for
|
||||
//! compact / prune thresholds, not billing decisions.
|
||||
|
||||
use agen::llm_client::client::LlmClient;
|
||||
use agen::token_counter::{item_bytes, prefix_bytes, tokens_at};
|
||||
@@ -26,9 +24,9 @@ pub use agen::token_counter::{EstimateSource, TokenEstimate};
|
||||
|
||||
use crate::Worker;
|
||||
|
||||
/// history を分割する位置。
|
||||
/// Position at which to split history.
|
||||
///
|
||||
/// `items[..index]` が捨てる/要約される側、`items[index..]` が残る側。
|
||||
/// `items[..index]` is discarded/summarized; `items[index..]` is retained.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct SplitPoint {
|
||||
pub index: usize,
|
||||
@@ -101,25 +99,19 @@ fn ceil_div_u128(n: u128, d: u128) -> u128 {
|
||||
if n == 0 { 0 } else { ((n - 1) / d) + 1 }
|
||||
}
|
||||
|
||||
/// `history[cut..]` が `ToolCall` / `ToolResult` のペア境界を尊重するよう
|
||||
/// `cut` を後退させる。
|
||||
/// Move `cut` backward so `history[cut..]` respects `ToolCall` / `ToolResult` pair boundaries.
|
||||
///
|
||||
/// LLM API は「`ToolResult` を送るならその `ToolCall` も同じ request に
|
||||
/// 含まれていなければならない」というバリデーションを持つ。トークン数
|
||||
/// だけで切った `cut` は並列 tool 呼び出しの途中に落ちうるので、retained
|
||||
/// 側の先頭に対応 `ToolCall` を持たない `ToolResult`(orphan)が残ると
|
||||
/// 次セッション初回 request が API バリデーションで弾かれる。
|
||||
/// LLM APIs require each `ToolResult`'s `ToolCall` in the same request. Token-only cuts can fall
|
||||
/// inside parallel tool calls, leaving an orphan `ToolResult` at the retained head and causing
|
||||
/// API validation to reject the next session's first request.
|
||||
///
|
||||
/// 対策は「retained に入る `ToolResult` について、対応 `ToolCall` も
|
||||
/// retained に含まれる位置まで `cut` を引き下げる」こと。retained_tokens
|
||||
/// 予算は超えうるが、ここでは直接 LLM に投げる訳ではなく次の
|
||||
/// `pre_llm_request` で再評価されるだけなので safe。
|
||||
/// Lower `cut` until every retained `ToolResult`'s matching `ToolCall` is also retained. This may
|
||||
/// exceed the retained_tokens budget, but is safe: nothing is sent directly to the LLM here;
|
||||
/// the next `pre_llm_request` reevaluates it.
|
||||
///
|
||||
/// アルゴリズム: history を末尾から走査し、retained 範囲内の `ToolResult`
|
||||
/// に出会うたびに対応 `ToolCall` の位置で `cut` を min 更新する。`cut` が
|
||||
/// 下がると以前は要約側だった位置が retained に入るので、後続走査で連鎖的
|
||||
/// に正しい位置まで引き下がる。`ToolCall` の `call_id` はユニークなので
|
||||
/// 事前にマップ化して O(n) で済ます。
|
||||
/// Scan history backward, lowering `cut` to each retained `ToolResult`'s matching `ToolCall` position.
|
||||
/// Lowering the cut retains positions previously on the summary side; the remaining scan cascades
|
||||
/// backward to the correct boundary. Unique `ToolCall` `call_id`s allow a precomputed map for O(n).
|
||||
fn balance_to_pair_boundary(history: &[Item], cut: usize) -> usize {
|
||||
let mut idx = cut.min(history.len());
|
||||
if idx == 0 {
|
||||
@@ -151,9 +143,8 @@ fn balance_to_pair_boundary(history: &[Item], cut: usize) -> usize {
|
||||
idx
|
||||
}
|
||||
|
||||
/// 1 つの ToolResult 項目について、`content` を `None` に射影したとき
|
||||
/// 減少するシリアライズ後バイト数。ToolResult 以外や既に content=None
|
||||
/// の item は 0 を返す。
|
||||
/// Serialized bytes saved by projecting one ToolResult's `content` to `None`.
|
||||
/// Returns 0 for non-ToolResult items or items already having content=None.
|
||||
fn tool_result_content_bytes(item: &Item) -> u64 {
|
||||
if !matches!(
|
||||
item,
|
||||
@@ -186,13 +177,12 @@ pub(crate) fn token_estimates_for_prune_impl(
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Prune 射影(`ToolResult.content = None`)で節約されるトークン数の推定。
|
||||
/// Estimate token savings from prune projection (`ToolResult.content = None`).
|
||||
///
|
||||
/// `indices` は [`agen::prune::prunable_indices`] が返す候補列を
|
||||
/// 想定する。各候補の content バイト差分を合算し、usage 履歴由来の
|
||||
/// tokens/byte レートでトークン数に換算する。範囲を「丸ごと drop」する
|
||||
/// のではなく、item 自体(summary 等)は残したままの値を返す点が
|
||||
/// `tokens_at` ベースの計算と異なる。
|
||||
/// `indices` is expected to contain candidates from [`agen::prune::prunable_indices`]. Sum their
|
||||
/// content-byte differences and convert using the usage-history tokens/byte rate. Unlike
|
||||
/// `tokens_at`-based calculations, this retains the items themselves (summaries, etc.) rather
|
||||
/// than dropping the entire range.
|
||||
pub(crate) fn savings_for_prune_impl(
|
||||
history: &[Item],
|
||||
records: &[UsageRecord],
|
||||
@@ -218,9 +208,9 @@ pub(crate) fn savings_for_prune_impl(
|
||||
};
|
||||
}
|
||||
|
||||
// 最新の measurement を使って tokens/byte を求め、バイト差分を換算する。
|
||||
// 実測値そのものではなく比率しか使わないので、history_len と
|
||||
// record.history_len が一致しなくても rate は正しい。
|
||||
// Convert byte differences using the latest measurement's tokens/byte rate.
|
||||
// Only the ratio, not the measurement itself, is used, so the rate remains
|
||||
// valid even when history_len differs from record.history_len.
|
||||
let prefix = prefix_bytes(history);
|
||||
let last = records.last().expect("records non-empty");
|
||||
let ref_bytes = prefix[last.history_len.min(history.len())];
|
||||
@@ -240,31 +230,30 @@ pub(crate) fn savings_for_prune_impl(
|
||||
TokenEstimate { tokens, source }
|
||||
}
|
||||
|
||||
// ── Worker に生やす公開 API ───────────────────────────────────────────────
|
||||
// ── Public Worker APIs ───────────────────────────────────────────────
|
||||
|
||||
impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
/// 現在の history 全体の推定トークン数。
|
||||
/// Estimated tokens for the entire current history.
|
||||
///
|
||||
/// 最後の measurement と、その後に追加された未測定分の byte/4 外挿。
|
||||
/// Uses the last measurement plus byte/4 extrapolation for subsequent unmeasured additions.
|
||||
pub fn total_tokens(&self) -> TokenEstimate {
|
||||
let usage = self.usage_history();
|
||||
agen::token_counter::total_tokens(&self.history(), &usage)
|
||||
}
|
||||
|
||||
/// 任意の history index 時点でのプロンプト全長推定。
|
||||
/// Estimate full prompt length at any history index.
|
||||
///
|
||||
/// `total_tokens()` と同じ accounting を任意位置で評価する版。
|
||||
/// memory extract trigger が
|
||||
/// `total_tokens_at(now) - total_tokens_at(pointer)` で
|
||||
/// pointer 以降に増えたプロンプト長を測るのに使う。
|
||||
/// Evaluates the same accounting as `total_tokens()` at an arbitrary position.
|
||||
/// Memory extraction triggers use `total_tokens_at(now) - total_tokens_at(pointer)`
|
||||
/// to measure prompt growth since the pointer.
|
||||
pub fn total_tokens_at(&self, history_len: usize) -> TokenEstimate {
|
||||
let usage = self.usage_history();
|
||||
agen::token_counter::total_tokens_at(&self.history(), &usage, history_len)
|
||||
}
|
||||
|
||||
/// 末尾から `retained` トークン以上を残すための分割位置。
|
||||
/// Split position retaining at least `retained` tokens from the tail.
|
||||
///
|
||||
/// `history[..cut.index]` が要約/破棄される側、`history[cut.index..]` が残る側。
|
||||
/// `history[..cut.index]` is summarized/discarded; `history[cut.index..]` is retained.
|
||||
pub fn split_for_retained(&self, retained: u64) -> SplitPoint {
|
||||
let usage = self.usage_history();
|
||||
split_for_retained_impl(&self.history(), &usage, retained)
|
||||
@@ -496,10 +485,10 @@ mod tests {
|
||||
#[test]
|
||||
fn balance_retreats_from_inside_parallel_tool_results() {
|
||||
// [Msg, TC_a, TC_b, TC_c, TR_a, TR_b, TR_c]
|
||||
// cut=5 → retained=[TR_b, TR_c]。TR_c の TC は idx=3、TR_b は idx=2 →
|
||||
// idx=2 まで後退。だが retained に TR_a (idx=4) が新たに入り、その TC_a
|
||||
// は idx=1 でまだ外 → 連鎖後退で最終的に idx=1。retained は
|
||||
// [TC_a, TC_b, TC_c, TR_a, TR_b, TR_c]。
|
||||
// cut=5 -> retained=[TR_b, TR_c]. TR_c's TC is at idx=3, TR_b's at idx=2,
|
||||
// so retreat to idx=2. This now retains TR_a (idx=4), whose TC_a at idx=1
|
||||
// is still outside. Cascading retreat ends at idx=1, retaining
|
||||
// [TC_a, TC_b, TC_c, TR_a, TR_b, TR_c].
|
||||
let history = vec![
|
||||
msg("u"),
|
||||
tc("a"),
|
||||
@@ -514,47 +503,47 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn balance_retreats_between_call_and_result() {
|
||||
// [TC_a, TR_a, TC_b, TR_b]。cut=3 → retained=[TR_b] orphan。
|
||||
// TC_b は idx=2 → cut=2。retained=[TC_b, TR_b]。
|
||||
// [TC_a, TR_a, TC_b, TR_b]. cut=3 -> retained=[TR_b], an orphan.
|
||||
// TC_b is at idx=2 -> cut=2, retained=[TC_b, TR_b].
|
||||
let history = vec![tc("a"), tr("a"), tc("b"), tr("b")];
|
||||
assert_eq!(balance_to_pair_boundary(&history, 3), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn balance_cascades_through_nested_pairs() {
|
||||
// [TC_a, TC_b, TR_b, TR_a, TC_c, TR_c]。cut=3 → retained=[TR_a, TC_c, TR_c]。
|
||||
// TR_a の TC は idx=0 → cut=0。retained=full。
|
||||
// [TC_a, TC_b, TR_b, TR_a, TC_c, TR_c]. cut=3 -> retained=[TR_a, TC_c, TR_c].
|
||||
// TR_a's TC is at idx=0 -> cut=0, retaining everything.
|
||||
let history = vec![tc("a"), tc("b"), tr("b"), tr("a"), tc("c"), tr("c")];
|
||||
assert_eq!(balance_to_pair_boundary(&history, 3), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn balance_noop_when_cut_at_pair_boundary() {
|
||||
// [TC_a, TR_a, Msg, TC_b, TR_b]。cut=2 → retained=[Msg, TC_b, TR_b] balanced。
|
||||
// [TC_a, TR_a, Msg, TC_b, TR_b]. cut=2 -> retained=[Msg, TC_b, TR_b], balanced.
|
||||
let history = vec![tc("a"), tr("a"), msg("u"), tc("b"), tr("b")];
|
||||
assert_eq!(balance_to_pair_boundary(&history, 2), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn balance_handles_orphan_result_without_matching_call() {
|
||||
// ToolCall がそもそも存在しない ToolResult は触らない(壊れた history は
|
||||
// ここでは直しようがない)。cut=1 → そのまま 1 を返す。
|
||||
// Leave ToolResults with no matching ToolCall untouched; broken history cannot
|
||||
// be repaired here. cut=1 -> return 1 unchanged.
|
||||
let history = vec![msg("u"), tr("zombie")];
|
||||
assert_eq!(balance_to_pair_boundary(&history, 1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn balance_keeps_cut_when_call_is_inside_retained() {
|
||||
// [Msg, TC_a, TR_a]。cut=1 → retained=[TC_a, TR_a]。TR_a の call_pos=1 >= idx=1。OK。
|
||||
// [Msg, TC_a, TR_a]. cut=1 -> retained=[TC_a, TR_a]. TR_a's call_pos=1 >= idx=1: OK.
|
||||
let history = vec![msg("u"), tc("a"), tr("a")];
|
||||
assert_eq!(balance_to_pair_boundary(&history, 1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_for_retained_aligns_to_pair_boundary() {
|
||||
// 並列 TC*3 / TR*3 ターン後に Msg を 1 件足し、retained=Msg のサイズ相当に
|
||||
// 設定。トークン的には cut=末尾近くだが、orphan を避けるため TC 群の手前
|
||||
// まで後退するはず。
|
||||
// Add one Msg after a parallel TC*3 / TR*3 turn and set retained to its size.
|
||||
// The token-only cut is near the tail, but should retreat before the TC group
|
||||
// if needed to avoid orphans.
|
||||
let history = vec![
|
||||
msg("user"),
|
||||
tc("a"),
|
||||
@@ -567,16 +556,15 @@ mod tests {
|
||||
];
|
||||
let total_bytes: u64 = history.iter().map(item_bytes).sum();
|
||||
let records = vec![record(history.len(), total_bytes)]; // rate = 1 tok/byte
|
||||
// tail の item_bytes 相当のみ retain したい。
|
||||
// Retain only the tail's item_bytes equivalent.
|
||||
let tail_tokens = item_bytes(&history[7]);
|
||||
let cut = split_for_retained_impl(&history, &records, tail_tokens);
|
||||
// token 単独だと cut は 7(tail のみ retained)になるが、retained 先頭が
|
||||
// Msg なら balance しなくて OK。balance helper の no-op を確認する意味も込めて
|
||||
// index == 7 を期待する。
|
||||
// Tokens alone give cut=7 (only the tail retained). A retained head of Msg needs
|
||||
// no balancing. Expect index == 7, also checking the balance helper's no-op.
|
||||
assert_eq!(cut.index, 7);
|
||||
|
||||
// 逆に retained をやや増やしてトークン的に cut=6(TR_c のみ retained)に
|
||||
// させると、TR_c は orphan なので balance が 1 まで後退するはず。
|
||||
// Slightly increasing retained gives token-only cut=6 (TR_c alone from the tool turn).
|
||||
// TR_c is an orphan, so balancing should retreat to 1.
|
||||
let big_retain = tail_tokens + item_bytes(&history[6]);
|
||||
let cut = split_for_retained_impl(&history, &records, big_retain);
|
||||
assert_eq!(cut.index, 1);
|
||||
|
||||
@@ -151,9 +151,8 @@ impl UsageTracker {
|
||||
pending.usage_recorded = true;
|
||||
let request_id = pending.id;
|
||||
let history_len = pending.history_len;
|
||||
// UsageEvent.input_tokens は scheme 層で「占有量(プロンプト全長)」に
|
||||
// 正規化済みである前提(Anthropic は cache_read + cache_creation を
|
||||
// 加算して emit する)。
|
||||
// Assumes the scheme layer has normalized UsageEvent.input_tokens to occupancy
|
||||
// (full prompt length). Anthropic adds cache_read + cache_creation before emitting.
|
||||
let input_total = event.input_tokens.unwrap_or(0);
|
||||
let cache_read = event.cache_read_input_tokens.unwrap_or(0);
|
||||
let cache_write = event.cache_creation_input_tokens.unwrap_or(0);
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
//! Compact worker state and the four tools that drive it.
|
||||
//! Summarization and reference-file selection by the compaction Internal Worker.
|
||||
//!
|
||||
//! The compact worker is a disposable `Engine` instance spun up by
|
||||
//! [`Worker::compact`]. It receives the history to summarise plus a list of
|
||||
//! default reference files (from the session-lifetime `Tracker`) and runs
|
||||
//! a tool-driven LLM loop. The tools here let it:
|
||||
//! Summary input is a bounded history projection; it does not rewrite the original history.
|
||||
//! Results collect in [`CompactWorkerContext`]; [`crate::Worker::compact`] validates, saves,
|
||||
//! and activates them. Missing summaries or hard-limit violations fail compaction, unlike
|
||||
//! targets or warning-only thresholds.
|
||||
//!
|
||||
//! - `read_file` — inspect referenced files (reuses `tools::read_tool`)
|
||||
//! - `mark_read_required(path, offset?, limit?)` — nominate a file whose
|
||||
//! contents should be injected into the compacted context as an
|
||||
//! auto-read system message
|
||||
//! - `add_reference(path)` — nominate a file the next session should
|
||||
//! know about by name only (contents not included)
|
||||
//! - `write_summary(text)` — deliver (or overwrite) the structured summary
|
||||
//!
|
||||
//! Everything the worker decides ends up in [`CompactWorkerContext`],
|
||||
//! which `Worker::compact` drains after the loop and turns into the
|
||||
//! compacted session's opening system messages.
|
||||
//! Auto-read accepts only UTF-8 text through the current WorkdirSession, checking scope and
|
||||
//! the aggregate budget. Images, binary data, and truncated content stay out of the body;
|
||||
//! paths can remain as references for the next request. Naming a reference grants no read
|
||||
//! authority; incorporating it into the compacted result still follows actual read conditions.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
|
||||
@@ -3790,8 +3790,8 @@ where
|
||||
St: Store,
|
||||
{
|
||||
let manifest = worker.manifest();
|
||||
// `build_client` がここに到達する前に同じマニフェストで成功している
|
||||
// ため、カタログ解決も必ず通る。念のため失敗時は "unknown" に落とす。
|
||||
// `build_client` already succeeded with this manifest, so catalog resolution
|
||||
// must also succeed. Fall back to "unknown" on failure as a precaution.
|
||||
let resolved = manifest::model_catalog::resolve_model_manifest(&manifest.model).ok();
|
||||
let context_window = resolved
|
||||
.as_ref()
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
//! Worker Drive operations use only the injected Workspace API. Observations
|
||||
//! below are bounded preconditions, never a hierarchy replica or a grant cache.
|
||||
//! Drive operations through the Workspace API and bounded mutation-precondition observations.
|
||||
//!
|
||||
//! The Backend owns Drive hierarchy, blobs, and grants. Worker neither reads the Server's physical
|
||||
//! blob root nor treats Drive as a Workdir or Session attachment store. Entries are identified by
|
||||
//! Workspace and canonical decimal-string node ID; large IDs are never converted to JSON numbers.
|
||||
//!
|
||||
//! # Mutation-precondition observations
|
||||
//!
|
||||
//! Regular Tool read/metadata keeps a bounded record of entries and their last commit request IDs.
|
||||
//! List/search is discovery, not a substitute for mutation-precondition observation. Explicit
|
||||
//! observation is required after cache eviction; successful mutations remove old observations.
|
||||
//! This is neither an authority cache nor a persistent hierarchy copy. Edit validates the full
|
||||
//! preimage and previously observed commit ID, never editing truncated text. String replacement
|
||||
//! shares `fs_operation::text` rules; Backend CAS decides the final winner.
|
||||
//!
|
||||
//! # Workdir sources and images
|
||||
//!
|
||||
//! Saving from a Workdir reads bytes through the selected alias's scoped session and verifies each
|
||||
//! chunk's path, offset, size, and hash plus the overall SHA256. Source reads and Drive destination
|
||||
//! writes are independent capabilities; completing blob transfer alone does not prove publication.
|
||||
//! Images are assembled from chunks bound to the entry's commit request; bytes from mixed
|
||||
//! generations are never attached. Image type is detected from bytes, not URLs or filenames.
|
||||
//! `ImageAttachment` returns through existing ToolOutput. Later Drive updates/deletions do not
|
||||
//! change observations already recorded in the Session; Engine/Session owns retention, pruning,
|
||||
//! and replay.
|
||||
//!
|
||||
//! [`wip`] owns native publication, [`backend`] request receipt checks, and delegation Drive blocking
|
||||
//! for internal children. Shared WIP references, permissions, and unknown outcomes follow [`crate::wip`].
|
||||
pub mod backend;
|
||||
pub(crate) mod delegation;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
//! Identity-bound Drive API adapter. The Backend owns grants, hierarchy, CAS and
|
||||
//! receipts. In particular, a lost mutation response is queried, never resent.
|
||||
//! Identity-bound Drive API adapter and mutation receipt checks.
|
||||
//!
|
||||
//! The Backend owns grants, hierarchy, CAS, and receipts, applying current authorization to every
|
||||
//! read/chunk/status request. The adapter validates Workspace, node ID, commit ID, and size, and
|
||||
//! never copies raw diagnostics that may contain provider paths, SQL, or credentials into output.
|
||||
//!
|
||||
//! Mutations use Host-generated request IDs and observed node/commit IDs. Conflicts do not trigger
|
||||
//! retries against substituted current state. For an unknown mutation response, check the same
|
||||
//! request's receipt once; only a committed receipt with valid IDs and metadata proves DB success.
|
||||
//! An uncommitted snapshot cannot rule out a later commit by an in-flight request, so all other
|
||||
//! cases retain an unknown outcome with the original request ID. Never blindly resend with a new ID.
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use crate::worker::{
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
//! Fail-closed Drive boundary for parent-owned Internal SubWorkers, including Reviewers.
|
||||
//! Boundary that blocks Drive capabilities for parent-owned Internal SubWorkers.
|
||||
//!
|
||||
//! Internal children have no Backend Drive identity. Explicit internal Drive delegation is not
|
||||
//! supported until the Backend can approve scope bound to that child identity. A parent's current
|
||||
//! grant, enabled profile, or Reviewer attestation is not a child Drive grant. Registered Runtime
|
||||
//! Workers must use their own identity-bound, current Backend grants; this wrapper creates neither
|
||||
//! identities nor grants and keeps no local authorization cache.
|
||||
//! Internal children have no independent Backend Drive identity/grant. Parent grants, effective
|
||||
//! Profiles, and Reviewer attestations therefore grant no child authority. Spawn disables Drive
|
||||
//! after resolving every Profile selector; this wrapper also rejects JSON/binary Drive operations
|
||||
//! through the shared Workspace client. Non-Drive operations and Reviewer attestations remain;
|
||||
//! no identity, grant, or authorization cache is created. Future internal delegation will require
|
||||
//! scope no broader than the parent's, bound to a child identity, and Backend reauthorization.
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
//! Native non-indexable Drive entries. Publication resolves just one ID through
|
||||
//! current Backend authorization; the Client owns entry/committed-request validators.
|
||||
//! Native Drive projection with no indexable entry children.
|
||||
//!
|
||||
//! `/drive` is a discovery entrance; Tree does not enumerate file inventory. Only entries selected
|
||||
//! from bounded list/search results resolve directly. Paths contain a hex-encoded Workspace ID and
|
||||
//! decimal node ID, preventing substitution with another Workspace or ID. Previously acquired
|
||||
//! Client entries are not added to Tree either.
|
||||
//!
|
||||
//! The provider resolves one entry under current Backend authorization and creates a self-scoped
|
||||
//! contract for its kind. Metadata, descriptor, validator, and handler are fixed to one publication;
|
||||
//! the route binds the entry/creation parent. Move destinations accept typed Entry paths.
|
||||
//! Interfaces describe operations for the entry kind; the Backend rejects writes under read-only
|
||||
//! grants. Native Invoke still applies original Tool permissions. [`crate::wip`] owns the shared
|
||||
//! cache, validator, and cancellation contracts.
|
||||
use super::{
|
||||
DriveFeature, SPECS, Spec,
|
||||
backend::{DriveError, decimal},
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
//! Feature connecting Worker-owned Flow state to a read-only internal verifier.
|
||||
//!
|
||||
//! The model submits only a reason, choosing neither instance nor target state. The coordinator
|
||||
//! clones the current Worker's local state and commits attempts/events to the Session before
|
||||
//! replacing in-memory state. It does not use the Workspace client for transition mutations and
|
||||
//! first recovers restored verifying attempts. The `flow` crate owns graph and verdict-set validation.
|
||||
//!
|
||||
//! The verifier receives a fixed capture of the committed parent Session, current state, reason,
|
||||
//! and all outgoing conditions. Even with a Workdir, it gets only Read/Glob/Grep through a read-only
|
||||
//! wrapper, not Workspace, Ticket, Memory, Worker management, write, or Bash capabilities.
|
||||
//! Only complete typed `FinishFlowVerification` results are accepted; prose completion is not
|
||||
//! success. State-entry instructions return as regular Tool results; this Feature owns neither
|
||||
//! role-specific business loops nor side effects.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
//! Workspace-HTTP backed Memory tools.
|
||||
//! Memory tools through Workspace HTTP and a Host-provided resident prompt source.
|
||||
//!
|
||||
//! Runtime workers may have Workspace authority without direct local filesystem
|
||||
//! authority. In that case model-visible Memory tools must go through the
|
||||
//! workspace backend instead of resolving `.yoi/memory` from a Worker workdir.
|
||||
//! Workspace authority does not imply filesystem authority, so model-visible Memory operations
|
||||
//! delegate to typed Backend operations. They do not discover/import `.yoi/memory` from cwd or
|
||||
//! ancestors. The legacy Workspace single document and subject-scoped Subjektiv retain separate
|
||||
//! authorities/operations. Enabling the Feature neither migrates/dual-writes legacy data nor
|
||||
//! replaces existing tool semantics with subject Memory.
|
||||
//!
|
||||
//! Resident context is supporting context, not current authority or grounds to update confirmed
|
||||
//! Memory. User-managed behavior and generated Memory surfaces remain distinct; fetch/render
|
||||
//! failures do not mean "unset".
|
||||
|
||||
use crate::subjektiv::{SubjektivHostConnection, SubjektivHostError};
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -729,6 +735,11 @@ impl WorkspaceSubjektivResidentSummarySource {
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect behavior changes against the state reflected in history, not the fetched state.
|
||||
///
|
||||
/// Initial load builds a prompt contribution; change refresh does not advance applied on fetch/render alone.
|
||||
/// The interceptor confirms after durably appending a system item, allowing refetch if append fails.
|
||||
/// Invalidate applied before rewrite so even identical bodies can be presented in the new history.
|
||||
#[async_trait]
|
||||
impl SystemPromptContributionSource for WorkspaceSubjektivResidentSummarySource {
|
||||
async fn load(&self) -> SystemPromptContribution {
|
||||
@@ -796,9 +807,14 @@ const SUBJEKTIV_RESTORE_REFRESH_HOOK: &str = "refresh-resident-surface-after-res
|
||||
const SUBJEKTIV_BEHAVIOR_REFRESH_HOOK: &str = "refresh-subject-behavior-before-request";
|
||||
const SUBJEKTIV_BEHAVIOR_REWRITE_HOOK: &str = "invalidate-subject-behavior-before-rewrite";
|
||||
|
||||
/// subjektiv-owned restore behavior. Initial prompt contribution remains a
|
||||
/// shared host facility, while loading and rendering a post-restore refresh is
|
||||
/// installed only with an execution-enabled, subject-attached subjektiv Feature.
|
||||
/// Subjektiv-owned restore-boundary refresh.
|
||||
///
|
||||
/// Initial prompt contributions are a shared Host facility; only an execution-enabled, subject-connected
|
||||
/// Feature requests restore refresh. Source presence alone must not make generic Worker core infer it.
|
||||
/// Saved prompt/history is unchanged; one system item representing current resident context is appended.
|
||||
/// A generated surface's ready-empty state differs from ungenerated, stale, or failed; those states
|
||||
/// never mean empty Memory. Pre-request hooks handle later behavior updates; the restore boundary
|
||||
/// itself is not repeated.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct SubjektivResidentRestoreRefreshFeature {
|
||||
source: Arc<dyn SystemPromptContributionSource>,
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
//! Memory extraction lifecycle using committed runs and a restricted Internal Worker.
|
||||
//!
|
||||
//! Worker core owns only generic hook/task/session plumbing; this Feature owns extraction targets,
|
||||
//! audit, and pointers. Workspace Memory and Subjektiv have separate pointer domains and are never
|
||||
//! simultaneous automatic extraction targets. Extraction uses the parent's committed capture and
|
||||
//! resolved evidence, not uncommitted child Worker output.
|
||||
//!
|
||||
//! Pointers advance only on success with explicit FinishMemoryExtraction, not mere Engine completion.
|
||||
//! Generation fences and cancellation are checked; extensions append only when the capture's Segment
|
||||
//! matches the current one, preventing pre-rewrite extraction results from marking ranges processed
|
||||
//! in a new Segment. Candidate staging is an independent durable write before Finish, so later
|
||||
//! failure, cancellation, or fence loss does not roll it back. Retries without pointer advancement
|
||||
//! can produce semantic duplicates; consolidation owns candidate comparison.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
//! Coordinates editing and publication of resident context generated from confirmed Memory.
|
||||
//!
|
||||
//! Budget checks cover the whole request, including the system prompt, JSON input representation,
|
||||
//! and tool schemas, not just source material. Each LLM request repeats the check to account for
|
||||
//! history growth in correction turns. This estimate excludes provider-specific HTTP representations;
|
||||
//! it is neither a billing-token measurement nor a strict guarantee of model limits.
|
||||
//!
|
||||
//! Edit counts, execution time, and source refetches after publication conflicts are bounded.
|
||||
//! Cancellation and background generations are rechecked before publication; the Host decides final
|
||||
//! source freshness and persistence. Active Memory whose material exceeds the budget must not yield
|
||||
//! successful empty generation. Generation failure does not undo confirmed Memory or applied candidates.
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
//! Connects enabled Merge Request Tools to the Workspace API and native WIP.
|
||||
//!
|
||||
//! The native projection accepts only `/merge-requests` and its immediate canonical IDs, binding
|
||||
//! each item's subject to its route. It removes `merge_request_id` from descriptors and reconstructs
|
||||
//! it for original schema and permission evaluation. Only enabled regular Tools are projected;
|
||||
//! no Backend-wide catalog or custom query is added. [`crate::wip`] owns shared reference,
|
||||
//! permission, Client-lifetime, and unknown-outcome contracts.
|
||||
//!
|
||||
//! # Injected review capability
|
||||
//!
|
||||
//! Review routes must exactly match the MR in the Host-injected Reviewer context. Capability tokens
|
||||
//! never appear in descriptors or model arguments. Candidate, Ticket snapshot, approval, repository,
|
||||
//! and integration evidence retain regular Tool and Backend checks. This adapter neither performs
|
||||
//! Git/provider integration nor owns Ticket conclusion decisions.
|
||||
//!
|
||||
//! # Bounded responses and subject validation
|
||||
//!
|
||||
//! Read returns the existing bounded thread page, keeping linked Tickets as references. Successful
|
||||
//! item-read response hashes are observed; mutations discard observations. Receipts do not fabricate
|
||||
//! complete representations or local mutation generations. Open results include the new item path.
|
||||
//! Item responses are checked against the bound MR; subject mismatches after mutations are not
|
||||
//! treated as unexecuted operations. Optional argument omission/null stays as in original Tool input.
|
||||
|
||||
use crate::feature::{
|
||||
FeatureDescriptor, FeatureInstallContext, FeatureInstallError, FeatureInstructionContribution,
|
||||
FeatureInstructionDeclaration, FeatureInstructionId, FeatureModule, ToolContribution,
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
//! Objective Tools and native WIP projection through the scoped Workspace API.
|
||||
//!
|
||||
//! Objective tool registration backed by Workspace API authority.
|
||||
//! Worker cannot assume Backend-local storage paths, so the Workspace API owns Objective reference
|
||||
//! resolution, state, audit, and linked Ticket validation. The native projection accepts `/objectives`
|
||||
//! and immediate canonical `O-*` or internal IDs, binding the subject `id` from the route.
|
||||
//! Linked Tickets remain separate reference inputs, requiring neither Ticket projection nor its
|
||||
//! permissions. [`crate::wip`] owns shared reference, permission, Client-lifetime, and unknown-outcome
|
||||
//! contracts.
|
||||
//!
|
||||
//! Objectives are project-level planning context. Runtime Workers may not know
|
||||
//! local `.yoi/objectives` paths, so model-visible Objective tools go through
|
||||
//! the scoped Workspace API.
|
||||
//! # Fixed contracts and current content
|
||||
//!
|
||||
//! Objects/Interfaces expose operation contracts, not mutable Objective bodies. Content edits thus
|
||||
//! do not invalidate fixed signatures; there is no content cache or mutation generation. Each call
|
||||
//! fetches a current bounded response through the same Backend adapter; the Backend also evaluates
|
||||
//! conditions such as edit's `old_string`. Host validators protect routes and Interface contracts.
|
||||
//! Native operations evaluate permission under regular Tool names and invoke the same business
|
||||
//! logic. Query/detail uses canonical references and bounded bodies/events, omitting internal
|
||||
//! storage IDs from output.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//! Feature exploring one immutable Session capture fixed by the Host.
|
||||
//!
|
||||
//! Worker-observation owns live Worker reauthorization and recapture; this Feature reads only its
|
||||
//! retained snapshot. It has no Workspace client or Memory state, and follows shared Session capture
|
||||
//! references, privacy, and exploration bounds. It exposes history to internal verifiers and similar
|
||||
//! consumers without business-mutation capabilities.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
//! Host-scoped model tools for change-identified subjektiv Memory.
|
||||
//! Model tools for Subjektiv Memory with change IDs, bound to a Host connection.
|
||||
//!
|
||||
//! Confirmed Memory is read-only here. Explicit remembering and change requests
|
||||
//! become immutable staging candidates; T-670 remains the only authority that may
|
||||
//! adopt a candidate as a confirmed change.
|
||||
//! Confirmed Memory is read-only here. Remember/ProposeChange creates only immutable staging
|
||||
//! candidates; adoption and confirmed changes remain under consolidation authority. The Host fixes
|
||||
//! subject/session/provenance from committed captures and connections, not model input. Preferences
|
||||
//! require HumanInput evidence; model-authored tool calls never become authority for human preferences.
|
||||
//!
|
||||
//! Non-preference proposals without entry_refs stay pending_commit and use their own tool call as
|
||||
//! evidence after commit. Receipts derive from Session and tool-call identity and are reconstructed
|
||||
//! from durable history for staging, enabling idempotent retries with the same receipt after hook
|
||||
//! failure or restore. RunCommitted and PreLlmRequest replay them; fail-closed BeforeSessionRewrite
|
||||
//! prevents compaction from dropping unstaged receipts. Uncommitted operations are never staged.
|
||||
//! Staging changes neither confirmed Memory fingerprints nor surfaces.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Task tools built-in feature module.
|
||||
//!
|
||||
//! The built-in Task feature owns the session-lifetime [`TaskStore`] shared by
|
||||
//! The built-in Task feature owns the session-lifetime `TaskStore` shared by
|
||||
//! the Task tools and reminder hooks. Worker hosts install this module through the
|
||||
//! feature contribution boundary and use its narrow snapshot surface for
|
||||
//! restore/rewind/compaction compatibility.
|
||||
|
||||
@@ -1,8 +1,32 @@
|
||||
//! Built-in Ticket feature adapter.
|
||||
//! Ticket Feature and native WIP projection connected to the Workspace API.
|
||||
//!
|
||||
//! The ticket crate owns Ticket domain logic and Tool implementations. This
|
||||
//! module binds an authority-scoped Workspace client, declares the built-in
|
||||
//! feature, and contributes those tools through the normal registry path.
|
||||
//! The `ticket` crate owns the Ticket domain and regular Tools; the Backend owns Workspace state,
|
||||
//! relationships, and audit. This module connects a scoped Workspace client to the enabled Tools.
|
||||
//! Shared WIP references, permissions, and unknown outcomes follow [`crate::wip`].
|
||||
//!
|
||||
//! # Route-bound subjects
|
||||
//!
|
||||
//! The native projection exposes `/tickets` and one immediate child segment. Items accept canonical
|
||||
//! `T-*` or canonical internal IDs, never resolving Objective keys or deeper suffixes as Tickets.
|
||||
//! Item descriptors omit subject `ticket`/`id`, reconstructing original Tool arguments only from the
|
||||
//! resolved route. Related/dependency Tickets remain separate inputs resolved by the Backend.
|
||||
//! Operations come from regular Tools actually enabled by `TicketFeatureAccess`, retaining original
|
||||
//! schemas and permission identities. WIP adds neither a broader business catalog nor another workflow.
|
||||
//!
|
||||
//! # Bounded responses and observation invalidation
|
||||
//!
|
||||
//! Query returns bounded snippets and canonical item paths; read returns bounded Backend details.
|
||||
//! Objectives and Merge Requests remain references, without expanding their content or native routes.
|
||||
//! Successful item-read response hashes feed route validators; canonical/internal aliases share an
|
||||
//! observation group. Mutation receipts are not complete representations, so updates discard
|
||||
//! observation hashes. Related targets and targets in queue results are also invalidated; no mutation
|
||||
//! counter is created. These local observations replace neither Backend item/state CAS nor current
|
||||
//! authorization.
|
||||
//!
|
||||
//! Fetching canonical references after mutations uses failure classes acknowledging that writes may
|
||||
//! already have succeeded. HTTP failures at that stage are not misclassified as "unexecuted".
|
||||
//! Optional arguments distinguish omission from explicit null for original JSON Schema/permission
|
||||
//! evaluation; defaults use the same implementation as regular Tools.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
//! Feature reading committed Sessions of Host-authorized Workers.
|
||||
//!
|
||||
//! The Host injects [`WorkerObservationProvider`]; model input never constructs grants, URLs,
|
||||
//! session handles, or provider clients. List returns only currently authorized subjects, and every
|
||||
//! capture reauthorizes. Missing and unauthorized subjects return the same NotFound, preventing
|
||||
//! distinction. Runtime peers and parent-owned SubWorkers are separate tagged subjects.
|
||||
//!
|
||||
//! Each exploration operation recaptures its target to reflect newly committed entries. Shared
|
||||
//! Session capture owns exploration projections and reference semantics. This Feature does not
|
||||
//! enable fixed-snapshot session-explore or Memory extraction; observation grants no Worker control,
|
||||
//! Ticket mutation, or approval capability. SubWorker completion notifications contain no transcript;
|
||||
//! they prompt inspection rather than substitute for reading the committed Session.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
//! WIP-only, grant-bound Workspace configuration Feature. No local config store
|
||||
//! or OS Workdir/session is created by this logical attachment.
|
||||
//! WIP-only logical attachment Feature for Workspace configuration allowed by explicit grants.
|
||||
//!
|
||||
//! Configuration source authority resides in the Backend virtual tree; evaluated Profiles/Prompts
|
||||
//! are not edited in place of source. The Feature connects to the existing ledger through a
|
||||
//! Workspace-bound client. It creates no OS directory copy, WorkdirSession, Bash capability, or
|
||||
//! independent attachment ledger. The Backend and configuration domain own source persistence,
|
||||
//! evaluation, CAS, and policies for applying changes to running Workers.
|
||||
//!
|
||||
//! # Shared attach path
|
||||
//!
|
||||
//! Structured chat invocation and native self-attach share [`WorkspaceConfigFeature::attach`].
|
||||
//! The access argument only restricts existing grants; unavailable explicit write access is rejected,
|
||||
//! not downgraded. Attach runs while preparing admitted input and records a durable result before
|
||||
//! passing it to the dependent LLM. Availability alone does not prove authority: validation, including
|
||||
//! replay/resume, and Backend ledger updates check current access. Opaque connection IDs identify
|
||||
//! connection lifetimes; alias reuse cannot revive old connection authority.
|
||||
//!
|
||||
//! # Request-time projection
|
||||
//!
|
||||
//! [`wip`] provides the native entrance; content builds a logical subtree from current Backend
|
||||
//! metadata. Source bodies stay out of observations; directories derive from source paths. This
|
||||
//! tree adds no Workflow/Repository registration, credential management, or Host configuration files.
|
||||
//! Shared WIP references, Client lifetimes, and unknown outcomes follow [`crate::wip`].
|
||||
|
||||
pub mod backend;
|
||||
mod content;
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
//! Identity-bound typed Workspace transport. Never retries a dispatched request.
|
||||
//! Typed Workspace configuration transport bound to Worker identity.
|
||||
//!
|
||||
//! Uses `workers/self/workspace-config`, never bypassing it for the owner editor API. Responses are
|
||||
//! checked for Workspace, alias, content root, and connection lifetime, distinguishing typed Backend
|
||||
//! `NotCommitted` from `Unknown`. Authorization, CAS, and input rejections become safe fixed
|
||||
//! diagnostics; raw provider/transport bodies never reach the model. Missing, oversized, or
|
||||
//! undecodable mutation responses do not prove non-commit; dispatched requests are never resent.
|
||||
//! This adapter does not reimplement Backend source-persistence transactions or activation.
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
//! Request-time logical subtree. Metadata is captured per resolution, never
|
||||
//! cached as authority; authorization and canonical tree CAS stay in Backend.
|
||||
//! Logical configuration subtree built per request from Backend metadata.
|
||||
//!
|
||||
//! Maps `/workspace-config` to the Backend's empty root path and children to canonical relative
|
||||
//! source paths. Connection, authorization, and node information are fetched at resolution, without
|
||||
//! permanent file inventory or authority caches. Read/write targets are fixed by selected routes;
|
||||
//! model arguments cannot override Workspace, connection, validator, or expected digest. Root mutation
|
||||
//! batches handle multiple sources and import references together, passing digest, entrypoints, and
|
||||
//! node state from one tree observation as preconditions to canonical commit.
|
||||
//!
|
||||
//! Path and root validators have different roles. Additional observations of other paths must match
|
||||
//! connection, root validator, digest, and entrypoints, never mixing snapshots in one batch.
|
||||
//! Backend whole-tree CAS is the final conflict boundary with UI/other Workers; local locks cannot
|
||||
//! replace it. Connection lifetimes and evaluation-contract changes can also cause staleness, so
|
||||
//! identical source bytes alone do not prove freshness. Post-call validators come from returned
|
||||
//! commit/read results, not subsequently read state.
|
||||
//!
|
||||
//! Explicit nonexistent paths provide create contracts bound to their absence observations.
|
||||
//! Oversized trees/paths/text/changes or invalid metadata are not published as complete observations.
|
||||
//! Only authorization denial maps to namespace absence; transport/Backend failures are not hidden as absence.
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
//! Shared native self-attach entrance and production request-time config subtree.
|
||||
//! Registers the native self-attach entrance and request-resolved configuration content subtree.
|
||||
//!
|
||||
//! Connection to the existing attachment ledger and configuration content share a root, but attach
|
||||
//! is not a content mutation and creates no separate content validator. Only the production adapter
|
||||
//! registers a subtree provider bound to current grants. There is no configuration-specific
|
||||
//! compatibility Tool or `/tools` fallback. The parent module and content own Feature enablement,
|
||||
//! connection targets, and configuration-body projection boundaries.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//! Registration, completion, and validation of Feature-declared structured chat invocations.
|
||||
//!
|
||||
//! Public names and aliases serve display/selection; source-qualified identities identify execution
|
||||
//! targets. Registration must match the declared descriptor and the Feature being installed. Public
|
||||
//! name/alias collisions are rejected; failed Feature installs roll back to the same checkpoint as
|
||||
//! other contributions. Completion exposes only enabled handler metadata, not code or credentials.
|
||||
//!
|
||||
//! Worker executes only `Segment::FeatureInvoke`, never extracting syntax from ordinary text,
|
||||
//! attachments, or history. Every call checks the installed descriptor, arguments, current
|
||||
//! availability, and handler semantic/authority validation. Availability is rechecked after async
|
||||
//! work. Completion providers come from registered metadata, not request-specified providers or
|
||||
//! authority. Completion is side-effect-free discovery; oversized candidate values are omitted,
|
||||
//! not shortened.
|
||||
//!
|
||||
//! Handlers use the Host-supplied `invocation_id` as a scoped idempotency key at the business boundary
|
||||
//! and reauthorize at the commit boundary inside `invoke`. General availability alone cannot prove
|
||||
//! argument-specific authority. Oversized results or ambiguous post-execution failures become
|
||||
//! `OutcomeUnknown`, not successful dependent model context. Worker owns execution order and durable receipts.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
//! Adapter projecting MCP wire discovery results into Worker Features.
|
||||
//!
|
||||
//! The `mcp` crate owns process/JSON-RPC lifecycle; this module owns capability-dependent bounded
|
||||
//! discovery, Tool-name namespacing, schema acceptance, and model-facing result rendering. Server
|
||||
//! instructions are not adopted as Worker instructions; external metadata and results remain data.
|
||||
//! List-changed notifications prompt rediscovery rather than prove catalog changes; startup
|
||||
//! rediscovery is also bounded. Failed server contributions are isolated with diagnostics;
|
||||
//! the regular registry decides Tool-name collisions.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//! Worker 視点のファイルシステム操作。
|
||||
//! Filesystem operations from the Worker's perspective.
|
||||
//!
|
||||
//! `WorkdirSession` の上に「Worker が読み取りたい / 列挙したい」操作を集約する軽い wrapper。
|
||||
//! Lightweight wrapper over `WorkdirSession` collecting Worker read/list operations.
|
||||
//!
|
||||
//! - `ReadRequirement` と `render_auto_read` — compact worker が `mark_read_required`
|
||||
//! で nominate したファイルを再読し、`[Auto-read file: ...]` system message に
|
||||
//! 変換する経路。`Worker::compact` から呼ばれる。
|
||||
//! - `slice_lines` — 行 offset / limit でテキストを切り出す純粋ヘルパ。
|
||||
//! compact tool 側の `mark_read_required` でも使用。
|
||||
//! - `list_file_completions` — TUI 補完用、prefix マッチでファイル候補を列挙する経路。
|
||||
//! IPC `Method::ListCompletions` 経由で呼ばれる前提(Phase 2 で接続)。
|
||||
//! - `ReadRequirement` and `render_auto_read` reread files nominated by the compact worker's
|
||||
//! `mark_read_required`, rendering `[Auto-read file: ...]` system messages.
|
||||
//! Called by `Worker::compact`.
|
||||
//! - `slice_lines` is a pure helper slicing text by line offset / limit, also used by the compact
|
||||
//! tool's `mark_read_required`.
|
||||
//! - `list_file_completions` lists prefix-matching file candidates for TUI completion.
|
||||
//! Intended for IPC `Method::ListCompletions` (connected in Phase 2).
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -21,16 +21,16 @@ use workdir::{
|
||||
EntryKind, ListRequest, ReadRequest, StatRequest, WorkdirPath, WorkdirSessionHandle,
|
||||
};
|
||||
|
||||
/// 補完候補1件の最大数。`list_file_completions` がこの値を超えたら打ち切り。
|
||||
/// Maximum completion candidate count; `list_file_completions` stops at this bound.
|
||||
const COMPLETION_LIMIT: usize = 100;
|
||||
/// submit-time directory FileRef の shallow listing で返す最大 entry 数。
|
||||
/// TUI completion と同じ浅い一覧という意味論に揃えるため、同じ上限を使う。
|
||||
/// Maximum entries in a submit-time directory FileRef's shallow listing.
|
||||
/// Shares the TUI completion bound to preserve the same shallow-listing semantics.
|
||||
const DIR_FILE_REF_ENTRY_LIMIT: usize = COMPLETION_LIMIT;
|
||||
/// Provider-side bound for auto-read and submit-time referenced-file reads.
|
||||
const AUTO_READ_BYTE_LIMIT: usize = 4 * 1024 * 1024;
|
||||
|
||||
/// Compact worker が `mark_read_required` で nominate した「次セッション開始時に
|
||||
/// 自動で再読すべきファイル」のエントリ。
|
||||
/// File nominated by the compact worker's `mark_read_required` for automatic rereading
|
||||
/// at the start of the next session.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReadRequirement {
|
||||
pub path: PathBuf,
|
||||
@@ -40,23 +40,23 @@ pub struct ReadRequirement {
|
||||
pub limit: Option<usize>,
|
||||
}
|
||||
|
||||
/// Worker から見えるファイルシステム操作の入口。Clone は cheap(`WorkdirSession` 内 `Arc`)。
|
||||
/// Entry point for Worker-visible filesystem operations. Cheap to clone (`Arc` in `WorkdirSession`).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WorkerFsView {
|
||||
session: WorkdirSessionHandle,
|
||||
}
|
||||
|
||||
/// `list_file_completions` が返す候補1件。
|
||||
/// One candidate returned by `list_file_completions`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FileCandidate {
|
||||
/// 入力 prefix と整合する形のパス(prefix が absolute なら absolute、
|
||||
/// relative なら cwd 相対)。
|
||||
/// Path matching the input prefix's form: absolute for an absolute prefix,
|
||||
/// cwd-relative for a relative prefix.
|
||||
pub path: String,
|
||||
pub is_dir: bool,
|
||||
}
|
||||
|
||||
/// `resolve_file_ref` の失敗理由。Worker 側で Alert に振り分けるために
|
||||
/// WorkdirSession / 内部判定の両方を区別できるよう保持する。
|
||||
/// Failure reason for `resolve_file_ref`. Distinguishes WorkdirSession failures from
|
||||
/// internal checks so Worker can select the appropriate Alert.
|
||||
#[derive(Debug)]
|
||||
pub enum ResolveError {
|
||||
/// Path resolution / scope check failed via `WorkdirSession`.
|
||||
@@ -251,7 +251,7 @@ impl WorkerFsView {
|
||||
}
|
||||
}
|
||||
|
||||
/// `text` の `offset` 行目から `limit` 行(None なら末尾まで)を、元の改行で繋いで返す。
|
||||
/// Return `limit` lines of `text` starting at line `offset` (None means to the end), joined by newlines.
|
||||
pub fn slice_lines(text: &str, offset: usize, limit: Option<usize>) -> String {
|
||||
let lines: Vec<&str> = text.lines().collect();
|
||||
let start = offset.min(lines.len());
|
||||
@@ -536,7 +536,7 @@ mod tests {
|
||||
let view = WorkerFsView::new(fs_for(&dir));
|
||||
|
||||
let cands = view.list_file_completions("").await;
|
||||
// ディレクトリ first
|
||||
// Directories first
|
||||
let names: Vec<&str> = cands.iter().map(|c| c.path.as_str()).collect();
|
||||
assert_eq!(names, vec!["subdir", "alpha.rs", "beta.rs"]);
|
||||
assert!(cands[0].is_dir);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
//! agent-visible WorkerEvent variants, and any future typed system reminder
|
||||
//! insertion all ride this queue.
|
||||
//! Per `tickets/notify-history-persist.md` and `AGENTS.md` (LLM
|
||||
//! context の加工原則), there is **no** "transient, history-skipping"
|
||||
//! context processing principles), there is **no** "transient, history-skipping"
|
||||
//! lane — everything injected into a request is also committed to
|
||||
//! history so any LLM reaction has a visible trigger across turns,
|
||||
//! resume, and compaction, and so the Anthropic prompt cache prefix
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
//! Host-facing, result-only Jobs executed by the private Internal Worker substrate.
|
||||
//! Job execution by a restricted Internal Worker; the Host owns result acceptance and persistence.
|
||||
//!
|
||||
//! The host owns persistence, concurrency, timeout, retries and result acceptance. Normal
|
||||
//! timeout/shutdown routes through the cancellation sender and awaits `run`, including Worker
|
||||
//! resource cleanup. An emergency drop/abort does not confirm asynchronous cleanup; the Host
|
||||
//! must retain its lease and reconcile any unknown outcome before allowing another execution.
|
||||
//! The result-only Feature owns no background tasks, but an in-flight synchronous Host sink
|
||||
//! cannot be preempted by dropping its tool future and must itself be bounded.
|
||||
//! Selected Profile requirements are checked against explicit Host grants. Unsatisfied capabilities
|
||||
//! are rejected, not silently stripped. Profile names and resolution cwd grant no filesystem or
|
||||
//! Workspace authority. Success requires structured output synchronously accepted by the Host sink,
|
||||
//! not a final answer or completion of execution.
|
||||
//!
|
||||
//! The Host manages concurrency, timeouts, and retries, and awaits `run` and resource release even
|
||||
//! after cancellation. Emergency drop or abort cannot confirm asynchronous cleanup, so leases and
|
||||
//! unknown-outcome state remain. The result Feature has no background work, but dropping a tool
|
||||
//! future cannot interrupt a running synchronous sink. The Host must also bound the sink's work.
|
||||
|
||||
mod result;
|
||||
pub use result::{JobResultFeature, JobResultFinalizer, JobResultSink, SUBMIT_JOB_RESULT_TOOL};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
mod backend_job;
|
||||
pub use backend_job::BackendJobExecutionBinding;
|
||||
pub mod bootstrap;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
//! [`ModelManifest`] を [`Box<dyn LlmClient>`] に落とす worker-side factory。
|
||||
//! Worker-side factory converting [`ModelManifest`] into [`Box<dyn LlmClient>`].
|
||||
//!
|
||||
//! 段階:
|
||||
//! 1. `ModelManifest` を [`catalog::resolve_model_manifest`] で
|
||||
//! カタログ込み [`ModelConfig`] に解決(ref → 展開 / inline → 検証)
|
||||
//! 2. `AuthRef` を local secret store / ファイルから解決して [`ResolvedAuth`] に
|
||||
//! 3. `scheme.required_auth()` と解決値を照合(非対応組合せは構築エラー)
|
||||
//! 4. `ModelCapability` は manifest 明示 > model catalog > provider
|
||||
//! default_capability > scheme 既定 の順でフォールバック(上位 3 段は
|
||||
//! `catalog::resolve_model_manifest` が [`ModelConfig`] に詰め込む)
|
||||
//! Steps:
|
||||
//! 1. Resolve `ModelManifest` through [`catalog::resolve_model_manifest`] into catalog-aware
|
||||
//! [`ModelConfig`] (expand refs; validate inline configuration).
|
||||
//! 2. Resolve `AuthRef` from the local secret store / files into [`ResolvedAuth`].
|
||||
//! 3. Check the resolved value against `scheme.required_auth()`; unsupported pairs fail construction.
|
||||
//! 4. `ModelCapability` falls back in order: explicit manifest > model catalog > provider
|
||||
//! default_capability > scheme default. `catalog::resolve_model_manifest` puts the first three
|
||||
//! levels into [`ModelConfig`].
|
||||
//!
|
||||
//! agen は低レベル基盤に留める方針なので、高レベル側で必要に
|
||||
//! なる認証ストア解決と secret store 解決は worker 側で行う。
|
||||
//! agen remains low-level infrastructure; worker handles the higher-level auth-store and secret-store
|
||||
//! resolution.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -30,7 +30,7 @@ use secrets::{SecretStore, SecretValue};
|
||||
|
||||
pub use manifest::model_catalog::{ModelConfig, ResolveError as CatalogResolveError};
|
||||
|
||||
/// プロバイダ構築時のエラー。
|
||||
/// Errors during provider construction.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ProviderError {
|
||||
#[error("model configuration error: {0}")]
|
||||
@@ -56,7 +56,7 @@ pub enum ProviderError {
|
||||
ManifestResolve(#[from] catalog::ResolveError),
|
||||
}
|
||||
|
||||
/// `AuthRef` をランタイムで使える [`ResolvedAuth`] に解決する。
|
||||
/// Resolve `AuthRef` into runtime-usable [`ResolvedAuth`].
|
||||
fn resolve_auth(scheme: SchemeKind, auth: &AuthRef) -> Result<ResolvedAuth, ProviderError> {
|
||||
let resolver = DefaultSecretResolver;
|
||||
resolve_auth_with_resolver(scheme, auth, &resolver)
|
||||
@@ -123,9 +123,9 @@ fn resolve_auth_with_resolver(
|
||||
}
|
||||
}
|
||||
|
||||
/// `AuthRef::CodexOAuth` 指定時、`base_url` 未指定なら ChatGPT backend を既定とする。
|
||||
/// Codex CLI が使う `/backend-api/codex` を base に取り、scheme 側の `/responses`
|
||||
/// path と結合して `https://chatgpt.com/backend-api/codex/responses` になる。
|
||||
/// `AuthRef::CodexOAuth` defaults to the ChatGPT backend when `base_url` is unset.
|
||||
/// The Codex CLI base `/backend-api/codex` combines with the scheme's `/responses` path
|
||||
/// to form `https://chatgpt.com/backend-api/codex/responses`.
|
||||
fn effective_base_url<S: Scheme>(scheme: &S, config: &ModelConfig) -> String {
|
||||
if let Some(b) = &config.base_url {
|
||||
return b.clone();
|
||||
@@ -147,11 +147,11 @@ fn build_transport<S: Scheme>(
|
||||
scheme: config.scheme,
|
||||
});
|
||||
}
|
||||
// capability の優先順位 (上位 3 段は `ModelConfig` に既に反映済み):
|
||||
// 1. manifest 明示
|
||||
// Capability precedence (the first three levels are already reflected in `ModelConfig`):
|
||||
// 1. explicit manifest
|
||||
// 2. model catalog
|
||||
// 3. provider.default_capability
|
||||
// 4. `Scheme::default_capability()`(scheme ごとの wire-level 安全側)
|
||||
// 4. `Scheme::default_capability()` (scheme-specific conservative wire-level default)
|
||||
let capability: ModelCapability = config
|
||||
.capability
|
||||
.clone()
|
||||
@@ -191,9 +191,8 @@ fn build_from_config(config: &ModelConfig) -> Result<Box<dyn LlmClient>, Provide
|
||||
TransportPolicy::standard(),
|
||||
),
|
||||
SchemeKind::OpenaiResponses => {
|
||||
// ChatGPT backend (codex-oauth) は `max_output_tokens` /
|
||||
// `temperature` / `top_p` を 400 で弾くため、その経路では
|
||||
// 送出を止める。
|
||||
// The ChatGPT backend (codex-oauth) rejects `max_output_tokens`,
|
||||
// `temperature`, and `top_p` with HTTP 400; omit them on that path.
|
||||
let send_to_official = !matches!(config.auth, AuthRef::CodexOAuth);
|
||||
let scheme = OpenAIResponsesScheme::new()
|
||||
.with_send_max_output_tokens(send_to_official)
|
||||
@@ -208,17 +207,16 @@ fn build_from_config(config: &ModelConfig) -> Result<Box<dyn LlmClient>, Provide
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ModelManifest`] から [`LlmClient`] を構築する。ref / inline の
|
||||
/// いずれも受け取り、カタログ解決は内部で行う。
|
||||
/// Build an [`LlmClient`] from a [`ModelManifest`], accepting refs or inline configuration
|
||||
/// and resolving the catalog internally.
|
||||
pub fn build_client(manifest: &ModelManifest) -> Result<Box<dyn LlmClient>, ProviderError> {
|
||||
let config = catalog::resolve_model_manifest(manifest)?;
|
||||
build_from_config(&config)
|
||||
}
|
||||
|
||||
/// 既に解決済みの [`ModelConfig`] から [`LlmClient`] を構築する。
|
||||
/// `ModelManifest` から既に `catalog::resolve_model_manifest` を通した
|
||||
/// ケース(factory / spawn 経路でカタログ引きを 1 回だけにしたい等)で
|
||||
/// 使う。
|
||||
/// Build an [`LlmClient`] from an already resolved [`ModelConfig`].
|
||||
/// Use when `ModelManifest` has already passed through `catalog::resolve_model_manifest`,
|
||||
/// for example to resolve the catalog only once in factory / spawn paths.
|
||||
pub fn build_client_from_config(config: &ModelConfig) -> Result<Box<dyn LlmClient>, ProviderError> {
|
||||
build_from_config(config)
|
||||
}
|
||||
@@ -360,7 +358,7 @@ mod tests {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let _guard = ConfigDirGuard::new(dir.path());
|
||||
|
||||
// Ollama は AuthRef::None で構築できる end-to-end path。
|
||||
// End-to-end path building Ollama with AuthRef::None.
|
||||
let manifest = ModelManifest {
|
||||
ref_: Some("ollama-local/llama3.1".into()),
|
||||
..Default::default()
|
||||
@@ -379,7 +377,7 @@ mod tests {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let _guard = ConfigDirGuard::new(dir.path());
|
||||
|
||||
// Form C: 完全直書き。Ollama 相当を AuthRef::None で構築。
|
||||
// Form C: fully inline. Build an Ollama equivalent with AuthRef::None.
|
||||
let manifest = ModelManifest {
|
||||
scheme: Some(SchemeKind::Anthropic),
|
||||
base_url: Some("http://localhost:11434".into()),
|
||||
@@ -397,7 +395,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn ollama_config_succeeds_without_key() {
|
||||
// Ollama = Anthropic scheme + base_url 差し替え + AuthRef::None
|
||||
// Ollama = Anthropic scheme + overridden base_url + AuthRef::None
|
||||
let config = ModelConfig {
|
||||
scheme: SchemeKind::Anthropic,
|
||||
base_url: Some("http://localhost:11434".into()),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! Pure functions that decide whether scope rules collide.
|
||||
//!
|
||||
//! These helpers are read-only over [`LockFile`]; they never touch the
|
||||
//! file or the lock itself. The mutating operations in [`crate::mutate`]
|
||||
//! call them under the [`crate::LockFileGuard`].
|
||||
//! file or the lock itself. The mutating operations in `super::mutate`
|
||||
//! call them under the [`super::LockFileGuard`].
|
||||
|
||||
use manifest::{Permission, ScopeRule};
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ pub(crate) fn install_top_level_at_for_test(
|
||||
/// a spawning Worker.
|
||||
///
|
||||
/// The spawning flow is two-stage: the spawner calls
|
||||
/// [`crate::delegate_scope`] (with its own pid as a live placeholder,
|
||||
/// [`super::delegate_scope`] (with its own pid as a live placeholder,
|
||||
/// `segment_id = None`), then exec's the child; the child, once
|
||||
/// running, calls this function to rewrite the allocation's pid +
|
||||
/// segment_id to its own and claim the [`ScopeAllocationGuard`] so
|
||||
|
||||
@@ -85,9 +85,9 @@ pub struct Allocation {
|
||||
/// a top-level Worker started directly by a human.
|
||||
pub delegated_from: Option<String>,
|
||||
/// Segment ID this Worker is currently writing to. `None` means this
|
||||
/// is a pre-reservation made by a spawner via [`super::super::delegate_scope`]
|
||||
/// is a pre-reservation made by a spawner via [`super::delegate_scope`]
|
||||
/// before the child has come up; the child fills it in at
|
||||
/// [`crate::adopt_allocation`] time.
|
||||
/// [`super::adopt_allocation`] time.
|
||||
#[serde(default)]
|
||||
pub segment_id: Option<SegmentId>,
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ impl SegmentLogSink {
|
||||
///
|
||||
/// Used by restore paths that load a session's complete log into
|
||||
/// the mirror before any subscriber is connected. Callers that need
|
||||
/// to notify existing subscribers should use [`reset_with_initial`].
|
||||
/// to notify existing subscribers should use [`Self::reset_with_initial`].
|
||||
pub fn replace_silent(&self, entries: Vec<LogEntry>) {
|
||||
let mut mirror = self
|
||||
.inner
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
//! Workspace- and Memory-independent exploration of an immutable ordered session capture.
|
||||
//! Exploration domain for immutable Session captures, with no Workspace/Memory mutation capability.
|
||||
//!
|
||||
//! Hosts construct a capture from committed session items. The capture excludes reasoning,
|
||||
//! assigns append-stable `SessionEntryRef` values, and provides sparse overview, bounded
|
||||
//! range/search, read, and generic evidence projections without granting mutation authority.
|
||||
//! A capture is an ordered projection of committed entries; exploration excludes reasoning and
|
||||
//! system prompts. Overview, search, bounded reads, and evidence conversion share `SessionEntryRef`.
|
||||
//! References identify entries in the selected Session and cannot be reused for another subject.
|
||||
//! Appends preserve existing entry identities. Sparse real user/assistant anchors and intervening
|
||||
//! entry counts index long histories. Search with an empty filter also provides bounded range lists.
|
||||
//!
|
||||
//! Host-fixed snapshot exploration, Memory candidate evidence validation, and current Worker
|
||||
//! observation share only this domain; they do not enable or modify each other's Features.
|
||||
//! The provider owns authorization to recapture a current Worker. A capture itself proves neither
|
||||
//! completion nor approval and grants no business-mutation authority.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
@@ -22,6 +29,8 @@ const MAX_READ_MAX_ITEMS: usize = 80;
|
||||
const DEFAULT_READ_MAX_BYTES: usize = 32 * 1024;
|
||||
const OVERVIEW_ANCHOR_STRIDE: usize = 8;
|
||||
|
||||
/// Append-stable exploration reference derived from an entry identity in the selected Session.
|
||||
/// Shared by overview, search, read, and evidence conversion; it does not identify other Sessions' entries.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct SessionEntryRef(String);
|
||||
|
||||
@@ -1,11 +1,65 @@
|
||||
//! Explicit-opt-in Web Interface Protocol (WIP) Worker surface.
|
||||
//! Worker's Web Interface Protocol (WIP) projection and execution boundary.
|
||||
//!
|
||||
//! The adapter intentionally keeps authority in the existing Worker host. In WIP
|
||||
//! mode ordinary tools are mounted below `/tools`, removed from the model-visible
|
||||
//! tool list, and invoked through three stateful tree/inspect/invoke tools. The
|
||||
//! published WIP crates own protocol validation, HTTP encoding, Known Space, and
|
||||
//! operation lifecycle state; this module owns only the in-process transport and
|
||||
//! the compatibility projection into existing async `Tool` implementations.
|
||||
//! In WIP mode, `Tree`, `Inspect`, and `Invoke` access native projections and compatibility
|
||||
//! projections at `/tools/<registered-name>`, without duplicating individual Tools for the model.
|
||||
//! The public WIP crate owns Protocol types, codecs, Known Space, and call lifecycle; this module
|
||||
//! owns Host registration/resolution and connection to existing async Tools. Provider-specific
|
||||
//! projections belong to their Features.
|
||||
//!
|
||||
//! # Object ownership and added operations
|
||||
//!
|
||||
//! [`WipMountRegistry`] retains root namespace owners. Object existence, identity, and resolver
|
||||
//! ownership are separate from operations added by other Features. Dynamic mounts resolve only one
|
||||
//! immediate collection child segment; added operations replace neither existing Objects nor resolvers.
|
||||
//! Descriptors extending the same Interface must match format, documentation, and type declarations.
|
||||
//! Registration rejects duplicate operation names, routes owned elsewhere, and conflicting contracts
|
||||
//! for the same reference. Changing enabled operations changes the Interface validator, preventing
|
||||
//! execution from old observations.
|
||||
//!
|
||||
//! Native projections hide compatibility Tools only for the same semantic capability or an explicit
|
||||
//! claim. Feature names are not intermediate Objects; old `/features/...` routes are not aliases.
|
||||
//! Old routes in persisted history are not rewritten and remain distinct from current call targets.
|
||||
//!
|
||||
//! # References and call-time authority
|
||||
//!
|
||||
//! Exact `(scope, name)` pairs identify Interfaces. The same name in different scopes means different
|
||||
//! contracts; ancestry uses canonical path segments, not string prefixes. Self-scoped projections use
|
||||
//! the resolved Object path as scope, fixing Object, descriptor, validator, and handler from the same
|
||||
//! projection. Missing scope Object refs are never fabricated.
|
||||
//!
|
||||
//! The Host checks target, Object precondition, scope/membership/ref, Interface precondition, and
|
||||
//! arguments before dispatching to the fixed handler. Publication, Feature enablement, and observation
|
||||
//! do not grant execution authority; handlers recheck current provider capabilities and caller
|
||||
//! authority. Compatibility calls validate input against original JSON Schema and evaluate permission
|
||||
//! with original Tool names/arguments. `ask` is denied without an approval protocol. Workdir scope
|
||||
//! and Backend authorization remain in existing implementations.
|
||||
//!
|
||||
//! # Client lifetime and observations
|
||||
//!
|
||||
//! Each [`WipRuntime`] creates a Client, distinguishing sessions by endpoint and Host-supplied opaque
|
||||
//! security context. Observations stay within the Runtime, never shared or persisted across Workers.
|
||||
//! Restore requires reacquisition; reference strings in history are not cache or wire authority
|
||||
//! evidence. Ordinary compaction preserves the live Runtime but does not turn shortened conversation
|
||||
//! into observation authority.
|
||||
//!
|
||||
//! Acquisition, display, and calls are separate. Tree follows only indexable edges; unobserved
|
||||
//! children are not empty sets. Inspect displays only the selected Object or Interface in the
|
||||
//! official Text View. Displays that cannot meet completeness, freshness, or bounds fail rather than
|
||||
//! silently truncate. Displays are untrusted data; signatures are never reparsed as call targets.
|
||||
//! The binding defines details.
|
||||
//!
|
||||
//! # Post-dispatch outcomes and cancellation
|
||||
//!
|
||||
//! Route/schema/permission denial, stale, provider-confirmed cancelled, interrupted, post-dispatch
|
||||
//! disconnection, and unknown outcomes are distinct. Dispatched operations are never automatically
|
||||
//! rerun. Unknown mutation outcomes in particular require external state or provider receipt checks.
|
||||
//! Cancellation goes only to the operation and handler fixed for execution, without re-resolving the
|
||||
//! Object. Sending cancellation alone does not establish a terminal outcome; completion or future
|
||||
//! drop releases the execution binding.
|
||||
//!
|
||||
//! Native typed returns remain values; Entries and strings never trigger implicit extra acquisition.
|
||||
//! Later Inspect failures do not fail completed calls. Attachments and compatibility ToolOutput use
|
||||
//! existing Engine output/history paths; native attachments still require WIP return-value validation.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
//! Target-specific AI binding. Retrieval, display and invocation are separate:
|
||||
//! signatures are untrusted display data, never parsed back into call targets.
|
||||
//! Binding separating model-facing WIP acquisition, display, and calls.
|
||||
//!
|
||||
//! Tree succeeds only after acquiring all indexable edges within the requested depth; it does not
|
||||
//! scan Known Space by path prefix. Children at the depth boundary are unobserved, distinct from
|
||||
//! observed empty sets. Acquisition completion checks freshness, data, and edge completeness, not
|
||||
//! just `ensure`'s return value. Loading and insufficient capacity are reported as acquisition states,
|
||||
//! not resolved by overwriting or busy loops.
|
||||
//!
|
||||
//! Inspect displays an Object signature with ordered Interface references, or one Interface signature.
|
||||
//! It neither expands referenced descriptors automatically nor requires a prior Tree. Official Text
|
||||
//! View signatures remain unchanged; paths and structured references sit in the outer envelope.
|
||||
//! Rendering/envelope bound violations fail the whole display; incomplete contracts never return success.
|
||||
//!
|
||||
//! Invoke uses structured Interface references and named arguments. Missing, stale, or failed
|
||||
//! observations get at most one pre-dispatch acquisition attempt per target; dispatched calls never
|
||||
//! rerun. Explicit refresh updates only the selected Object/Interface, not referenced targets.
|
||||
//! Interrupted acquisition fails only its own request, never leaving Loading permanently.
|
||||
|
||||
use super::*;
|
||||
use wip_client::{InterfaceObservation, PreparedRequest};
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
//! Input-only compact Interface addresses. Never parse rendered signatures.
|
||||
//! Compact Interface addresses exclusively for Inspect input.
|
||||
//!
|
||||
//! `scope::name` preserves the explicit absolute scope and name exactly, rejecting scope inference,
|
||||
//! normalization, relative resolution, and operation suffixes. Special characters decode as JSON
|
||||
//! strings. Quoting an entire Object path containing `::` distinguishes it from an Interface address.
|
||||
//! Invalid Interface input never falls back to Object lookup; displayed signatures are not parsed.
|
||||
use super::{InterfaceReference, ToolError};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
//! Conservative JSON-shape-preserving types for the compatibility `input` value.
|
||||
//! The original JSON Schema remains authoritative and visible in Inspect.
|
||||
//! In particular, open records and JSON unions must not become closed records
|
||||
//! or WIP's tagged unions: that would reject/change valid original tool inputs.
|
||||
//! Projects compatibility Tool `input` into WIP types without changing JSON shape.
|
||||
//!
|
||||
//! Conservatively represents only closed records, homogeneous lists, primitives, and string enums.
|
||||
//! Local JSON Pointer expansion has depth/node limits; cycles, unsupported syntax, open records,
|
||||
//! and mixed unions remain `Json`. Replacing them with WIP closed records or tagged unions would
|
||||
//! reject inputs valid for the original Tool or change their shape.
|
||||
//!
|
||||
//! Original JSON Schema also stays in Inspect-visible parameter summaries for execution validation.
|
||||
//! Type projection does not replace schema constraints, rename fields, insert defaults, or add union
|
||||
//! wrappers. The parent WIP Host connects display and original schemas to execution.
|
||||
use super::{Documentation, Json, TypeExpr};
|
||||
use wip_protocol::{EnumCase, FieldDeclaration};
|
||||
|
||||
|
||||
+42
-22
@@ -89,8 +89,11 @@ const MAX_SUBMISSION_RECEIPTS: usize = 128;
|
||||
const MAX_INVOCATION_RECEIPTS: usize = 1024;
|
||||
const MAX_INVOCATION_IDENTITY_BYTES: usize = 256;
|
||||
|
||||
/// A durable start without a matching result is intentionally not retryable.
|
||||
/// This is a conservative fence, not an exactly-once business-operation ledger.
|
||||
/// Durable replay-prevention receipt retaining invocation start and a bounded result.
|
||||
///
|
||||
/// Stores the typed payload digest and qualified identity, not a copy of all arguments.
|
||||
/// A start without a result cannot rule out side effects, so reexecution is rejected.
|
||||
/// The same ID with a different payload is also rejected. This is not a business exactly-once ledger.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct InvocationReceipt {
|
||||
@@ -3105,7 +3108,7 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
/// Note: this constructor does **not** parse `manifest.worker.system_prompt`
|
||||
/// as a template. `Worker::from_manifest` is the production path for
|
||||
/// templated prompts; callers of `Worker::new` that want a template
|
||||
/// should parse it themselves and call [`set_system_prompt_template`].
|
||||
/// should parse it themselves and call [`Self::set_system_prompt_template`].
|
||||
pub async fn new(
|
||||
manifest: WorkerManifest,
|
||||
worker: Engine<C, Mutable, SessionHistoryMetadata>,
|
||||
@@ -3714,7 +3717,12 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
&self.store
|
||||
}
|
||||
|
||||
/// Rewind the current segment to just before the specified public user input.
|
||||
/// Rewinds the current Segment to just before the specified public user input.
|
||||
///
|
||||
/// Saves the display-history prefix and pending activation checkpoint in one Store replacement.
|
||||
/// Retains invocation-only receipts and recovery-blocked state, avoiding a failure window
|
||||
/// between truncation and checkpoint append. Rewinding history does not undo committed
|
||||
/// business side effects.
|
||||
pub async fn rewind_to(&mut self, entry_id: String) -> Result<RewindAppliedState, RewindError> {
|
||||
// Reject invalid targets before invoking rewrite hooks or quiescing tasks.
|
||||
// Resolve again under the append lock after the asynchronous barrier.
|
||||
@@ -3895,7 +3903,7 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
/// Snapshot of the cumulative LLM Usage measurement timeline.
|
||||
///
|
||||
/// One entry per LLM call. Restored on `restore` and appended in
|
||||
/// `persist_turn`. Used by token-accounting APIs in [`token_counter`].
|
||||
/// `persist_turn`. Used by token-accounting APIs in [`agen::token_counter`].
|
||||
/// Returns a clone since the underlying vector is shared with hooks
|
||||
/// running on the Engine.
|
||||
pub fn usage_history(&self) -> Vec<UsageRecord> {
|
||||
@@ -3905,14 +3913,6 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Snapshot of the extract (memory.extract) boundary pointer.
|
||||
///
|
||||
/// `None` means no extract has run yet on the current session — the
|
||||
/// next extract will start from entry 0. Updated by
|
||||
/// [`try_post_run_extract`](Self::try_post_run_extract) on success
|
||||
/// and reset by [`compact`](Self::compact) (the new compacted
|
||||
/// session has a fresh log with no `LogEntry::Extension` entries).
|
||||
/// Cheap clone via `Option<Clone>`.
|
||||
/// Snapshot of the typed user segments tracked alongside worker
|
||||
/// history. The K-th entry corresponds to the K-th `Item::user_message`
|
||||
/// derived from `LogEntry::AnnotatedUserInput` entries (post-compaction); seed
|
||||
@@ -4090,7 +4090,7 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
|
||||
/// Attach the broadcast sender used for typed lifecycle `Event`s.
|
||||
///
|
||||
/// The Controller wires this alongside [`attach_alerter`] so that
|
||||
/// The Controller wires this alongside [`Self::attach_alerter`] so that
|
||||
/// Worker-internal operations (currently: compaction) can surface
|
||||
/// progress to connected clients.
|
||||
pub fn attach_working_event_tx(&mut self, working_event_tx: broadcast::Sender<Event>) {
|
||||
@@ -4143,12 +4143,6 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Push a `Method::Notify` entry onto the pending buffer.
|
||||
///
|
||||
/// The notification will be appended to `worker.history` as an
|
||||
/// `Item::system_message` just before the next LLM request, via
|
||||
/// `WorkerInterceptor::pending_history_appends`. See [`NotifyBuffer`]
|
||||
/// for overflow behaviour and the lane-of-record rationale.
|
||||
/// Configure the fixed idle notification coalescing window.
|
||||
///
|
||||
/// The deadline is derived from the oldest durable notification receipt,
|
||||
@@ -4613,6 +4607,12 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolves Submit's Flow selector and prepares initial state and instruction projections.
|
||||
///
|
||||
/// Rejects duplicate active instances, multiple Flow segments, disabled Features, and Workspace
|
||||
/// mismatches. Source resolution is read-only and does not yet change the live instance.
|
||||
/// The caller commits typed input, the instruction history projection, and Flow state in one
|
||||
/// AnnotatedUserInput before updating in-memory state.
|
||||
fn prepare_flow_input(
|
||||
&self,
|
||||
input: Vec<Segment>,
|
||||
@@ -4932,6 +4932,16 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
self.handle_worker_result(result, history_before).await
|
||||
}
|
||||
|
||||
/// Processes committed Submit invocations in segment order and returns whether dependent LLM
|
||||
/// execution may proceed.
|
||||
///
|
||||
/// Persists start in the pending activation extension before calling the handler, then appends
|
||||
/// the result and updated receipt in one AnnotatedSystemItem. If result append fails, start
|
||||
/// remains, preventing reexecution of uncertain side effects within this process or after
|
||||
/// restore, Pause/Resume, or compaction. Exact replay uses saved results but revalidates semantics
|
||||
/// and authority before reusing successful context. Receipt exhaustion rejects untrackable
|
||||
/// execution rather than dropping old evidence. The first failure/unknown result stops later
|
||||
/// invocations and dependent LLM execution without rolling back earlier side effects.
|
||||
async fn execute_feature_invocations(&self, segments: &[Segment]) -> Result<bool, WorkerError>
|
||||
where
|
||||
St: Clone + 'static,
|
||||
@@ -6352,8 +6362,18 @@ impl<C: LlmClient + 'static, St: Store> Worker<C, St> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs one parent-owned observable compaction service and returns the new
|
||||
/// Segment ID. Lifecycle states are committed before they are broadcast.
|
||||
/// Saves a new Segment and switches history references through parent-owned compaction.
|
||||
///
|
||||
/// Completes pre-rewrite hooks and confirms background tasks stopped, then saves the summary
|
||||
/// and retained tail in a new Segment. Creation alone does not change current history; CAS of
|
||||
/// the metadata active reference is the commit point. Lock order is append barrier, allocation,
|
||||
/// then metadata, excluding Submit/Notify during the switch. If allocation update fails after
|
||||
/// CAS, rejects appends and retains exclusion while reporting the mismatch. Success aligns the
|
||||
/// append destination, history, and cache identity before publishing the new base to subscribers.
|
||||
///
|
||||
/// Confirms release of the compaction Internal Worker before activation. Cancellation or future
|
||||
/// drop alone does not prove release; unconfirmed cleanup blocks the next execution. Progress
|
||||
/// is a runtime observation, not a restartable durable workflow record.
|
||||
pub async fn compact(&mut self, retained_tokens: u64) -> Result<SegmentId, WorkerError> {
|
||||
self.compact_with_cancel(retained_tokens, None, CompactionTrigger::RequestThreshold)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user