@team-agent/installer 0.1.0

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.
Files changed (36) hide show
  1. package/README.md +201 -0
  2. package/crates/team-agent-core/Cargo.toml +12 -0
  3. package/crates/team-agent-core/src/lib.rs +287 -0
  4. package/crates/team-agent-core/src/main.rs +152 -0
  5. package/examples/team.spec.yaml +206 -0
  6. package/examples/team_state.md +35 -0
  7. package/npm/install.mjs +266 -0
  8. package/package.json +28 -0
  9. package/pyproject.toml +18 -0
  10. package/schemas/result-envelope.schema.json +76 -0
  11. package/schemas/team.schema.json +241 -0
  12. package/scripts/install.py +88 -0
  13. package/scripts/run_regression_tests.py +79 -0
  14. package/skills/team-agent/SKILL.md +173 -0
  15. package/src/team_agent/__init__.py +3 -0
  16. package/src/team_agent/__main__.py +5 -0
  17. package/src/team_agent/cli.py +857 -0
  18. package/src/team_agent/compiler.py +269 -0
  19. package/src/team_agent/coordinator.py +62 -0
  20. package/src/team_agent/errors.py +10 -0
  21. package/src/team_agent/events.py +37 -0
  22. package/src/team_agent/fake_worker.py +80 -0
  23. package/src/team_agent/mcp_server.py +579 -0
  24. package/src/team_agent/message_store.py +497 -0
  25. package/src/team_agent/paths.py +45 -0
  26. package/src/team_agent/permissions.py +123 -0
  27. package/src/team_agent/profiles.py +882 -0
  28. package/src/team_agent/providers.py +1045 -0
  29. package/src/team_agent/routing.py +84 -0
  30. package/src/team_agent/runtime.py +5213 -0
  31. package/src/team_agent/rust_core.py +156 -0
  32. package/src/team_agent/simple_yaml.py +236 -0
  33. package/src/team_agent/spec.py +308 -0
  34. package/src/team_agent/state.py +112 -0
  35. package/src/team_agent/task_graph.py +80 -0
  36. package/templates/team_state.md +32 -0
