@team-agent/installer 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/src/coordinator/tick.rs +86 -16
- package/package.json +4 -4
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -2629,6 +2629,33 @@ fn agent_rollout_path(agent: &Value) -> Option<PathBuf> {
|
|
|
2629
2629
|
.map(PathBuf::from)
|
|
2630
2630
|
}
|
|
2631
2631
|
|
|
2632
|
+
/// Memory-growth fix (architect §5): bounded tail cap for JSONL activity reads.
|
|
2633
|
+
/// Matches `ABNORMAL_TAIL_BYTES` (131_072 bytes) — the abnormal-exit path
|
|
2634
|
+
/// already proved this size is sufficient to capture the latest lifecycle
|
|
2635
|
+
/// records across all providers (claude / codex / copilot).
|
|
2636
|
+
const JSONL_ACTIVITY_TAIL_BYTES: u64 = 131_072;
|
|
2637
|
+
|
|
2638
|
+
/// Memory-growth fix (architect §5): per-process `(path, size, mtime_ns) →
|
|
2639
|
+
/// activity` cache. When a rollout file hasn't changed since the previous
|
|
2640
|
+
/// tick, we skip the file read AND the classification entirely. This is the
|
|
2641
|
+
/// dominant savings: a 538MB Claude transcript that updates every few seconds
|
|
2642
|
+
/// is touched only when its size or mtime actually moves. Stored values are
|
|
2643
|
+
/// small (Option<AgentActivity> = enum + short rationale string); we never
|
|
2644
|
+
/// cache the transcript text or parsed JSON.
|
|
2645
|
+
struct JsonlActivityCacheEntry {
|
|
2646
|
+
size: u64,
|
|
2647
|
+
mtime_ns: u64,
|
|
2648
|
+
activity: Option<crate::messaging::AgentActivity>,
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
fn jsonl_activity_cache(
|
|
2652
|
+
) -> &'static std::sync::Mutex<std::collections::HashMap<PathBuf, JsonlActivityCacheEntry>> {
|
|
2653
|
+
static CACHE: std::sync::OnceLock<
|
|
2654
|
+
std::sync::Mutex<std::collections::HashMap<PathBuf, JsonlActivityCacheEntry>>,
|
|
2655
|
+
> = std::sync::OnceLock::new();
|
|
2656
|
+
CACHE.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2632
2659
|
/// E47 (0.3.24 P0, idle/busy 假阳): consult the authoritative provider JSONL
|
|
2633
2660
|
/// classifier and map to neutral `AgentActivity`. Returns `None` when the
|
|
2634
2661
|
/// classifier reports `TurnState::Unknown` (unreadable JSONL / no lifecycle
|
|
@@ -2638,29 +2665,72 @@ fn agent_rollout_path(agent: &Value) -> Option<PathBuf> {
|
|
|
2638
2665
|
/// so we hand off to the TUI scanner which has its OWN no-signal → Uncertain
|
|
2639
2666
|
/// path. Copilot/Gemini/Fake providers (which don't have JSONL — classify.rs
|
|
2640
2667
|
/// returns Unknown for them) thus keep using TUI scanning unchanged.
|
|
2668
|
+
///
|
|
2669
|
+
/// Memory-growth fix (architect analysis 2026-06-23): bounded tail read +
|
|
2670
|
+
/// metadata cache. Pre-fix `std::fs::read_to_string` on a 538MB Claude
|
|
2671
|
+
/// transcript every 5s caused 200MB+ coordinator RSS plateaus from allocator
|
|
2672
|
+
/// fragmentation. Now bounded to 128KiB tail and skipped entirely when
|
|
2673
|
+
/// (size, mtime_ns) is unchanged.
|
|
2641
2674
|
fn jsonl_activity_for_agent(agent: &Value) -> Option<crate::messaging::AgentActivity> {
|
|
2642
2675
|
let rollout_path = agent_rollout_path(agent)?;
|
|
2643
2676
|
let provider = agent
|
|
2644
2677
|
.get("provider")
|
|
2645
2678
|
.and_then(Value::as_str)
|
|
2646
2679
|
.and_then(parse_provider)?;
|
|
2647
|
-
|
|
2680
|
+
|
|
2681
|
+
// Metadata check + cache lookup. Cache hit when the rollout file has
|
|
2682
|
+
// not changed since the previous tick: return the cached classification
|
|
2683
|
+
// without re-reading the file. Truncation (size shrink with stable mtime)
|
|
2684
|
+
// still forces re-read because size is part of the cache key.
|
|
2685
|
+
let metadata = std::fs::metadata(&rollout_path).ok()?;
|
|
2686
|
+
let size = metadata.len();
|
|
2687
|
+
let mtime_ns = metadata_mtime_ns(&metadata)?;
|
|
2688
|
+
if let Ok(cache) = jsonl_activity_cache().lock() {
|
|
2689
|
+
if let Some(entry) = cache.get(&rollout_path) {
|
|
2690
|
+
if entry.size == size && entry.mtime_ns == mtime_ns {
|
|
2691
|
+
return entry.activity.clone();
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
// Cache miss: bounded tail read + classify. The classifier only needs the
|
|
2697
|
+
// latest lifecycle records to determine idle/working state; the
|
|
2698
|
+
// abnormal-exit path uses the same 128KiB tail and is sufficient for
|
|
2699
|
+
// claude / codex / copilot lifecycle markers.
|
|
2700
|
+
let log_text = read_tail_text(&rollout_path, JSONL_ACTIVITY_TAIL_BYTES).ok()?;
|
|
2648
2701
|
let process = explicit_process_liveness(agent).unwrap_or(ProcessLiveness::Unverifiable);
|
|
2649
|
-
let
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2702
|
+
let activity = crate::provider::classify(provider, &log_text, process, 0.0)
|
|
2703
|
+
.ok()
|
|
2704
|
+
.and_then(|result| {
|
|
2705
|
+
use crate::messaging::{ActivityStatus, AgentActivity};
|
|
2706
|
+
use crate::provider::types::TurnState;
|
|
2707
|
+
let status = match result.state {
|
|
2708
|
+
TurnState::Idle => ActivityStatus::Idle,
|
|
2709
|
+
TurnState::IdleInterrupted => ActivityStatus::Idle,
|
|
2710
|
+
TurnState::Working => ActivityStatus::Working,
|
|
2711
|
+
TurnState::BlockedOnHuman | TurnState::Abnormal => ActivityStatus::Uncertain,
|
|
2712
|
+
TurnState::Unknown => return None,
|
|
2713
|
+
};
|
|
2714
|
+
Some(AgentActivity {
|
|
2715
|
+
status,
|
|
2716
|
+
confidence: 0.95,
|
|
2717
|
+
rationale: format!("provider_jsonl:{}", result.reason),
|
|
2718
|
+
})
|
|
2719
|
+
});
|
|
2720
|
+
|
|
2721
|
+
// Store the classification (including None / Unknown) so the next tick
|
|
2722
|
+
// can short-circuit when the file is unchanged.
|
|
2723
|
+
if let Ok(mut cache) = jsonl_activity_cache().lock() {
|
|
2724
|
+
cache.insert(
|
|
2725
|
+
rollout_path,
|
|
2726
|
+
JsonlActivityCacheEntry {
|
|
2727
|
+
size,
|
|
2728
|
+
mtime_ns,
|
|
2729
|
+
activity: activity.clone(),
|
|
2730
|
+
},
|
|
2731
|
+
);
|
|
2732
|
+
}
|
|
2733
|
+
activity
|
|
2664
2734
|
}
|
|
2665
2735
|
|
|
2666
2736
|
fn runtime_approval_target(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "npx installer for Team Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codex",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"team-agent-installer": "npm/install.mjs"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"@team-agent/cli-darwin-arm64": "0.4.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.4.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.4.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.4.1",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.4.1",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.4.1"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|