@team-agent/installer 0.3.22 → 0.3.23
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
CHANGED
package/Cargo.toml
CHANGED
|
@@ -403,6 +403,7 @@ pub(super) fn resume_backing_exists_for_agent(
|
|
|
403
403
|
Provider::Claude | Provider::ClaudeCode => {
|
|
404
404
|
rollout_path_exists(rollout_path)
|
|
405
405
|
|| event_log_transcript_exists(workspace, agent_id.as_str(), session_id.as_str())
|
|
406
|
+
|| claude_project_transcript_exists(agent, session_id.as_str())
|
|
406
407
|
}
|
|
407
408
|
Provider::Copilot => copilot_session_store_has_session(session_id.as_str()),
|
|
408
409
|
Provider::GeminiCli | Provider::Fake => false,
|
|
@@ -448,6 +449,42 @@ fn event_transcript_path(event: &serde_json::Value) -> Option<PathBuf> {
|
|
|
448
449
|
.map(PathBuf::from)
|
|
449
450
|
}
|
|
450
451
|
|
|
452
|
+
/// E36 fix-B: a real Claude worker writes its session transcript to
|
|
453
|
+
/// `<claude_projects_root>/<workspace-slug>/<session_id>.jsonl` even when neither
|
|
454
|
+
/// `rollout_path` was persisted to state nor a `session.captured` event was logged.
|
|
455
|
+
/// That landed transcript is itself a valid resume backing — restart was wrongly
|
|
456
|
+
/// refusing resumable workers because it only checked the two paths above. Scan the
|
|
457
|
+
/// projects root recursively for `<session_id>.jsonl` (session_id is a unique UUID,
|
|
458
|
+
/// so we avoid recomputing the project-dir slug, which is brittle for non-ASCII
|
|
459
|
+
/// workspace paths).
|
|
460
|
+
fn claude_project_transcript_exists(agent: &serde_json::Value, session_id: &str) -> bool {
|
|
461
|
+
if session_id.is_empty() {
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
let projects_root = agent
|
|
465
|
+
.get("claude_projects_root")
|
|
466
|
+
.and_then(serde_json::Value::as_str)
|
|
467
|
+
.filter(|value| !value.is_empty())
|
|
468
|
+
.map(PathBuf::from)
|
|
469
|
+
.or_else(|| {
|
|
470
|
+
std::env::var_os("HOME").map(|home| PathBuf::from(home).join(".claude").join("projects"))
|
|
471
|
+
});
|
|
472
|
+
let Some(projects_root) = projects_root else {
|
|
473
|
+
return false;
|
|
474
|
+
};
|
|
475
|
+
if !projects_root.is_dir() {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
let transcript_name = format!("{session_id}.jsonl");
|
|
479
|
+
let Ok(project_dirs) = std::fs::read_dir(&projects_root) else {
|
|
480
|
+
return false;
|
|
481
|
+
};
|
|
482
|
+
project_dirs
|
|
483
|
+
.flatten()
|
|
484
|
+
.filter(|entry| entry.path().is_dir())
|
|
485
|
+
.any(|entry| entry.path().join(&transcript_name).is_file())
|
|
486
|
+
}
|
|
487
|
+
|
|
451
488
|
fn copilot_session_store_has_session(session_id: &str) -> bool {
|
|
452
489
|
let Some(home) = std::env::var_os("HOME").map(PathBuf::from) else {
|
|
453
490
|
return false;
|
|
@@ -908,3 +945,69 @@ pub(super) fn is_dynamic_agent(
|
|
|
908
945
|
.and_then(YamlValue::as_str)
|
|
909
946
|
.is_some_and(|s| !s.is_empty())
|
|
910
947
|
}
|
|
948
|
+
|
|
949
|
+
#[cfg(test)]
|
|
950
|
+
mod e36_transcript_backing_tests {
|
|
951
|
+
use super::*;
|
|
952
|
+
|
|
953
|
+
struct ScratchDir(PathBuf);
|
|
954
|
+
impl ScratchDir {
|
|
955
|
+
fn new(tag: &str) -> Self {
|
|
956
|
+
let pid = std::process::id();
|
|
957
|
+
let base = std::env::temp_dir().join(format!("ta-e36-{tag}-{pid}"));
|
|
958
|
+
std::fs::create_dir_all(&base).expect("scratch dir");
|
|
959
|
+
ScratchDir(base)
|
|
960
|
+
}
|
|
961
|
+
fn path(&self) -> &Path {
|
|
962
|
+
&self.0
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
impl Drop for ScratchDir {
|
|
966
|
+
fn drop(&mut self) {
|
|
967
|
+
let _ = std::fs::remove_dir_all(&self.0);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// E36 fix-B RED→GREEN: a real Claude worker that sent a message has its session
|
|
972
|
+
// transcript landed at <projects_root>/<slug>/<session_id>.jsonl, but neither
|
|
973
|
+
// rollout_path was persisted to state nor a session.captured event was logged.
|
|
974
|
+
// Before the fix, claude_project_transcript_exists did not exist and restart
|
|
975
|
+
// refused such a worker. This asserts the landed transcript is recognized.
|
|
976
|
+
#[test]
|
|
977
|
+
fn claude_project_transcript_is_recognized_without_rollout_or_capture_event() {
|
|
978
|
+
let scratch = ScratchDir::new("recognized");
|
|
979
|
+
let projects_root = scratch.path().join("projects");
|
|
980
|
+
let slug_dir = projects_root.join("-Users-alauda-Documents-code---rust---9");
|
|
981
|
+
std::fs::create_dir_all(&slug_dir).expect("mkdir slug");
|
|
982
|
+
let session_id = "87742d3f-0b4e-4fc1-ad35-447ac2340b65";
|
|
983
|
+
std::fs::write(slug_dir.join(format!("{session_id}.jsonl")), b"{}\n").expect("transcript");
|
|
984
|
+
|
|
985
|
+
let agent = serde_json::json!({
|
|
986
|
+
"claude_projects_root": projects_root.to_string_lossy(),
|
|
987
|
+
});
|
|
988
|
+
assert!(
|
|
989
|
+
claude_project_transcript_exists(&agent, session_id),
|
|
990
|
+
"landed claude transcript must count as resume backing (E36 fix-B)"
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
#[test]
|
|
995
|
+
fn missing_claude_transcript_is_not_backing() {
|
|
996
|
+
let scratch = ScratchDir::new("missing");
|
|
997
|
+
let projects_root = scratch.path().join("projects");
|
|
998
|
+
std::fs::create_dir_all(&projects_root).expect("mkdir");
|
|
999
|
+
let agent = serde_json::json!({
|
|
1000
|
+
"claude_projects_root": projects_root.to_string_lossy(),
|
|
1001
|
+
});
|
|
1002
|
+
assert!(
|
|
1003
|
+
!claude_project_transcript_exists(&agent, "deadbeef-0000-0000-0000-000000000000"),
|
|
1004
|
+
"no transcript file => no backing"
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
#[test]
|
|
1009
|
+
fn empty_session_id_is_not_backing() {
|
|
1010
|
+
let agent = serde_json::json!({});
|
|
1011
|
+
assert!(!claude_project_transcript_exists(&agent, ""));
|
|
1012
|
+
}
|
|
1013
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.23",
|
|
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.3.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.3.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.3.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.3.23",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.3.23",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.3.23"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|
|
@@ -158,6 +158,7 @@ For diagnosis, run `team-agent profile show deepseek --workspace . --json`; neve
|
|
|
158
158
|
- Quick-start generated files stay inside the selected team directory, for example `.team/current/` or `.team/alpha/`; do not create or expect root `team.spec.yaml` or `team_state.md`.
|
|
159
159
|
- Use `team-agent quick-start ./roles --team-id alpha` to create a second generated team under `.team/alpha/`, or pass an existing team directory directly such as `team-agent quick-start .team/alpha`.
|
|
160
160
|
- `quick-start` is for fresh startup from role docs. If stored worker context already exists for that runtime session, follow the returned `team-agent restart . --team <session>` action to resume it. Use `--fresh` only when the user explicitly accepts new blank worker contexts.
|
|
161
|
+
- If the user explicitly asks a worker to create or operate a nested child team, first read `references/team-in-team.md`. Child teams must use an independent child workspace, never the parent `.team/current`.
|
|
161
162
|
- `team-agent send --watch-result coder "Do the bounded task"` sends a direct worker message, returns after delivery, and lets the coordinator collect/report completion asynchronously.
|
|
162
163
|
- After `send --watch-result` succeeds, do not run `sleep`, `status`, `inbox`, or `collect` polling loops unless the user explicitly asks for diagnosis; the coordinator will notify the leader when the result arrives.
|
|
163
164
|
- `team-agent send --task task_initial "Start"` routes by task.
|
|
@@ -214,7 +215,7 @@ Removing a worker at runtime is the symmetric `team-agent remove-agent <agent> -
|
|
|
214
215
|
|
|
215
216
|
## Worker Protocol
|
|
216
217
|
|
|
217
|
-
Workers do not run nested Team Agent teams.
|
|
218
|
+
Workers normally do not run nested Team Agent teams. When the user or leader explicitly asks for a child team, follow `references/team-in-team.md`; otherwise workers only provide the target and content for progress, and a short completion summary at the end:
|
|
218
219
|
|
|
219
220
|
```text
|
|
220
221
|
team_orchestrator.send_message(to="leader", content="short progress or blocker")
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Team-In-Team Reference
|
|
2
|
+
|
|
3
|
+
This reference is for explicitly requested nested Team Agent teams. Use it only when the user or leader asks a worker to create or operate a child team. For normal work, a worker should stay inside its parent team and report through `team_orchestrator`.
|
|
4
|
+
|
|
5
|
+
## Safety Rules
|
|
6
|
+
|
|
7
|
+
- Use the Team Agent framework skill and `team-agent` CLI. Do not use a provider's built-in agent-team feature when the user asked for Team Agent.
|
|
8
|
+
- Never create a child team in the parent team's `.team/current` directory.
|
|
9
|
+
- Never edit, delete, or reuse files under the parent team's `.team/current`.
|
|
10
|
+
- Never shut down a team just because `.team/current` is already occupied. In a nested setup, that is probably the parent team; shutting it down can kill the main node.
|
|
11
|
+
- A child team needs its own workspace directory. The role-doc directory is not the child workspace.
|
|
12
|
+
- MCP tools are team-scoped. A worker must not try to widen `team_orchestrator.send_message` to another team.
|
|
13
|
+
|
|
14
|
+
## Child Workspace
|
|
15
|
+
|
|
16
|
+
Pick an independent directory before writing `TEAM.md` or agent role docs. A safe default is a child workspace under the parent workspace, but outside the parent `.team/current`:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
PARENT_WORKSPACE="$PWD"
|
|
20
|
+
case "$PARENT_WORKSPACE" in
|
|
21
|
+
*/.team/current|*/.team/current/*)
|
|
22
|
+
echo "Refusing to create a child team from parent .team/current; cd to the parent workspace root first." >&2
|
|
23
|
+
exit 1
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
CHILD_TEAM="child-review-team"
|
|
27
|
+
CHILD_WORKSPACE="$PARENT_WORKSPACE/.team/children/review-$(date +%Y%m%d%H%M%S)"
|
|
28
|
+
mkdir -p "$CHILD_WORKSPACE"
|
|
29
|
+
cd "$CHILD_WORKSPACE"
|
|
30
|
+
mkdir -p .team/current/agents
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then create the child team's files inside the child workspace:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cat > .team/current/TEAM.md <<'EOF'
|
|
37
|
+
---
|
|
38
|
+
name: child-review-team
|
|
39
|
+
objective: Run a bounded child-team task and report to the parent worker.
|
|
40
|
+
dangerous_auto_approve: false
|
|
41
|
+
fast: false
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
Child team config only.
|
|
45
|
+
EOF
|
|
46
|
+
|
|
47
|
+
cat > .team/current/agents/reviewer.md <<'EOF'
|
|
48
|
+
---
|
|
49
|
+
name: reviewer
|
|
50
|
+
role: Independent Reviewer
|
|
51
|
+
provider: codex
|
|
52
|
+
tools:
|
|
53
|
+
- fs_read
|
|
54
|
+
- fs_list
|
|
55
|
+
- mcp_team
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
Review only the files and instructions provided by the child-team leader.
|
|
59
|
+
Report findings with team_orchestrator.report_result exactly once.
|
|
60
|
+
EOF
|
|
61
|
+
|
|
62
|
+
team-agent quick-start .team/current
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If `quick-start` reports that `current` or a tmux session already exists, do not run `shutdown`. Move to a new child workspace or change the child team name.
|
|
66
|
+
|
|
67
|
+
## Parent Protection
|
|
68
|
+
|
|
69
|
+
The parent worker is also the child team's leader. Keep the two scopes separate:
|
|
70
|
+
|
|
71
|
+
- Parent team: the worker talks upward with its parent `team_orchestrator` MCP tools.
|
|
72
|
+
- Child team: the same worker talks downward with `team-agent send --workspace "$CHILD_WORKSPACE" --team "$CHILD_TEAM" ...`.
|
|
73
|
+
|
|
74
|
+
Do not run child lifecycle commands against the parent workspace. Always pass the child workspace and team when operating the child team from the parent worker.
|
|
75
|
+
|
|
76
|
+
## Parent-Child Messaging
|
|
77
|
+
|
|
78
|
+
Use explicit two-hop routing.
|
|
79
|
+
|
|
80
|
+
Parent leader to child worker:
|
|
81
|
+
|
|
82
|
+
1. Parent leader sends the child-task instruction to the parent worker that owns the child team.
|
|
83
|
+
2. Parent worker sends into the child team:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
team-agent send reviewer "Review this patch and report findings" \
|
|
87
|
+
--workspace "$CHILD_WORKSPACE" \
|
|
88
|
+
--team "$CHILD_TEAM" \
|
|
89
|
+
--watch-result
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Child worker to parent leader:
|
|
93
|
+
|
|
94
|
+
1. Child worker reports to its child leader:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
team_orchestrator.send_message(to="leader", content="short progress")
|
|
98
|
+
team_orchestrator.report_result(summary="short completion", status="success")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
2. The parent worker, acting as child leader, relays the relevant summary upward to the parent leader with its parent-team MCP tools.
|
|
102
|
+
|
|
103
|
+
Do not ask a child worker to send directly to a parent-team agent id. Current MCP scope is team-local and should refuse out-of-scope peers. Use CLI `--workspace` and `--team` only from the appropriate leader/operator side when crossing team boundaries.
|
|
104
|
+
|
|
105
|
+
## Clean-Context Roles
|
|
106
|
+
|
|
107
|
+
Asking for a clean-context role is a normal feature request when the user wants a blind reviewer, a fresh second opinion, or a role that forgets earlier conversation. It is not the same as failure recovery.
|
|
108
|
+
|
|
109
|
+
For an existing worker that should restart with a blank provider context, the leader can use:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
team-agent reset-agent "$AGENT_ID" --workspace "$WORKSPACE" --team "$TEAM" --discard-session --json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
or the equivalent restart form:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
team-agent restart-agent "$AGENT_ID" --workspace "$WORKSPACE" --team "$TEAM" --discard-session --json
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Use this only when the user intentionally wants a clean context. For incident recovery, follow `recovery-runbook.md`, where context-preserving repair comes first.
|
|
122
|
+
|
|
123
|
+
## Framework Gaps To Track
|
|
124
|
+
|
|
125
|
+
- Nested mode needs a way to pin the framework Team Agent skill by absolute path, so providers do not choose their built-in agent-team feature.
|
|
126
|
+
- A child-team worker should be prevented from shutting down the parent team by mistake.
|
|
127
|
+
- Child team creation should avoid defaulting to the fragile parent `current` directory.
|