@@ -0,0 +1,241 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://teamspec.local/schemas/team.schema.json",
4
+ "title": "TeamSpec Agent Mode team.spec.yaml",
5
+ "type": "object",
6
+ "required": ["version", "team", "leader", "agents", "routing", "communication", "runtime", "context", "tasks"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "version": { "const": 1 },
10
+ "team": {
11
+ "type": "object",
12
+ "required": ["name", "mode", "objective", "workspace"],
13
+ "additionalProperties": false,
14
+ "properties": {
15
+ "name": { "type": "string", "minLength": 1 },
16
+ "mode": { "enum": ["supervisor_worker", "swarm_limited"] },
17
+ "objective": { "type": "string", "minLength": 1 },
18
+ "workspace": { "type": "string", "minLength": 1 }
19
+ }
20
+ },
21
+ "leader": { "$ref": "#/$defs/leader" },
22
+ "agents": {
23
+ "type": "array",
24
+ "minItems": 1,
25
+ "items": { "$ref": "#/$defs/agent" }
26
+ },
27
+ "routing": {
28
+ "type": "object",
29
+ "required": ["default_assignee", "rules"],
30
+ "additionalProperties": false,
31
+ "properties": {
32
+ "default_assignee": { "type": "string" },
33
+ "rules": {
34
+ "type": "array",
35
+ "items": { "$ref": "#/$defs/routingRule" }
36
+ }
37
+ }
38
+ },
39
+ "communication": {
40
+ "type": "object",
41
+ "required": ["protocol", "topology", "worker_to_worker", "ack_timeout_sec", "result_format", "message_store"],
42
+ "additionalProperties": false,
43
+ "properties": {
44
+ "protocol": { "enum": ["mcp_inbox", "file_bus"] },
45
+ "topology": { "enum": ["leader_centered"] },
46
+ "worker_to_worker": { "type": "boolean" },
47
+ "ack_timeout_sec": { "type": "integer", "minimum": 1 },
48
+ "result_format": { "const": "result_envelope_v1" },
49
+ "message_store": {
50
+ "type": "object",
51
+ "required": ["sqlite", "mirror_files"],
52
+ "additionalProperties": false,
53
+ "properties": {
54
+ "sqlite": { "type": "string" },
55
+ "mirror_files": { "type": "string" }
56
+ }
57
+ }
58
+ }
59
+ },
60
+ "runtime": {
61
+ "type": "object",
62
+ "required": ["backend", "display_backend", "session_name", "auto_launch", "require_user_approval_before_launch", "max_active_agents", "startup_order"],
63
+ "additionalProperties": false,
64
+ "properties": {
65
+ "backend": { "enum": ["tmux", "pty"] },
66
+ "display_backend": { "enum": ["none", "tmux_attach", "iterm", "ghostty"] },
67
+ "session_name": { "type": "string", "minLength": 1 },
68
+ "auto_launch": { "type": "boolean" },
69
+ "require_user_approval_before_launch": { "type": "boolean" },
70
+ "dangerous_auto_approve": { "type": "boolean" },
71
+ "max_active_agents": { "type": "integer", "minimum": 1 },
72
+ "startup_order": {
73
+ "type": "array",
74
+ "items": { "type": "string" }
75
+ }
76
+ }
77
+ },
78
+ "context": {
79
+ "type": "object",
80
+ "required": ["state_file", "artifact_dir", "log_dir", "summarization"],
81
+ "additionalProperties": false,
82
+ "properties": {
83
+ "state_file": { "type": "string" },
84
+ "artifact_dir": { "type": "string" },
85
+ "log_dir": { "type": "string" },
86
+ "summarization": {
87
+ "type": "object",
88
+ "required": ["worker_full_logs", "state_update"],
89
+ "additionalProperties": false,
90
+ "properties": {
91
+ "worker_full_logs": { "enum": ["retain_outside_leader_context"] },
92
+ "state_update": { "enum": ["after_each_result", "manual"] }
93
+ }
94
+ }
95
+ }
96
+ },
97
+ "tasks": {
98
+ "type": "array",
99
+ "items": { "$ref": "#/$defs/task" }
100
+ }
101
+ },
102
+ "$defs": {
103
+ "leader": {
104
+ "type": "object",
105
+ "required": ["id", "role", "provider", "model", "tools", "context_policy"],
106
+ "additionalProperties": false,
107
+ "properties": {
108
+ "id": { "type": "string", "minLength": 1 },
109
+ "role": { "type": "string", "minLength": 1 },
110
+ "provider": { "type": "string", "minLength": 1 },
111
+ "model": { "type": ["string", "null"] },
112
+ "tools": { "$ref": "#/$defs/tools" },
113
+ "context_policy": {
114
+ "type": "object",
115
+ "required": ["keep_user_thread", "receive_worker_outputs", "max_worker_result_tokens"],
116
+ "additionalProperties": false,
117
+ "properties": {
118
+ "keep_user_thread": { "type": "boolean" },
119
+ "receive_worker_outputs": { "enum": ["summary_only", "structured_only", "full_on_request"] },
120
+ "max_worker_result_tokens": { "type": "integer", "minimum": 1 }
121
+ }
122
+ }
123
+ }
124
+ },
125
+ "agent": {
126
+ "type": "object",
127
+ "required": ["id", "role", "provider", "model", "working_directory", "system_prompt", "tools", "permission_mode", "preferred_for", "avoid_for", "output_contract"],
128
+ "additionalProperties": false,
129
+ "properties": {
130
+ "id": { "type": "string", "minLength": 1 },
131
+ "role": { "type": "string", "minLength": 1 },
132
+ "provider": { "type": "string", "minLength": 1 },
133
+ "model": { "type": ["string", "null"] },
134
+ "auth_mode": { "enum": ["subscription", "official_api", "compatible_api"] },
135
+ "profile": { "type": "string", "minLength": 1 },
136
+ "credential_ref": { "type": "string", "minLength": 1 },
137
+ "working_directory": { "type": "string", "minLength": 1 },
138
+ "paused": { "type": "boolean" },
139
+ "system_prompt": {
140
+ "type": "object",
141
+ "required": ["inline", "file"],
142
+ "additionalProperties": false,
143
+ "properties": {
144
+ "inline": { "type": ["string", "null"] },
145
+ "file": { "type": ["string", "null"] }
146
+ }
147
+ },
148
+ "tools": { "$ref": "#/$defs/tools" },
149
+ "permission_mode": { "enum": ["restricted", "ask", "trusted"] },
150
+ "preferred_for": {
151
+ "type": "array",
152
+ "items": { "type": "string" }
153
+ },
154
+ "avoid_for": {
155
+ "type": "array",
156
+ "items": { "type": "string" }
157
+ },
158
+ "output_contract": {
159
+ "type": "object",
160
+ "required": ["format", "required_fields"],
161
+ "additionalProperties": false,
162
+ "properties": {
163
+ "format": { "const": "result_envelope_v1" },
164
+ "required_fields": {
165
+ "type": "array",
166
+ "items": { "type": "string" }
167
+ }
168
+ }
169
+ }
170
+ }
171
+ },
172
+ "routingRule": {
173
+ "type": "object",
174
+ "required": ["id", "assign_to", "priority"],
175
+ "additionalProperties": false,
176
+ "properties": {
177
+ "id": { "type": "string" },
178
+ "when": { "type": "string" },
179
+ "match": {
180
+ "type": "object",
181
+ "additionalProperties": false,
182
+ "properties": {
183
+ "type": { "$ref": "#/$defs/stringOrList" },
184
+ "files": { "$ref": "#/$defs/stringOrList" },
185
+ "risk": { "$ref": "#/$defs/stringOrList" },
186
+ "requires_tools": { "$ref": "#/$defs/stringOrList" }
187
+ }
188
+ },
189
+ "assign_to": { "type": "string" },
190
+ "priority": { "type": "integer" }
191
+ },
192
+ "anyOf": [
193
+ { "required": ["when"] },
194
+ { "required": ["match"] }
195
+ ]
196
+ },
197
+ "task": {
198
+ "type": "object",
199
+ "required": ["id", "title", "type", "assignee", "deps", "acceptance", "status"],
200
+ "additionalProperties": false,
201
+ "properties": {
202
+ "id": { "type": "string", "minLength": 1 },
203
+ "title": { "type": "string", "minLength": 1 },
204
+ "description": { "type": "string" },
205
+ "type": { "type": "string", "minLength": 1 },
206
+ "assignee": { "type": ["string", "null"] },
207
+ "deps": {
208
+ "type": "array",
209
+ "items": { "type": "string" }
210
+ },
211
+ "acceptance": {
212
+ "type": "array",
213
+ "items": { "type": "string" }
214
+ },
215
+ "status": { "enum": ["pending", "ready", "running", "blocked", "needs_retry", "done", "failed", "cancelled"] },
216
+ "requires_tools": {
217
+ "type": "array",
218
+ "items": { "type": "string" }
219
+ },
220
+ "files": {
221
+ "type": "array",
222
+ "items": { "type": "string" }
223
+ },
224
+ "risk": { "enum": ["low", "medium", "high"] },
225
+ "retry_limit": { "type": "integer", "minimum": 0 },
226
+ "human_confirmation": { "type": "boolean" }
227
+ }
228
+ },
229
+ "tools": {
230
+ "type": "array",
231
+ "items": { "type": "string" },
232
+ "uniqueItems": true
233
+ },
234
+ "stringOrList": {
235
+ "oneOf": [
236
+ { "type": "string" },
237
+ { "type": "array", "items": { "type": "string" } }
238
+ ]
239
+ }
240
+ }
241
+ }
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import glob
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ from pathlib import Path
9
+
10
+
11
+ def main() -> None:
12
+ parser = argparse.ArgumentParser(description="Install local team-agent wrapper commands")
13
+ parser.add_argument("--prefix", default=str(Path.home() / ".local"), help="Install prefix; bin/ is created under it")
14
+ parser.add_argument("--python", help="Python executable to embed in wrappers; defaults to TEAM_AGENT_PYTHON, python3, then python")
15
+ args = parser.parse_args()
16
+ repo = Path(__file__).resolve().parents[1]
17
+ python = _resolve_python(args.python)
18
+ bin_dir = Path(args.prefix) / "bin"
19
+ bin_dir.mkdir(parents=True, exist_ok=True)
20
+ _write_wrapper(bin_dir / "team-agent", repo, "team_agent", python)
21
+ _write_wrapper(bin_dir / "team_orchestrator", repo, "team_agent.mcp_server", python)
22
+ _write_wrapper(bin_dir / "team-agent-coordinator", repo, "team_agent.coordinator", python)
23
+ print(f"installed: {bin_dir / 'team-agent'}")
24
+ print(f"installed: {bin_dir / 'team_orchestrator'}")
25
+ print(f"installed: {bin_dir / 'team-agent-coordinator'}")
26
+ print(f"python: {python}")
27
+ print(f"ensure PATH contains: {bin_dir}")
28
+
29
+
30
+ def _resolve_python(explicit: str | None) -> str:
31
+ candidates = _python_candidates(explicit)
32
+ for candidate in candidates:
33
+ if not candidate:
34
+ continue
35
+ resolved = shutil.which(candidate) if os.path.basename(candidate) == candidate else candidate
36
+ if not resolved:
37
+ continue
38
+ proc = subprocess.run(
39
+ [
40
+ resolved,
41
+ "-c",
42
+ "import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)",
43
+ ],
44
+ text=True,
45
+ capture_output=True,
46
+ check=False,
47
+ )
48
+ if proc.returncode == 0:
49
+ return resolved
50
+ raise SystemExit("No usable Python >= 3.10 found. Set TEAM_AGENT_PYTHON or pass --python.")
51
+
52
+
53
+ def _python_candidates(explicit: str | None) -> list[str]:
54
+ candidates = [
55
+ explicit,
56
+ os.environ.get("TEAM_AGENT_PYTHON"),
57
+ "python3",
58
+ "python",
59
+ "/opt/homebrew/bin/python3",
60
+ "/usr/local/bin/python3",
61
+ "/usr/bin/python3",
62
+ "/opt/homebrew/opt/python@3/bin/python3",
63
+ "/usr/local/opt/python@3/bin/python3",
64
+ ]
65
+ candidates.extend(glob.glob("/opt/homebrew/opt/python@*/bin/python3*"))
66
+ candidates.extend(glob.glob("/usr/local/opt/python@*/bin/python3*"))
67
+ seen: set[str] = set()
68
+ result: list[str] = []
69
+ for item in candidates:
70
+ if not item or item in seen:
71
+ continue
72
+ seen.add(item)
73
+ result.append(item)
74
+ return result
75
+
76
+
77
+ def _write_wrapper(path: Path, repo: Path, module: str, python: str) -> None:
78
+ path.write_text(
79
+ "#!/usr/bin/env sh\n"
80
+ f'PYTHON_BIN="${{TEAM_AGENT_PYTHON:-{python}}}"\n'
81
+ f'PYTHONPATH="{repo / "src"}" exec "$PYTHON_BIN" -m {module} "$@"\n',
82
+ encoding="utf-8",
83
+ )
84
+ path.chmod(path.stat().st_mode | 0o755)
85
+
86
+
87
+ if __name__ == "__main__":
88
+ main()
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ from pathlib import Path
9
+
10
+
11
+ REGRESSION_TESTS = [
12
+ "tests.run_tests.RuntimeTests.test_send_default_timeout_reports_submitted_unverified",
13
+ "tests.run_tests.RuntimeTests.test_worker_delivery_retries_paste_until_message_ready",
14
+ "tests.run_tests.RuntimeTests.test_worker_pasted_content_prompt_retries_enter_until_submitted",
15
+ "tests.run_tests.RuntimeTests.test_worker_pasted_content_prompt_reports_unverified_when_enter_does_not_submit",
16
+ "tests.run_tests.RuntimeTests.test_delivery_claim_prevents_duplicate_worker_injection",
17
+ "tests.run_tests.RuntimeTests.test_approvals_returns_structured_prompt_without_terminal_page",
18
+ "tests.run_tests.RuntimeTests.test_coordinator_auto_approves_internal_mcp_prompt_with_retry_verification",
19
+ "tests.run_tests.RuntimeTests.test_coordinator_auto_approves_claude_internal_mcp_prompt",
20
+ "tests.run_tests.RuntimeTests.test_stale_approval_prompt_in_scrollback_is_not_current_approval",
21
+ "tests.run_tests.RuntimeTests.test_ghostty_display_session_is_linked_and_window_selected",
22
+ "tests.run_tests.RuntimeTests.test_shutdown_closes_ghostty_display_session_before_base_session_without_pid",
23
+ "tests.run_tests.RuntimeTests.test_restart_resumes_known_sessions_and_fresh_spawns_missing_sessions",
24
+ "tests.run_tests.RuntimeTests.test_restart_first_resume_exit_fallback_recreates_session_and_opens_display",
25
+ "tests.run_tests.RuntimeTests.test_start_agent_repairs_missing_worker_window_without_restart",
26
+ "tests.run_tests.RuntimeTests.test_start_agent_falls_back_to_fresh_when_resume_window_exits",
27
+ "tests.run_tests.RuntimeTests.test_broadcast_sends_only_to_current_team_and_excludes_sender",
28
+ "tests.run_tests.RuntimeTests.test_status_and_collect_expose_uncollected_report_result",
29
+ "tests.run_tests.RuntimeTests.test_report_result_queues_leader_notification_without_blocking_mcp",
30
+ "tests.run_tests.RuntimeTests.test_mcp_send_message_accepts_thin_args_and_returns_compact_result",
31
+ "tests.run_tests.RuntimeTests.test_mcp_send_message_accepts_broadcast_target",
32
+ "tests.run_tests.RuntimeTests.test_mcp_send_message_without_env_infers_worker_before_leader_send",
33
+ "tests.run_tests.RuntimeTests.test_mcp_report_result_without_env_infers_task_and_agent",
34
+ "tests.run_tests.RuntimeTests.test_mcp_report_result_normalizes_common_loose_shapes",
35
+ "tests.run_tests.RuntimeTests.test_compile_system_prompt_prepends_teammate_runtime_contract",
36
+ "tests.run_tests.RuntimeTests.test_codex_default_command_avoids_dangerous_bypass",
37
+ "tests.run_tests.RuntimeTests.test_launch_inherits_leader_dangerous_permissions_in_dry_run",
38
+ "tests.run_tests.RuntimeTests.test_launch_passes_inherited_dangerous_permissions_to_worker_runtime",
39
+ "tests.run_tests.RuntimeTests.test_restart_passes_inherited_dangerous_permissions_to_resume_and_fresh_workers",
40
+ "tests.run_tests.RuntimeTests.test_quick_start_refuses_to_overwrite_existing_context_without_fresh",
41
+ "tests.run_tests.RuntimeTests.test_quick_start_team_id_stores_loose_docs_outside_current",
42
+ "tests.run_tests.RuntimeTests.test_start_writes_compiled_spec_inside_selected_team_dir",
43
+ "tests.run_tests.RuntimeTests.test_preflight_uses_selected_team_profile_dir_not_current",
44
+ "tests.run_tests.RuntimeTests.test_restart_requires_team_selector_when_multiple_snapshots_exist",
45
+ "tests.run_tests.RuntimeTests.test_leader_start_plan_creates_tmux_session_outside_tmux_and_passes_args",
46
+ "tests.run_tests.RuntimeTests.test_leader_start_plan_inside_tmux_execs_provider_in_current_pane",
47
+ "tests.run_tests.RuntimeTests.test_launch_requires_current_tmux_leader_for_real_workers",
48
+ "tests.run_tests.RuntimeTests.test_resolve_leader_scans_workspace_when_tool_shell_has_wrong_tmux_client",
49
+ "tests.run_tests.RuntimeTests.test_resolve_leader_reports_ambiguous_workspace_panes",
50
+ "tests.run_tests.CliContractTests.test_leader_commands_pass_provider_flags_without_argparse_consuming_them",
51
+ "tests.run_tests.CliContractTests.test_npx_installer_installs_runtime_wrappers_and_skills",
52
+ "tests.run_tests.CliContractTests.test_skill_blackbox_lint",
53
+ ]
54
+
55
+
56
+ def main() -> int:
57
+ parser = argparse.ArgumentParser(description="Run the fixed Team Agent regression asset.")
58
+ parser.add_argument("--iterations", type=int, default=1, help="repeat the regression batch N times")
59
+ parser.add_argument("--list", action="store_true", help="print the selected unittest ids and exit")
60
+ args = parser.parse_args()
61
+ if args.iterations < 1:
62
+ parser.error("--iterations must be >= 1")
63
+ if args.list:
64
+ print("\n".join(REGRESSION_TESTS))
65
+ return 0
66
+
67
+ repo = Path(__file__).resolve().parents[1]
68
+ env = os.environ.copy()
69
+ env["PYTHONPATH"] = str(repo / "src") + os.pathsep + env.get("PYTHONPATH", "")
70
+ for index in range(1, args.iterations + 1):
71
+ print(f"[team-agent-regression] iteration {index}/{args.iterations}", flush=True)
72
+ proc = subprocess.run([sys.executable, "-m", "unittest", *REGRESSION_TESTS], cwd=repo, env=env, check=False)
73
+ if proc.returncode != 0:
74
+ return proc.returncode
75
+ return 0
76
+
77
+
78
+ if __name__ == "__main__":
79
+ raise SystemExit(main())
@@ -0,0 +1,173 @@
1
+ ---
2
+ name: team-agent
3
+ description: Use only when the user explicitly asks to start, operate, inspect, shutdown, or restart a Team Agent team. Treat the team-agent CLI as a sealed appliance.
4
+ ---
5
+
6
+ # Team Agent
7
+
8
+ Use this skill only for Team Agent operation. The leader is the current user-facing agent; do not create a `leader` worker. Worker role docs live in `<workspace>/agents/`; `TEAM.md` lives at `<workspace>/TEAM.md`.
9
+
10
+ ## Leader Requirement
11
+
12
+ Real Team Agent teams require the current leader to run inside a tmux-managed pane. Prefer the short launchers:
13
+
14
+ ```bash
15
+ team-agent codex
16
+ team-agent claude
17
+ ```
18
+
19
+ Pass provider flags after the provider name, for example `team-agent codex --dangerously-bypass-approvals-and-sandbox`. Existing tmux layouts are valid too, including Finder/Ghostty launchers, as long as `team-agent quick-start` is invoked from the leader's current tmux pane. Do not start a real team from a naked terminal that Team Agent cannot address through tmux.
20
+
21
+ ## Minimal Copy-Paste Team
22
+
23
+ ```bash
24
+ mkdir -p .team/current/agents
25
+ cat > .team/current/TEAM.md <<'EOF'
26
+ ---
27
+ name: demo-team
28
+ objective: One worker handles bounded tasks and reports through Team Agent MCP.
29
+ dangerous_auto_approve: false
30
+ display_backend: ghostty_window
31
+ fast: false
32
+ provider_models:
33
+ codex: gpt-5.5
34
+ claude: claude-sonnet-4-6
35
+ claude_code: claude-sonnet-4-6
36
+ ---
37
+
38
+ Team config only. This is not a worker role.
39
+ EOF
40
+ cat > .team/current/agents/coder.md <<'EOF'
41
+ ---
42
+ name: coder
43
+ role: Implementation Worker
44
+ provider: codex
45
+ auth_mode: subscription
46
+ profile: codex-default
47
+ tools:
48
+ - fs_read
49
+ - fs_list
50
+ - fs_write
51
+ - execute_bash
52
+ - mcp_team
53
+ - provider_builtin
54
+ ---
55
+
56
+ Handle one bounded task at a time. Send progress to leader only when needed. Final completion must call report_result exactly once; MCP fills task ids and result envelope fields.
57
+ EOF
58
+ team-agent quick-start .team/current
59
+ ```
60
+
61
+ YAML lists must be block style. Use `tools:\n - fs_read`; do not use `tools: [fs_read, mcp_team]`.
62
+ Omitting `display_backend` defaults to `ghostty_window`; set `display_backend: none` only for headless/CI runs.
63
+
64
+ ## Provider Prep
65
+
66
+ Codex: run `codex login` first. Optional `~/.codex/config.toml` profile:
67
+
68
+ ```toml
69
+ [profiles.team-agent]
70
+ model = "gpt-5.5"
71
+ approval_policy = "on-request"
72
+ sandbox_mode = "workspace-write"
73
+ ```
74
+
75
+ Use exact provider model ids, not display names. For Codex workers, the model must match a `slug` from `codex debug models`; for example use `gpt-5.3-codex-spark`, not `GPT-5.3-Codex-Spark`.
76
+ Role docs may omit `model` for subscription workers. Team Agent fills subscription defaults from `TEAM.md` `provider_models`, then built-in provider defaults (`codex: gpt-5.5`, `claude/claude_code: claude-sonnet-4-6`). Use role-level `model` only for intentional per-worker overrides.
77
+
78
+ Claude: run `claude auth status`; if missing, run `claude auth login`. Team Agent stores Claude worker sessions by passing `--session-id` and resumes with `--resume`.
79
+ Use `provider: claude` or `provider: claude_code` for Claude workers.
80
+
81
+ If the current leader process was started with `claude --dangerously-skip-permissions` or `codex --dangerously-bypass-approvals-and-sandbox`, Team Agent inherits that permission mode for worker launch, restart, and single-agent repair.
82
+ Role `profile` values are secret-safe references. Do not put API keys in role docs or `TEAM.md`.
83
+ Never read raw provider profile files into model context. Do not use `Read`, `cat`, `sed`, `grep`, editors, or screenshots on `.team/current/profiles/*.env` or `.team/runtime/provider-env/*.env`. Those files may contain live API keys. Use only `team-agent profile show <name> --workspace . --json` or `team-agent profile doctor <name> --workspace . --json` for redacted diagnostics; if a value is missing, ask the human user to edit the local profile file.
84
+ When the user asks for a third-party or compatible API, do not ask them to paste keys into the chat. Generate a local blank profile instead, for example:
85
+
86
+ ```bash
87
+ team-agent profile init deepseek --auth-mode compatible_api --workspace .
88
+ ```
89
+
90
+ Tell the user to fill `.team/current/profiles/deepseek.env` locally:
91
+
92
+ ```env
93
+ AUTH_MODE=compatible_api
94
+ PROFILE_NAME=deepseek
95
+ BASE_URL=
96
+ API_KEY=
97
+ MODEL=
98
+ ```
99
+
100
+ Then reference only `auth_mode: compatible_api` and `profile: deepseek` in role docs. Do not invent or duplicate a role `model` when the profile already has `MODEL=`; if both places define a model they must match exactly. Team Agent loads the profile automatically during quick-start, launch, restart, and start-agent. Compatible API workers inherit the current shell proxy/CA environment by default. Claude compatible API workers use Team Agent managed `CLAUDE_CONFIG_DIR` so user-level Claude subscription settings cannot re-inject Anthropic proxy variables into third-party API sessions. If quick-start reports an ambient proxy blocker, do not silently unset proxy for the whole team; tell the user to choose one path: fix that proxy for `BASE_URL`, put `HTTPS_PROXY=`/`HTTP_PROXY=` in the profile, or put `PROXY_MODE=direct` in the profile to bypass proxy only for that worker. Subscription workers keep their native provider settings and environment. Startup runs a redacted smoke check for compatible API profiles before worker windows are created, so a bad URL/key/model or proxy/base URL connectivity failure is reported to the leader command instead of producing idle workers.
101
+ For diagnosis, run `team-agent profile show deepseek --workspace . --json`; never open the `.env` file to check whether `API_KEY` or `MODEL` is filled.
102
+
103
+ ## Commands
104
+
105
+ - `team-agent codex ...` starts or attaches a tmux-managed Codex leader in the current directory; arguments after `codex` pass through to Codex.
106
+ - `team-agent claude ...` starts or attaches a tmux-managed Claude leader in the current directory; arguments after `claude` pass through to Claude.
107
+ - `team-agent quick-start .team/current` starts workers from `TEAM.md` and `agents/*.md`. When it prints `ready:` and `ready_signal`, startup is complete; do not run sleep/status/wait loops afterward unless diagnosing a failure.
108
+ - For real workers, `quick-start` requires a current tmux leader pane. If it says the leader must run inside tmux, restart the leader with `team-agent codex`/`team-agent claude` or use an existing tmux-managed layout, then run quick-start again.
109
+ - 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`.
110
+ - 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`.
111
+ - `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.
112
+ - `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.
113
+ - 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.
114
+ - `team-agent send --task task_initial "Start"` routes by task.
115
+ - `team-agent status` shows team, worker health, result-store counts, `session_id`, `captured_via`, and attribution confidence.
116
+ - `team-agent status coder` shows one worker.
117
+ - `team-agent approvals [coder]` shows structured pending approval prompts without copying worker terminal pages.
118
+ - `team-agent inbox coder` shows message history only. Final results are not in inbox.
119
+ - `team-agent shutdown --workspace . --keep-logs` stops the tmux session after a final session capture attempt.
120
+ - `team-agent restart .` restarts a stopped team from stored worker sessions. If one workspace has multiple restartable teams, use `team-agent restart . --team <session_name_or_team_name>`.
121
+ - `team-agent start-agent coder --workspace .` repairs one missing worker window without interrupting other workers.
122
+ - `team-agent doctor` checks local dependencies and provider auth hints.
123
+
124
+ ## Restart Semantics
125
+
126
+ `restart` takes one workspace argument. It preserves each worker's original provider. If a verified provider session exists, the worker resumes (`codex resume <id>` or `claude --resume <id>`). Claude sessions are considered resumable only after the provider has written a project transcript for that session; a freshly opened blank Claude window is not recorded as recovered context. If the stored id is stale, the runtime first tries to repair it from verified transcript history. If a stored session cannot be verified or repaired, restart fails closed instead of silently losing context; use `team-agent restart . --allow-fresh` only when the user explicitly accepts a fresh worker context. If multiple stopped teams in the same workspace have restart context, plain `team-agent restart .` fails and lists candidates; rerun with `--team <session_name_or_team_name>`. If no prior session id exists, that worker starts fresh and the event log records `restart.fresh_spawn`. Claude resume must run from the original cwd and the same provider transcript root; Team Agent stores `spawn_cwd` and compatible-API `claude_projects_root` for that.
127
+
128
+ Startup trust prompts are handled by the runtime/coordinator with bounded probes; do not wait on raw worker screens or manually press Enter for routine startup trust prompts.
129
+
130
+ Use `team-agent start-agent <agent_id> --workspace .` only as a narrow repair when one worker window is missing after launch/restart/display failure. It preserves the worker provider, resumes from `session_id` when available, starts fresh when there is no prior session id, and does not restart the rest of the team. If an existing session id cannot resume, it fails closed unless the user explicitly passes `--allow-fresh`. To change the team, edit role docs and start a new team or regenerate the compiled spec.
131
+
132
+ ## Worker Protocol
133
+
134
+ Workers do not run nested Team Agent teams. Workers only provide the target and content for progress, and a short completion summary at the end:
135
+
136
+ ```text
137
+ team_orchestrator.send_message(to="leader", content="short progress or blocker")
138
+ # to another teammate:
139
+ team_orchestrator.send_message(to="<agent_id>", content="short coordination note")
140
+ # to every other team member:
141
+ team_orchestrator.send_message(to="*", content="short broadcast")
142
+ team_orchestrator.report_result(summary="short completion", status="success", tests=[{"command":"command","status":"passed"}])
143
+ ```
144
+
145
+ Do not pass `sender`, `task_id`, `requires_ack`, `schema_version`, or `agent_id` unless doing a low-level compatibility diagnostic. The MCP runtime fills those fields and keeps delivery metadata in runtime state and event logs. If provider env loses the worker id, MCP infers it from active task/message state and falls back to an explicit `unknown` sender instead of treating the worker as leader.
146
+
147
+ Message targets are team-scoped. Use `leader`, another teammate agent id, or `*` for all other team members. The runtime excludes the sender from `*` broadcasts and never scans unrelated terminal windows for recipients.
148
+
149
+ `report_result` stores final completion and immediately attempts a leader notification through the verified/fallback delivery path. `team-agent collect` remains the authoritative state-update path. Do not wait for final results through `team-agent inbox`, message ack counts, or repeated plain status polling. `acknowledged_count` only means prior task messages were acknowledged by the worker; it is not a missing-result signal.
150
+ For normal leader dispatch, prefer `team-agent send --watch-result ...`; when it returns a registered watcher notice, the framework will notify the leader at completion.
151
+
152
+ For long processes, workers must write logs, keep a pid, provide a health check, and stop after a bounded number of retries. QA/reviewer roles must stay within their authorized files and stop on service unavailable, approval prompts, or repeated startup failure.
153
+
154
+ ## Failure Rules
155
+
156
+ For any non-zero `team-agent` exit, report the command, exit code, last about 20 stderr lines, and affected task or agent when known. Then stop and wait for the user.
157
+
158
+ Do not retry with changed flags. Do not inspect source code or private runtime state. Do not operate tmux directly except when the user asks for a manual diagnostic. Do not answer provider approval prompts for the user.
159
+
160
+ If `quick-start` reports `tmux session already exists`, treat it as a team-name collision. The existing session may be an active team; do not terminate it and do not suggest `shutdown` as the normal fix. Change `name:` in `TEAM.md` so the next launch uses a different tmux session name, then run `team-agent quick-start .team/current` again.
161
+
162
+ Known Team Agent control-plane MCP prompts such as `team_orchestrator.report_result` and `team_orchestrator.send_message` are handled by the coordinator. It uses session-scoped approval, verifies the prompt cleared, retries boundedly, and logs the result. Do not ask the user to approve those routine internal prompts.
163
+
164
+ When `status` still shows `AWAITING_APPROVAL`, run `team-agent approvals <agent_id>`, show the structured prompt summary and choices, ask the user to decide, and wait.
165
+
166
+ Do not inspect raw worker terminal output during normal operation. Use `team-agent status`, `team-agent approvals`, `team-agent inbox`, `team-agent collect`, and event logs instead. Raw-screen diagnostics are outside this skill's normal workflow, require explicit user authorization, and are guarded by the CLI; use them only as a one-shot bounded diagnostic, never as a routine workflow step.
167
+
168
+ For "worker reported but leader cannot see completion":
169
+
170
+ 1. Run `team-agent collect` once; this is the final-result intake path.
171
+ 2. If no result is collected, inspect `team-agent status --json` field `results`. `uncollected > 0` means the result is already accepted by MCP and waiting in the result store.
172
+ 3. Check `.team/logs/events.jsonl` for `mcp.report_result` and `collect.result` before sending another prompt to the worker.
173
+ 4. Do not loop on `team-agent inbox` or ack/status counts; that burns context and cannot consume final results.
@@ -0,0 +1,3 @@
1
+ """TeamSpec Agent Mode runtime."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ from team_agent.cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ main()