@team-agent/installer 0.2.10 → 0.3.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.
- package/Cargo.lock +744 -0
- package/Cargo.toml +34 -0
- package/crates/team-agent/Cargo.toml +33 -0
- package/crates/team-agent/src/cli/adapters.rs +1343 -0
- package/crates/team-agent/src/cli/diagnose.rs +554 -0
- package/crates/team-agent/src/cli/emit.rs +1077 -0
- package/crates/team-agent/src/cli/helpers.rs +88 -0
- package/crates/team-agent/src/cli/leader.rs +216 -0
- package/crates/team-agent/src/cli/mod.rs +1141 -0
- package/crates/team-agent/src/cli/profile.rs +306 -0
- package/crates/team-agent/src/cli/send.rs +215 -0
- package/crates/team-agent/src/cli/status.rs +179 -0
- package/crates/team-agent/src/cli/status_port.rs +502 -0
- package/crates/team-agent/src/cli/tests/base.rs +616 -0
- package/crates/team-agent/src/cli/tests/compile.rs +96 -0
- package/crates/team-agent/src/cli/tests/divergence.rs +509 -0
- package/crates/team-agent/src/cli/tests/lane_c.rs +333 -0
- package/crates/team-agent/src/cli/tests/leader_watch.rs +395 -0
- package/crates/team-agent/src/cli/tests/main_preserved.rs +675 -0
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +390 -0
- package/crates/team-agent/src/cli/tests/mod.rs +97 -0
- package/crates/team-agent/src/cli/tests/peer_allow.rs +137 -0
- package/crates/team-agent/src/cli/tests/repair_state_byte_lock.rs +302 -0
- package/crates/team-agent/src/cli/tests/run_delegation.rs +305 -0
- package/crates/team-agent/src/cli/tests/status_send.rs +385 -0
- package/crates/team-agent/src/cli/tests/verb_profile.rs +182 -0
- package/crates/team-agent/src/cli/tests/verb_settle.rs +236 -0
- package/crates/team-agent/src/cli/tests/verb_validate.rs +184 -0
- package/crates/team-agent/src/cli/types.rs +605 -0
- package/crates/team-agent/src/compiler/tests.rs +701 -0
- package/crates/team-agent/src/compiler.rs +489 -0
- package/crates/team-agent/src/coordinator/backoff.rs +153 -0
- package/crates/team-agent/src/coordinator/health.rs +436 -0
- package/crates/team-agent/src/coordinator/mod.rs +80 -0
- package/crates/team-agent/src/coordinator/orphan.rs +179 -0
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +255 -0
- package/crates/team-agent/src/coordinator/tests/basics.rs +262 -0
- package/crates/team-agent/src/coordinator/tests/daemon.rs +323 -0
- package/crates/team-agent/src/coordinator/tests/health_sync.rs +263 -0
- package/crates/team-agent/src/coordinator/tests/main_preserved.rs +136 -0
- package/crates/team-agent/src/coordinator/tests/mod.rs +310 -0
- package/crates/team-agent/src/coordinator/tests/spine.rs +261 -0
- package/crates/team-agent/src/coordinator/tests/takeover.rs +227 -0
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +256 -0
- package/crates/team-agent/src/coordinator/tests/watch.rs +167 -0
- package/crates/team-agent/src/coordinator/tick.rs +2032 -0
- package/crates/team-agent/src/coordinator/types.rs +584 -0
- package/crates/team-agent/src/db/migration.rs +716 -0
- package/crates/team-agent/src/db/mod.rs +23 -0
- package/crates/team-agent/src/db/schema.rs +378 -0
- package/crates/team-agent/src/event_log.rs +375 -0
- package/crates/team-agent/src/fake_worker.rs +253 -0
- package/crates/team-agent/src/leader/helpers.rs +190 -0
- package/crates/team-agent/src/leader/inject.rs +33 -0
- package/crates/team-agent/src/leader/lease.rs +1063 -0
- package/crates/team-agent/src/leader/mod.rs +99 -0
- package/crates/team-agent/src/leader/owner_bind.rs +292 -0
- package/crates/team-agent/src/leader/rediscover/tests.rs +525 -0
- package/crates/team-agent/src/leader/rediscover.rs +1099 -0
- package/crates/team-agent/src/leader/start.rs +273 -0
- package/crates/team-agent/src/leader/takeover.rs +235 -0
- package/crates/team-agent/src/leader/tests/basics.rs +183 -0
- package/crates/team-agent/src/leader/tests/byte_findings.rs +234 -0
- package/crates/team-agent/src/leader/tests/identity.rs +206 -0
- package/crates/team-agent/src/leader/tests/idle.rs +271 -0
- package/crates/team-agent/src/leader/tests/lease_api.rs +225 -0
- package/crates/team-agent/src/leader/tests/lease_claim.rs +253 -0
- package/crates/team-agent/src/leader/tests/mod.rs +125 -0
- package/crates/team-agent/src/leader/tests/rediscover.rs +351 -0
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +204 -0
- package/crates/team-agent/src/leader/types.rs +487 -0
- package/crates/team-agent/src/lib.rs +85 -0
- package/crates/team-agent/src/lifecycle/display.rs +228 -0
- package/crates/team-agent/src/lifecycle/helpers.rs +112 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +227 -0
- package/crates/team-agent/src/lifecycle/launch.rs +1833 -0
- package/crates/team-agent/src/lifecycle/mod.rs +62 -0
- package/crates/team-agent/src/lifecycle/restart/agent.rs +533 -0
- package/crates/team-agent/src/lifecycle/restart/common.rs +517 -0
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +41 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +268 -0
- package/crates/team-agent/src/lifecycle/restart/remove.rs +780 -0
- package/crates/team-agent/src/lifecycle/restart/selection.rs +208 -0
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +242 -0
- package/crates/team-agent/src/lifecycle/restart.rs +76 -0
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +455 -0
- package/crates/team-agent/src/lifecycle/tests/core.rs +989 -0
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +583 -0
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +933 -0
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +265 -0
- package/crates/team-agent/src/lifecycle/tests.rs +27 -0
- package/crates/team-agent/src/lifecycle/types.rs +685 -0
- package/crates/team-agent/src/main.rs +41 -0
- package/crates/team-agent/src/mcp_server/helpers.rs +228 -0
- package/crates/team-agent/src/mcp_server/mod.rs +183 -0
- package/crates/team-agent/src/mcp_server/normalize.rs +312 -0
- package/crates/team-agent/src/mcp_server/tests/golden.rs +283 -0
- package/crates/team-agent/src/mcp_server/tests/normalize.rs +244 -0
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +189 -0
- package/crates/team-agent/src/mcp_server/tests/send.rs +222 -0
- package/crates/team-agent/src/mcp_server/tests/tools.rs +158 -0
- package/crates/team-agent/src/mcp_server/tests/wire.rs +159 -0
- package/crates/team-agent/src/mcp_server/tests.rs +38 -0
- package/crates/team-agent/src/mcp_server/tools.rs +603 -0
- package/crates/team-agent/src/mcp_server/types.rs +421 -0
- package/crates/team-agent/src/mcp_server/wire.rs +388 -0
- package/crates/team-agent/src/message_store.rs +767 -0
- package/crates/team-agent/src/messaging/activity.rs +433 -0
- package/crates/team-agent/src/messaging/delivery.rs +542 -0
- package/crates/team-agent/src/messaging/helpers.rs +209 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +340 -0
- package/crates/team-agent/src/messaging/mod.rs +147 -0
- package/crates/team-agent/src/messaging/peers.rs +32 -0
- package/crates/team-agent/src/messaging/results.rs +537 -0
- package/crates/team-agent/src/messaging/scheduler.rs +344 -0
- package/crates/team-agent/src/messaging/selftest.rs +100 -0
- package/crates/team-agent/src/messaging/send.rs +582 -0
- package/crates/team-agent/src/messaging/tests/basic.rs +357 -0
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +122 -0
- package/crates/team-agent/src/messaging/tests/mod.rs +293 -0
- package/crates/team-agent/src/messaging/tests/runtime.rs +1422 -0
- package/crates/team-agent/src/messaging/tests/spine.rs +437 -0
- package/crates/team-agent/src/messaging/trust.rs +192 -0
- package/crates/team-agent/src/messaging/types.rs +355 -0
- package/crates/team-agent/src/messaging/watchers.rs +591 -0
- package/crates/team-agent/src/model/enums.rs +311 -0
- package/crates/team-agent/src/model/errors.rs +17 -0
- package/crates/team-agent/src/model/ids.rs +155 -0
- package/crates/team-agent/src/model/mod.rs +22 -0
- package/crates/team-agent/src/model/paths.rs +228 -0
- package/crates/team-agent/src/model/permissions.rs +567 -0
- package/crates/team-agent/src/model/routing.rs +340 -0
- package/crates/team-agent/src/model/spec.rs +680 -0
- package/crates/team-agent/src/model/task_graph.rs +380 -0
- package/crates/team-agent/src/model/testdata/fuzz.golden.yaml +43 -0
- package/crates/team-agent/src/model/testdata/fuzz.yaml +43 -0
- package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +207 -0
- package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +206 -0
- package/crates/team-agent/src/model/testdata/team.spec.yaml +206 -0
- package/crates/team-agent/src/model/yaml/tests.rs +288 -0
- package/crates/team-agent/src/model/yaml.rs +800 -0
- package/crates/team-agent/src/packaging/install.rs +305 -0
- package/crates/team-agent/src/packaging/migrate.rs +30 -0
- package/crates/team-agent/src/packaging/mod.rs +82 -0
- package/crates/team-agent/src/packaging/repair.rs +24 -0
- package/crates/team-agent/src/packaging/tests.rs +829 -0
- package/crates/team-agent/src/packaging/types.rs +369 -0
- package/crates/team-agent/src/provider/adapter.rs +801 -0
- package/crates/team-agent/src/provider/approvals/mod.rs +2 -0
- package/crates/team-agent/src/provider/approvals/parsing.rs +452 -0
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +163 -0
- package/crates/team-agent/src/provider/classify.rs +456 -0
- package/crates/team-agent/src/provider/faults.rs +136 -0
- package/crates/team-agent/src/provider/helpers.rs +41 -0
- package/crates/team-agent/src/provider/mod.rs +53 -0
- package/crates/team-agent/src/provider/startup_prompt.rs +423 -0
- package/crates/team-agent/src/provider/tests/adapter.rs +239 -0
- package/crates/team-agent/src/provider/tests/classify.rs +240 -0
- package/crates/team-agent/src/provider/tests/faults.rs +120 -0
- package/crates/team-agent/src/provider/tests/idle.rs +208 -0
- package/crates/team-agent/src/provider/tests/wire.rs +213 -0
- package/crates/team-agent/src/provider/tests.rs +31 -0
- package/crates/team-agent/src/provider/types.rs +424 -0
- package/crates/team-agent/src/state/identity.rs +656 -0
- package/crates/team-agent/src/state/mod.rs +58 -0
- package/crates/team-agent/src/state/owner_gate.rs +423 -0
- package/crates/team-agent/src/state/persist.rs +712 -0
- package/crates/team-agent/src/state/projection.rs +657 -0
- package/crates/team-agent/src/state/selector.rs +105 -0
- package/crates/team-agent/src/state/testdata/state-rich.canonical.json +133 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +586 -0
- package/crates/team-agent/src/tmux_backend.rs +758 -0
- package/crates/team-agent/src/transport/test_support.rs +252 -0
- package/crates/team-agent/src/transport/tests/behavior.rs +327 -0
- package/crates/team-agent/src/transport/tests/mod.rs +199 -0
- package/crates/team-agent/src/transport/tests/wire.rs +527 -0
- package/crates/team-agent/src/transport.rs +774 -0
- package/npm/install.mjs +90 -106
- package/package.json +15 -13
- package/crates/team-agent-core/Cargo.toml +0 -12
- package/crates/team-agent-core/src/lib.rs +0 -332
- package/crates/team-agent-core/src/main.rs +0 -152
- package/pyproject.toml +0 -18
- package/scripts/install.py +0 -88
- package/scripts/run_regression_tests.py +0 -83
- package/src/team_agent/__init__.py +0 -3
- package/src/team_agent/__main__.py +0 -5
- package/src/team_agent/_legacy_pane_discovery.py +0 -186
- package/src/team_agent/abnormal_track.py +0 -253
- package/src/team_agent/approvals/__init__.py +0 -65
- package/src/team_agent/approvals/constants.py +0 -6
- package/src/team_agent/approvals/parsing.py +0 -176
- package/src/team_agent/approvals/runtime_prompts.py +0 -171
- package/src/team_agent/approvals/status.py +0 -176
- package/src/team_agent/cli/__init__.py +0 -137
- package/src/team_agent/cli/commands.py +0 -481
- package/src/team_agent/cli/e2e.py +0 -202
- package/src/team_agent/cli/helpers.py +0 -226
- package/src/team_agent/cli/parser.py +0 -540
- package/src/team_agent/compiler.py +0 -334
- package/src/team_agent/coordinator/__init__.py +0 -53
- package/src/team_agent/coordinator/__main__.py +0 -83
- package/src/team_agent/coordinator/lifecycle.py +0 -363
- package/src/team_agent/coordinator/metadata.py +0 -61
- package/src/team_agent/coordinator/paths.py +0 -17
- package/src/team_agent/diagnose/__init__.py +0 -48
- package/src/team_agent/diagnose/checks.py +0 -101
- package/src/team_agent/diagnose/comms.py +0 -213
- package/src/team_agent/diagnose/health.py +0 -241
- package/src/team_agent/diagnose/orphan_cleanup.py +0 -364
- package/src/team_agent/diagnose/preflight.py +0 -194
- package/src/team_agent/diagnose/quick_start.py +0 -324
- package/src/team_agent/display/__init__.py +0 -92
- package/src/team_agent/display/adaptive.py +0 -511
- package/src/team_agent/display/backend.py +0 -46
- package/src/team_agent/display/close.py +0 -154
- package/src/team_agent/display/ghostty.py +0 -77
- package/src/team_agent/display/rebuild.py +0 -102
- package/src/team_agent/display/tiling.py +0 -156
- package/src/team_agent/display/worker_window.py +0 -114
- package/src/team_agent/display/workspace.py +0 -382
- package/src/team_agent/errors.py +0 -10
- package/src/team_agent/events.py +0 -84
- package/src/team_agent/fake_worker.py +0 -80
- package/src/team_agent/idle_predicate.py +0 -200
- package/src/team_agent/idle_takeover.py +0 -59
- package/src/team_agent/idle_takeover_wiring.py +0 -111
- package/src/team_agent/launch/__init__.py +0 -41
- package/src/team_agent/launch/bootstrap.py +0 -85
- package/src/team_agent/launch/config.py +0 -106
- package/src/team_agent/launch/core.py +0 -301
- package/src/team_agent/launch/requirements.py +0 -57
- package/src/team_agent/leader/__init__.py +0 -926
- package/src/team_agent/leader_binding.py +0 -183
- package/src/team_agent/lifecycle/__init__.py +0 -5
- package/src/team_agent/lifecycle/agents.py +0 -278
- package/src/team_agent/lifecycle/operations.py +0 -411
- package/src/team_agent/lifecycle/paste_buffer_hygiene.py +0 -39
- package/src/team_agent/lifecycle/start.py +0 -363
- package/src/team_agent/mcp_server/__init__.py +0 -42
- package/src/team_agent/mcp_server/__main__.py +0 -7
- package/src/team_agent/mcp_server/contracts.py +0 -148
- package/src/team_agent/mcp_server/normalize.py +0 -257
- package/src/team_agent/mcp_server/server.py +0 -150
- package/src/team_agent/mcp_server/tools.py +0 -352
- package/src/team_agent/message_store/__init__.py +0 -23
- package/src/team_agent/message_store/agent_health.py +0 -113
- package/src/team_agent/message_store/core.py +0 -497
- package/src/team_agent/message_store/leader_notification_log.py +0 -198
- package/src/team_agent/message_store/result_watchers.py +0 -251
- package/src/team_agent/message_store/schema.py +0 -308
- package/src/team_agent/message_store/schema_migration.py +0 -448
- package/src/team_agent/messaging/__init__.py +0 -1
- package/src/team_agent/messaging/activity_detector.py +0 -254
- package/src/team_agent/messaging/delivery.py +0 -473
- package/src/team_agent/messaging/deps.py +0 -247
- package/src/team_agent/messaging/idle_alerts.py +0 -423
- package/src/team_agent/messaging/internal_delivery.py +0 -46
- package/src/team_agent/messaging/leader.py +0 -497
- package/src/team_agent/messaging/leader_api_errors.py +0 -216
- package/src/team_agent/messaging/leader_panes.py +0 -673
- package/src/team_agent/messaging/owner_bypass.py +0 -29
- package/src/team_agent/messaging/result_delivery.py +0 -539
- package/src/team_agent/messaging/results.py +0 -447
- package/src/team_agent/messaging/scheduler.py +0 -450
- package/src/team_agent/messaging/send.py +0 -532
- package/src/team_agent/messaging/session_drift.py +0 -94
- package/src/team_agent/messaging/tmux_io.py +0 -506
- package/src/team_agent/messaging/tmux_prompt.py +0 -338
- package/src/team_agent/messaging/trust_auto_answer.py +0 -52
- package/src/team_agent/orchestrator/__init__.py +0 -376
- package/src/team_agent/orchestrator/plan.py +0 -122
- package/src/team_agent/orchestrator/state.py +0 -128
- package/src/team_agent/paths.py +0 -45
- package/src/team_agent/permissions.py +0 -123
- package/src/team_agent/profiles/__init__.py +0 -82
- package/src/team_agent/profiles/constants.py +0 -19
- package/src/team_agent/profiles/core.py +0 -407
- package/src/team_agent/profiles/helpers.py +0 -69
- package/src/team_agent/profiles/provider_env.py +0 -188
- package/src/team_agent/profiles/smoke.py +0 -201
- package/src/team_agent/provider_cli/__init__.py +0 -43
- package/src/team_agent/provider_cli/adapter.py +0 -172
- package/src/team_agent/provider_cli/base.py +0 -48
- package/src/team_agent/provider_cli/claude.py +0 -457
- package/src/team_agent/provider_cli/codex.py +0 -336
- package/src/team_agent/provider_cli/copilot.py +0 -8
- package/src/team_agent/provider_cli/fake.py +0 -39
- package/src/team_agent/provider_cli/gemini.py +0 -95
- package/src/team_agent/provider_cli/opencode.py +0 -8
- package/src/team_agent/provider_cli/prompt.py +0 -62
- package/src/team_agent/provider_cli/registry.py +0 -18
- package/src/team_agent/provider_cli/unsupported.py +0 -32
- package/src/team_agent/provider_state/README.md +0 -78
- package/src/team_agent/provider_state/__init__.py +0 -86
- package/src/team_agent/provider_state/claude.py +0 -86
- package/src/team_agent/provider_state/codex.py +0 -84
- package/src/team_agent/provider_state/common.py +0 -207
- package/src/team_agent/provider_state/registry.py +0 -118
- package/src/team_agent/providers.py +0 -163
- package/src/team_agent/quality_gates.py +0 -104
- package/src/team_agent/restart/__init__.py +0 -34
- package/src/team_agent/restart/orchestration.py +0 -554
- package/src/team_agent/restart/selection.py +0 -89
- package/src/team_agent/restart/snapshot.py +0 -70
- package/src/team_agent/routing.py +0 -84
- package/src/team_agent/runtime.py +0 -1239
- package/src/team_agent/rust_core.py +0 -327
- package/src/team_agent/sessions/__init__.py +0 -25
- package/src/team_agent/sessions/capture.py +0 -143
- package/src/team_agent/sessions/inventory.py +0 -44
- package/src/team_agent/sessions/resume.py +0 -135
- package/src/team_agent/simple_yaml.py +0 -236
- package/src/team_agent/spec.py +0 -370
- package/src/team_agent/state.py +0 -602
- package/src/team_agent/status/__init__.py +0 -63
- package/src/team_agent/status/approvals.py +0 -52
- package/src/team_agent/status/compact.py +0 -158
- package/src/team_agent/status/constants.py +0 -18
- package/src/team_agent/status/inbox.py +0 -58
- package/src/team_agent/status/peek.py +0 -117
- package/src/team_agent/status/queries.py +0 -199
- package/src/team_agent/task_graph.py +0 -80
- package/src/team_agent/terminal.py +0 -57
- package/src/team_agent/wake.py +0 -58
- package/src/team_agent/watch/__init__.py +0 -145
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import re
|
|
4
|
-
import urllib.parse
|
|
5
|
-
from typing import Any
|
|
6
|
-
|
|
7
|
-
from team_agent.rust_core import redact_text
|
|
8
|
-
from team_agent.profiles.constants import SECRET_KEYS
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def _format_profile_check_failure(check: dict[str, Any]) -> str:
|
|
12
|
-
agent_id = check.get("agent_id") or "unknown"
|
|
13
|
-
profile = check.get("profile") or "-"
|
|
14
|
-
reason = check.get("reason") or "profile_invalid"
|
|
15
|
-
suggestion = check.get("suggestion") or f"Inspect safely with `team-agent profile show {profile} --workspace . --json`."
|
|
16
|
-
return f"profile validation failed for {agent_id} profile {profile}: {reason}. {suggestion}"
|
|
17
|
-
|
|
18
|
-
def _alternate_value(values: dict[str, str], key: str) -> str | None:
|
|
19
|
-
alternates = {
|
|
20
|
-
"BASE_URL": ["ANTHROPIC_BASE_URL", "OPENAI_BASE_URL"],
|
|
21
|
-
"API_KEY": ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "AUTH_TOKEN", "OPENAI_API_KEY", "GEMINI_API_KEY"],
|
|
22
|
-
}
|
|
23
|
-
for candidate in alternates.get(key, []):
|
|
24
|
-
if values.get(candidate):
|
|
25
|
-
return values[candidate]
|
|
26
|
-
return None
|
|
27
|
-
|
|
28
|
-
def _strip_env_value(value: str) -> str:
|
|
29
|
-
if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
|
|
30
|
-
return value[1:-1]
|
|
31
|
-
return value
|
|
32
|
-
|
|
33
|
-
def _safe_codex_provider_id(value: str) -> bool:
|
|
34
|
-
return re.fullmatch(r"[A-Za-z0-9_-]+", value) is not None
|
|
35
|
-
|
|
36
|
-
def _is_secret_key(key: str) -> bool:
|
|
37
|
-
upper = key.upper()
|
|
38
|
-
return upper in SECRET_KEYS or "KEY" in upper or "TOKEN" in upper or "SECRET" in upper
|
|
39
|
-
|
|
40
|
-
def _safe_profile_value(key: str, value: str) -> dict[str, Any]:
|
|
41
|
-
if _is_secret_key(key):
|
|
42
|
-
return {"present": bool(value), "redacted": True}
|
|
43
|
-
return {"present": bool(value), "redacted": False, "value": _safe_plain_profile_value(value)}
|
|
44
|
-
|
|
45
|
-
def _common_missing_values(auth_mode: str | None, values: dict[str, str]) -> list[str]:
|
|
46
|
-
if auth_mode == "compatible_api":
|
|
47
|
-
required = ["BASE_URL", "API_KEY", "MODEL"]
|
|
48
|
-
elif auth_mode == "official_api":
|
|
49
|
-
required = ["API_KEY"]
|
|
50
|
-
else:
|
|
51
|
-
required = []
|
|
52
|
-
missing = []
|
|
53
|
-
for key in required:
|
|
54
|
-
if key == "MODEL":
|
|
55
|
-
if not (values.get("MODEL") or values.get("ANTHROPIC_MODEL")):
|
|
56
|
-
missing.append(key)
|
|
57
|
-
continue
|
|
58
|
-
if not values.get(key) and not _alternate_value(values, key):
|
|
59
|
-
missing.append(key)
|
|
60
|
-
return missing
|
|
61
|
-
|
|
62
|
-
def _safe_plain_profile_value(value: str) -> str:
|
|
63
|
-
parsed = urllib.parse.urlparse(value)
|
|
64
|
-
if parsed.scheme and parsed.netloc:
|
|
65
|
-
host = parsed.hostname or ""
|
|
66
|
-
port = f":{parsed.port}" if parsed.port else ""
|
|
67
|
-
auth = "[redacted]@" if parsed.username or parsed.password else ""
|
|
68
|
-
value = urllib.parse.urlunparse((parsed.scheme, f"{auth}{host}{port}", parsed.path, "", "", ""))
|
|
69
|
-
return str(redact_text(value).get("text") or "")
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import shlex
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
from team_agent.profiles.constants import COMPATIBLE_API_NETWORK_ENV_KEYS
|
|
9
|
-
from team_agent.profiles.helpers import _safe_codex_provider_id
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _provider_env_exports(provider: str, auth_mode: str, values: dict[str, str]) -> dict[str, str]:
|
|
13
|
-
if auth_mode == "subscription":
|
|
14
|
-
return {}
|
|
15
|
-
if provider in {"claude", "claude_code"}:
|
|
16
|
-
exports: dict[str, str] = {}
|
|
17
|
-
base_url = values.get("ANTHROPIC_BASE_URL") or values.get("BASE_URL")
|
|
18
|
-
api_key = values.get("ANTHROPIC_API_KEY") or values.get("API_KEY")
|
|
19
|
-
auth_token = values.get("ANTHROPIC_AUTH_TOKEN") or values.get("AUTH_TOKEN")
|
|
20
|
-
model = values.get("ANTHROPIC_MODEL") or values.get("MODEL")
|
|
21
|
-
if base_url:
|
|
22
|
-
exports["ANTHROPIC_BASE_URL"] = base_url
|
|
23
|
-
if auth_mode == "official_api" and api_key:
|
|
24
|
-
exports["ANTHROPIC_API_KEY"] = api_key
|
|
25
|
-
if auth_token or (auth_mode == "compatible_api" and api_key):
|
|
26
|
-
exports["ANTHROPIC_AUTH_TOKEN"] = auth_token or api_key
|
|
27
|
-
if model:
|
|
28
|
-
exports["ANTHROPIC_MODEL"] = model
|
|
29
|
-
return exports
|
|
30
|
-
if provider == "codex":
|
|
31
|
-
exports = {}
|
|
32
|
-
api_key = values.get("OPENAI_API_KEY") or values.get("API_KEY")
|
|
33
|
-
if api_key:
|
|
34
|
-
exports["TEAM_AGENT_PROVIDER_API_KEY"] = api_key
|
|
35
|
-
exports.setdefault("OPENAI_API_KEY", api_key)
|
|
36
|
-
if values.get("BASE_URL"):
|
|
37
|
-
exports["OPENAI_BASE_URL"] = values["BASE_URL"]
|
|
38
|
-
return exports
|
|
39
|
-
if provider == "gemini_cli":
|
|
40
|
-
api_key = values.get("GEMINI_API_KEY") or values.get("API_KEY")
|
|
41
|
-
return {"GEMINI_API_KEY": api_key} if api_key else {}
|
|
42
|
-
return {}
|
|
43
|
-
|
|
44
|
-
def _provider_env_unsets(provider: str, auth_mode: str) -> list[str]:
|
|
45
|
-
unsets: list[str] = []
|
|
46
|
-
if provider in {"claude", "claude_code"}:
|
|
47
|
-
if auth_mode == "compatible_api":
|
|
48
|
-
unsets.append("ANTHROPIC_API_KEY")
|
|
49
|
-
if auth_mode == "official_api":
|
|
50
|
-
unsets.append("ANTHROPIC_AUTH_TOKEN")
|
|
51
|
-
if provider == "codex" and auth_mode == "compatible_api":
|
|
52
|
-
unsets.extend(["OPENAI_API_KEY", "OPENAI_BASE_URL"])
|
|
53
|
-
if provider == "gemini_cli" and auth_mode == "compatible_api":
|
|
54
|
-
unsets.append("GEMINI_API_KEY")
|
|
55
|
-
return sorted(set(unsets))
|
|
56
|
-
|
|
57
|
-
def _provider_command_overrides(provider: str, auth_mode: str, values: dict[str, str], agent: dict[str, Any]) -> dict[str, Any]:
|
|
58
|
-
overrides: dict[str, Any] = {}
|
|
59
|
-
model = agent.get("model") or values.get("MODEL") or values.get("ANTHROPIC_MODEL")
|
|
60
|
-
if model:
|
|
61
|
-
overrides["model"] = str(model)
|
|
62
|
-
if provider == "codex":
|
|
63
|
-
codex_profile = values.get("CODEX_PROFILE") or values.get("NATIVE_PROFILE")
|
|
64
|
-
if codex_profile:
|
|
65
|
-
overrides["codex_profile"] = codex_profile
|
|
66
|
-
configs: list[str] = []
|
|
67
|
-
model_provider = values.get("MODEL_PROVIDER")
|
|
68
|
-
base_url = values.get("BASE_URL")
|
|
69
|
-
if auth_mode == "compatible_api" and model_provider and base_url and _safe_codex_provider_id(model_provider):
|
|
70
|
-
configs.append(f'model_provider="{model_provider}"')
|
|
71
|
-
prefix = f"model_providers.{model_provider}"
|
|
72
|
-
configs.append(f'{prefix}.base_url="{base_url}"')
|
|
73
|
-
configs.append(f'{prefix}.env_key="TEAM_AGENT_PROVIDER_API_KEY"')
|
|
74
|
-
if values.get("WIRE_API"):
|
|
75
|
-
configs.append(f'{prefix}.wire_api="{values["WIRE_API"]}"')
|
|
76
|
-
if values.get("PROVIDER_NAME"):
|
|
77
|
-
configs.append(f'{prefix}.name="{values["PROVIDER_NAME"]}"')
|
|
78
|
-
if configs:
|
|
79
|
-
overrides["codex_config"] = configs
|
|
80
|
-
return overrides
|
|
81
|
-
|
|
82
|
-
def _write_runtime_env_file(workspace: Path, agent_id: str, exports: dict[str, str], unsets: list[str]) -> Path:
|
|
83
|
-
directory = workspace / ".team" / "runtime" / "provider-env"
|
|
84
|
-
directory.mkdir(parents=True, exist_ok=True)
|
|
85
|
-
path = directory / f"{agent_id}.env"
|
|
86
|
-
lines = [f"unset {key}" for key in sorted(unsets)]
|
|
87
|
-
lines.extend(f"export {key}={shlex.quote(value)}" for key, value in sorted(exports.items()))
|
|
88
|
-
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
89
|
-
try:
|
|
90
|
-
path.chmod(0o600)
|
|
91
|
-
except OSError:
|
|
92
|
-
pass
|
|
93
|
-
return path
|
|
94
|
-
|
|
95
|
-
def _compatible_claude_config_dir(workspace: Path, agent_id: str) -> Path:
|
|
96
|
-
directory = workspace / ".team" / "runtime" / "provider-config" / agent_id / "claude"
|
|
97
|
-
directory.mkdir(parents=True, exist_ok=True)
|
|
98
|
-
_ensure_compatible_claude_config(directory, workspace)
|
|
99
|
-
return directory
|
|
100
|
-
|
|
101
|
-
def ensure_compatible_claude_mcp_config(workspace: Path, agent_id: str, mcp_config: dict[str, Any]) -> None:
|
|
102
|
-
if not mcp_config:
|
|
103
|
-
return
|
|
104
|
-
directory = _compatible_claude_config_dir(workspace, agent_id)
|
|
105
|
-
state_path = directory / ".claude.json"
|
|
106
|
-
state = _read_json_object(state_path)
|
|
107
|
-
projects = state.setdefault("projects", {})
|
|
108
|
-
if not isinstance(projects, dict):
|
|
109
|
-
projects = {}
|
|
110
|
-
state["projects"] = projects
|
|
111
|
-
for project_key in _claude_project_keys(workspace):
|
|
112
|
-
project = projects.setdefault(project_key, {})
|
|
113
|
-
if not isinstance(project, dict):
|
|
114
|
-
project = {}
|
|
115
|
-
projects[project_key] = project
|
|
116
|
-
project["hasTrustDialogAccepted"] = True
|
|
117
|
-
project.setdefault("projectOnboardingSeenCount", 1)
|
|
118
|
-
project.setdefault("allowedTools", [])
|
|
119
|
-
project.setdefault("mcpContextUris", [])
|
|
120
|
-
project.setdefault("enabledMcpjsonServers", [])
|
|
121
|
-
project.setdefault("disabledMcpjsonServers", [])
|
|
122
|
-
project.setdefault("hasClaudeMdExternalIncludesApproved", False)
|
|
123
|
-
project.setdefault("hasClaudeMdExternalIncludesWarningShown", False)
|
|
124
|
-
servers = project.setdefault("mcpServers", {})
|
|
125
|
-
if not isinstance(servers, dict):
|
|
126
|
-
servers = {}
|
|
127
|
-
project["mcpServers"] = servers
|
|
128
|
-
servers.update(mcp_config)
|
|
129
|
-
_write_json(state_path, state)
|
|
130
|
-
|
|
131
|
-
def _ensure_compatible_claude_config(directory: Path, workspace: Path) -> None:
|
|
132
|
-
settings_path = directory / "settings.json"
|
|
133
|
-
settings = _read_json_object(settings_path)
|
|
134
|
-
settings.setdefault("theme", "auto")
|
|
135
|
-
settings.setdefault("skipDangerousModePermissionPrompt", True)
|
|
136
|
-
_write_json(settings_path, settings)
|
|
137
|
-
|
|
138
|
-
state_path = directory / ".claude.json"
|
|
139
|
-
state = _read_json_object(state_path)
|
|
140
|
-
state["hasCompletedOnboarding"] = True
|
|
141
|
-
state.setdefault("lastOnboardingVersion", "2.1.0")
|
|
142
|
-
state.setdefault("firstStartTime", "1970-01-01T00:00:00.000Z")
|
|
143
|
-
state.setdefault("numStartups", 0)
|
|
144
|
-
projects = state.get("projects")
|
|
145
|
-
if not isinstance(projects, dict):
|
|
146
|
-
projects = {}
|
|
147
|
-
state["projects"] = projects
|
|
148
|
-
for project_key in _claude_project_keys(workspace):
|
|
149
|
-
project = projects.get(project_key)
|
|
150
|
-
if not isinstance(project, dict):
|
|
151
|
-
project = {}
|
|
152
|
-
projects[project_key] = project
|
|
153
|
-
project["hasTrustDialogAccepted"] = True
|
|
154
|
-
project.setdefault("projectOnboardingSeenCount", 1)
|
|
155
|
-
_write_json(state_path, state)
|
|
156
|
-
|
|
157
|
-
def _claude_project_keys(workspace: Path) -> list[str]:
|
|
158
|
-
keys = [str(workspace)]
|
|
159
|
-
try:
|
|
160
|
-
resolved = str(workspace.resolve())
|
|
161
|
-
except OSError:
|
|
162
|
-
resolved = None
|
|
163
|
-
if resolved and resolved not in keys:
|
|
164
|
-
keys.append(resolved)
|
|
165
|
-
return keys
|
|
166
|
-
|
|
167
|
-
def _read_json_object(path: Path) -> dict[str, Any]:
|
|
168
|
-
try:
|
|
169
|
-
data = json.loads(path.read_text(encoding="utf-8"))
|
|
170
|
-
except (OSError, json.JSONDecodeError):
|
|
171
|
-
return {}
|
|
172
|
-
return data if isinstance(data, dict) else {}
|
|
173
|
-
|
|
174
|
-
def _write_json(path: Path, data: dict[str, Any]) -> None:
|
|
175
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
176
|
-
path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
177
|
-
try:
|
|
178
|
-
path.chmod(0o600)
|
|
179
|
-
except OSError:
|
|
180
|
-
pass
|
|
181
|
-
|
|
182
|
-
def _compatible_api_network_exports(auth_mode: str, values: dict[str, str]) -> dict[str, str]:
|
|
183
|
-
if auth_mode != "compatible_api" or _profile_proxy_mode(values) == "direct":
|
|
184
|
-
return {}
|
|
185
|
-
return {key: values[key] for key in COMPATIBLE_API_NETWORK_ENV_KEYS if values.get(key)}
|
|
186
|
-
|
|
187
|
-
def _profile_proxy_mode(values: dict[str, str]) -> str:
|
|
188
|
-
return str(values.get("PROXY_MODE") or values.get("NETWORK_MODE") or "inherit").strip().lower()
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from contextlib import contextmanager
|
|
4
|
-
import json
|
|
5
|
-
import os
|
|
6
|
-
import urllib.error
|
|
7
|
-
import urllib.parse
|
|
8
|
-
import urllib.request
|
|
9
|
-
from typing import Any
|
|
10
|
-
|
|
11
|
-
from team_agent.profiles.constants import COMPATIBLE_API_NETWORK_ENV_KEYS
|
|
12
|
-
from team_agent.profiles.provider_env import _compatible_api_network_exports, _profile_proxy_mode
|
|
13
|
-
from team_agent.rust_core import redact_text
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def _anthropic_compatible_smoke(
|
|
17
|
-
values: dict[str, str],
|
|
18
|
-
model: str | None,
|
|
19
|
-
base_result: dict[str, Any],
|
|
20
|
-
timeout: float,
|
|
21
|
-
) -> dict[str, Any]:
|
|
22
|
-
base_url = values.get("ANTHROPIC_BASE_URL") or values.get("BASE_URL")
|
|
23
|
-
api_key = values.get("ANTHROPIC_API_KEY") or values.get("API_KEY")
|
|
24
|
-
auth_token = values.get("ANTHROPIC_AUTH_TOKEN") or values.get("AUTH_TOKEN")
|
|
25
|
-
if not base_url or not (api_key or auth_token) or not model:
|
|
26
|
-
return {**base_result, "ok": False, "status": "smoke_failed", "reason": "missing_base_url_api_key_or_model"}
|
|
27
|
-
endpoint = _anthropic_messages_url(base_url)
|
|
28
|
-
payload = {
|
|
29
|
-
"model": model,
|
|
30
|
-
"max_tokens": 1,
|
|
31
|
-
"messages": [{"role": "user", "content": "ping"}],
|
|
32
|
-
}
|
|
33
|
-
headers = {
|
|
34
|
-
"content-type": "application/json",
|
|
35
|
-
"anthropic-version": values.get("ANTHROPIC_VERSION") or "2023-06-01",
|
|
36
|
-
}
|
|
37
|
-
headers["authorization"] = f"Bearer {auth_token or api_key}"
|
|
38
|
-
return _http_json_smoke(endpoint, payload, headers, values, base_result, timeout)
|
|
39
|
-
|
|
40
|
-
def _openai_compatible_smoke(
|
|
41
|
-
values: dict[str, str],
|
|
42
|
-
model: str | None,
|
|
43
|
-
base_result: dict[str, Any],
|
|
44
|
-
timeout: float,
|
|
45
|
-
) -> dict[str, Any]:
|
|
46
|
-
base_url = values.get("OPENAI_BASE_URL") or values.get("BASE_URL")
|
|
47
|
-
api_key = values.get("OPENAI_API_KEY") or values.get("API_KEY")
|
|
48
|
-
if not base_url or not api_key or not model:
|
|
49
|
-
return {**base_result, "ok": False, "status": "smoke_failed", "reason": "missing_base_url_api_key_or_model"}
|
|
50
|
-
endpoint = _openai_chat_url(base_url)
|
|
51
|
-
payload = {
|
|
52
|
-
"model": model,
|
|
53
|
-
"max_tokens": 1,
|
|
54
|
-
"messages": [{"role": "user", "content": "ping"}],
|
|
55
|
-
}
|
|
56
|
-
headers = {"content-type": "application/json", "authorization": f"Bearer {api_key}"}
|
|
57
|
-
return _http_json_smoke(endpoint, payload, headers, values, base_result, timeout)
|
|
58
|
-
|
|
59
|
-
def _http_json_smoke(
|
|
60
|
-
endpoint: str,
|
|
61
|
-
payload: dict[str, Any],
|
|
62
|
-
headers: dict[str, str],
|
|
63
|
-
values: dict[str, str],
|
|
64
|
-
base_result: dict[str, Any],
|
|
65
|
-
timeout: float,
|
|
66
|
-
) -> dict[str, Any]:
|
|
67
|
-
proxy_info = _proxy_info_for_endpoint(endpoint, values)
|
|
68
|
-
request = urllib.request.Request(
|
|
69
|
-
endpoint,
|
|
70
|
-
data=json.dumps(payload).encode("utf-8"),
|
|
71
|
-
headers=headers,
|
|
72
|
-
method="POST",
|
|
73
|
-
)
|
|
74
|
-
try:
|
|
75
|
-
with _temporary_profile_network_env(values):
|
|
76
|
-
response_ctx = urllib.request.urlopen(request, timeout=timeout)
|
|
77
|
-
with response_ctx as response:
|
|
78
|
-
status = int(getattr(response, "status", 200))
|
|
79
|
-
body = response.read(1024).decode("utf-8", errors="replace")
|
|
80
|
-
except urllib.error.HTTPError as exc:
|
|
81
|
-
body = exc.read(4096).decode("utf-8", errors="replace")
|
|
82
|
-
return {
|
|
83
|
-
**base_result,
|
|
84
|
-
**proxy_info,
|
|
85
|
-
"ok": False,
|
|
86
|
-
"status": "smoke_failed",
|
|
87
|
-
"reason": "http_error",
|
|
88
|
-
"http_status": exc.code,
|
|
89
|
-
"endpoint": _redacted_endpoint(endpoint),
|
|
90
|
-
"error": redact_text(body or str(exc)).get("text"),
|
|
91
|
-
}
|
|
92
|
-
except Exception as exc:
|
|
93
|
-
reason = "proxy_connectivity_failed" if proxy_info.get("proxy_configured") else "request_failed"
|
|
94
|
-
return {
|
|
95
|
-
**base_result,
|
|
96
|
-
**proxy_info,
|
|
97
|
-
"ok": False,
|
|
98
|
-
"status": "smoke_failed",
|
|
99
|
-
"reason": reason,
|
|
100
|
-
"endpoint": _redacted_endpoint(endpoint),
|
|
101
|
-
"error": redact_text(str(exc)).get("text"),
|
|
102
|
-
"suggestion": (
|
|
103
|
-
"Proxy is configured for this request; allow the profile BASE_URL through the proxy or disable the proxy for Team Agent startup."
|
|
104
|
-
if proxy_info.get("proxy_configured")
|
|
105
|
-
else "Check BASE_URL network connectivity from this machine."
|
|
106
|
-
),
|
|
107
|
-
}
|
|
108
|
-
if 200 <= status < 300:
|
|
109
|
-
return {
|
|
110
|
-
**base_result,
|
|
111
|
-
**proxy_info,
|
|
112
|
-
"ok": True,
|
|
113
|
-
"status": "smoke_passed",
|
|
114
|
-
"http_status": status,
|
|
115
|
-
"endpoint": _redacted_endpoint(endpoint),
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
**base_result,
|
|
119
|
-
**proxy_info,
|
|
120
|
-
"ok": False,
|
|
121
|
-
"status": "smoke_failed",
|
|
122
|
-
"reason": "unexpected_status",
|
|
123
|
-
"http_status": status,
|
|
124
|
-
"endpoint": _redacted_endpoint(endpoint),
|
|
125
|
-
"error": redact_text(body).get("text"),
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
def _anthropic_messages_url(base_url: str) -> str:
|
|
129
|
-
base = base_url.rstrip("/")
|
|
130
|
-
if base.endswith("/messages"):
|
|
131
|
-
return base
|
|
132
|
-
if base.endswith("/v1"):
|
|
133
|
-
return f"{base}/messages"
|
|
134
|
-
return f"{base}/v1/messages"
|
|
135
|
-
|
|
136
|
-
def _openai_chat_url(base_url: str) -> str:
|
|
137
|
-
base = base_url.rstrip("/")
|
|
138
|
-
if base.endswith("/chat/completions"):
|
|
139
|
-
return base
|
|
140
|
-
if base.endswith("/v1"):
|
|
141
|
-
return f"{base}/chat/completions"
|
|
142
|
-
return f"{base}/v1/chat/completions"
|
|
143
|
-
|
|
144
|
-
def _redacted_endpoint(endpoint: str) -> str:
|
|
145
|
-
return endpoint.split("?", 1)[0]
|
|
146
|
-
|
|
147
|
-
def _proxy_info_for_endpoint(endpoint: str, values: dict[str, str]) -> dict[str, Any]:
|
|
148
|
-
parsed = urllib.parse.urlparse(endpoint)
|
|
149
|
-
if _profile_proxy_mode(values) == "direct":
|
|
150
|
-
return {"proxy_configured": False, "proxy_mode": "direct"}
|
|
151
|
-
profile_env = _compatible_api_network_exports("compatible_api", values)
|
|
152
|
-
proxy_url = _proxy_url_from_env(parsed.scheme, profile_env)
|
|
153
|
-
if proxy_url:
|
|
154
|
-
return {
|
|
155
|
-
"proxy_configured": True,
|
|
156
|
-
"proxy_scheme": parsed.scheme,
|
|
157
|
-
"proxy_url": _redact_proxy_url(proxy_url),
|
|
158
|
-
"proxy_source": "profile",
|
|
159
|
-
}
|
|
160
|
-
ambient_proxy_url = _proxy_url_from_env(parsed.scheme, os.environ)
|
|
161
|
-
if ambient_proxy_url:
|
|
162
|
-
return {
|
|
163
|
-
"proxy_configured": True,
|
|
164
|
-
"proxy_scheme": parsed.scheme,
|
|
165
|
-
"proxy_url": _redact_proxy_url(ambient_proxy_url),
|
|
166
|
-
"proxy_source": "ambient",
|
|
167
|
-
}
|
|
168
|
-
return {"proxy_configured": False}
|
|
169
|
-
|
|
170
|
-
def _proxy_url_from_env(scheme: str, env: Any) -> str | None:
|
|
171
|
-
upper = f"{scheme.upper()}_PROXY"
|
|
172
|
-
lower = f"{scheme.lower()}_proxy"
|
|
173
|
-
return env.get(upper) or env.get(lower) or env.get("ALL_PROXY") or env.get("all_proxy")
|
|
174
|
-
|
|
175
|
-
@contextmanager
|
|
176
|
-
def _temporary_profile_network_env(values: dict[str, str]) -> Any:
|
|
177
|
-
profile_env = _compatible_api_network_exports("compatible_api", values)
|
|
178
|
-
direct = _profile_proxy_mode(values) == "direct"
|
|
179
|
-
touched_keys = COMPATIBLE_API_NETWORK_ENV_KEYS if direct else tuple(profile_env)
|
|
180
|
-
saved = {key: os.environ.get(key) for key in touched_keys}
|
|
181
|
-
try:
|
|
182
|
-
if direct:
|
|
183
|
-
for key in COMPATIBLE_API_NETWORK_ENV_KEYS:
|
|
184
|
-
os.environ.pop(key, None)
|
|
185
|
-
os.environ.update(profile_env)
|
|
186
|
-
yield
|
|
187
|
-
finally:
|
|
188
|
-
for key, value in saved.items():
|
|
189
|
-
if value is None:
|
|
190
|
-
os.environ.pop(key, None)
|
|
191
|
-
else:
|
|
192
|
-
os.environ[key] = value
|
|
193
|
-
|
|
194
|
-
def _redact_proxy_url(proxy_url: str) -> str:
|
|
195
|
-
parsed = urllib.parse.urlparse(proxy_url)
|
|
196
|
-
if not parsed.netloc:
|
|
197
|
-
return proxy_url
|
|
198
|
-
host = parsed.hostname or ""
|
|
199
|
-
port = f":{parsed.port}" if parsed.port else ""
|
|
200
|
-
auth = "[redacted]@" if parsed.username or parsed.password else ""
|
|
201
|
-
return urllib.parse.urlunparse((parsed.scheme, f"{auth}{host}{port}", parsed.path, "", "", ""))
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from team_agent.provider_cli.adapter import (
|
|
4
|
-
ProviderAdapter,
|
|
5
|
-
ResumeUnavailable,
|
|
6
|
-
agent_model,
|
|
7
|
-
parse_time,
|
|
8
|
-
read_json_object,
|
|
9
|
-
)
|
|
10
|
-
from team_agent.provider_cli.base import (
|
|
11
|
-
ProviderCapabilityError,
|
|
12
|
-
ProviderCliSocket,
|
|
13
|
-
ProviderStartupInput,
|
|
14
|
-
)
|
|
15
|
-
from team_agent.provider_cli.claude import ClaudeCodeAdapter
|
|
16
|
-
from team_agent.provider_cli.codex import CodexAdapter
|
|
17
|
-
from team_agent.provider_cli.copilot import CopilotCliPlug
|
|
18
|
-
from team_agent.provider_cli.fake import FakeAdapter
|
|
19
|
-
from team_agent.provider_cli.gemini import GeminiCliAdapter
|
|
20
|
-
from team_agent.provider_cli.opencode import OpenCodeCliPlug
|
|
21
|
-
from team_agent.provider_cli.prompt import TEAMMATE_SYSTEM_PROMPT, compile_system_prompt
|
|
22
|
-
from team_agent.provider_cli.registry import PLUG_TYPES, build_plug
|
|
23
|
-
|
|
24
|
-
__all__ = [
|
|
25
|
-
"ClaudeCodeAdapter",
|
|
26
|
-
"CodexAdapter",
|
|
27
|
-
"CopilotCliPlug",
|
|
28
|
-
"FakeAdapter",
|
|
29
|
-
"GeminiCliAdapter",
|
|
30
|
-
"OpenCodeCliPlug",
|
|
31
|
-
"PLUG_TYPES",
|
|
32
|
-
"ProviderAdapter",
|
|
33
|
-
"ProviderCapabilityError",
|
|
34
|
-
"ProviderCliSocket",
|
|
35
|
-
"ProviderStartupInput",
|
|
36
|
-
"ResumeUnavailable",
|
|
37
|
-
"TEAMMATE_SYSTEM_PROMPT",
|
|
38
|
-
"agent_model",
|
|
39
|
-
"build_plug",
|
|
40
|
-
"compile_system_prompt",
|
|
41
|
-
"parse_time",
|
|
42
|
-
"read_json_object",
|
|
43
|
-
]
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import shutil
|
|
5
|
-
import subprocess
|
|
6
|
-
import sys
|
|
7
|
-
from datetime import datetime, timezone
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import Any
|
|
10
|
-
|
|
11
|
-
from team_agent.paths import repo_root
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class ResumeUnavailable(RuntimeError):
|
|
15
|
-
pass
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class ProviderAdapter:
|
|
19
|
-
provider = ""
|
|
20
|
-
command_name = ""
|
|
21
|
-
|
|
22
|
-
def is_installed(self) -> bool:
|
|
23
|
-
return shutil.which(self.command_name) is not None
|
|
24
|
-
|
|
25
|
-
def version(self) -> str | None:
|
|
26
|
-
if not self.is_installed():
|
|
27
|
-
return None
|
|
28
|
-
for args in ([self.command_name, "--version"], [self.command_name, "version"]):
|
|
29
|
-
try:
|
|
30
|
-
proc = subprocess.run(args, text=True, capture_output=True, timeout=8, check=False)
|
|
31
|
-
except (OSError, subprocess.TimeoutExpired):
|
|
32
|
-
continue
|
|
33
|
-
text = (proc.stdout or proc.stderr).strip()
|
|
34
|
-
if text:
|
|
35
|
-
return text.splitlines()[0]
|
|
36
|
-
return "installed"
|
|
37
|
-
|
|
38
|
-
def auth_hint(self) -> dict[str, Any]:
|
|
39
|
-
return {"status": "unknown", "detail": "adapter cannot verify auth without starting CLI"}
|
|
40
|
-
|
|
41
|
-
def build_command(self, agent: dict[str, Any], workspace: Path, mcp_config: dict[str, Any]) -> list[str]:
|
|
42
|
-
raise NotImplementedError
|
|
43
|
-
|
|
44
|
-
def capture_session_id(
|
|
45
|
-
self,
|
|
46
|
-
agent_id: str,
|
|
47
|
-
spawn_context: dict[str, Any],
|
|
48
|
-
timeout_s: float = 3.0,
|
|
49
|
-
) -> dict[str, Any] | None:
|
|
50
|
-
_ = agent_id, spawn_context, timeout_s
|
|
51
|
-
return None
|
|
52
|
-
|
|
53
|
-
def build_resume_command(
|
|
54
|
-
self,
|
|
55
|
-
agent_state: dict[str, Any],
|
|
56
|
-
workspace: Path,
|
|
57
|
-
mcp_config: dict[str, Any] | None = None,
|
|
58
|
-
) -> list[str]:
|
|
59
|
-
_ = workspace, mcp_config
|
|
60
|
-
session_id = agent_state.get("session_id")
|
|
61
|
-
if not session_id:
|
|
62
|
-
raise ResumeUnavailable("session_id is required to resume")
|
|
63
|
-
raise ResumeUnavailable(f"{self.provider} does not support resume")
|
|
64
|
-
|
|
65
|
-
def supports_session_fork(self, agent: dict[str, Any] | None = None) -> bool:
|
|
66
|
-
_ = agent
|
|
67
|
-
return False
|
|
68
|
-
|
|
69
|
-
def build_fork_command(
|
|
70
|
-
self,
|
|
71
|
-
agent: dict[str, Any],
|
|
72
|
-
source_session_id: str,
|
|
73
|
-
workspace: Path,
|
|
74
|
-
mcp_config: dict[str, Any],
|
|
75
|
-
) -> list[str]:
|
|
76
|
-
_ = agent, source_session_id, workspace, mcp_config
|
|
77
|
-
raise ResumeUnavailable(f"{self.provider} does not support native session fork")
|
|
78
|
-
|
|
79
|
-
def session_is_resumable(self, agent_state: dict[str, Any], workspace: Path) -> bool:
|
|
80
|
-
_ = workspace
|
|
81
|
-
return bool(agent_state.get("session_id"))
|
|
82
|
-
|
|
83
|
-
def recover_session_id(
|
|
84
|
-
self,
|
|
85
|
-
agent_id: str,
|
|
86
|
-
agent_state: dict[str, Any],
|
|
87
|
-
workspace: Path,
|
|
88
|
-
exclude_session_ids: set[str] | None = None,
|
|
89
|
-
) -> dict[str, Any] | None:
|
|
90
|
-
_ = agent_id, agent_state, workspace, exclude_session_ids
|
|
91
|
-
return None
|
|
92
|
-
|
|
93
|
-
def mcp_config(self, workspace: Path, agent_id: str, team_id: str | None = None) -> dict[str, Any]:
|
|
94
|
-
# 0.2.6 Family C (C13): worker spawn env always carries the owning
|
|
95
|
-
# team id so the MCP server can scope sender requests without
|
|
96
|
-
# asking the worker which team it belongs to.
|
|
97
|
-
env = {
|
|
98
|
-
"TEAM_AGENT_ID": agent_id,
|
|
99
|
-
"TEAM_AGENT_OWNER_TEAM_ID": str(team_id or ""),
|
|
100
|
-
"PYTHONPATH": str(repo_root() / "src"),
|
|
101
|
-
}
|
|
102
|
-
return {
|
|
103
|
-
"team_orchestrator": {
|
|
104
|
-
"type": "stdio",
|
|
105
|
-
"command": sys.executable,
|
|
106
|
-
"args": ["-m", "team_agent.mcp_server", "--workspace", str(workspace)],
|
|
107
|
-
"env": env,
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
def install_mcp(self, workspace: Path, agent_id: str, config: dict[str, Any]) -> Path:
|
|
112
|
-
path = workspace / ".team" / "runtime" / "mcp" / f"{agent_id}.json"
|
|
113
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
114
|
-
path.write_text(json.dumps({"mcpServers": config}, indent=2), encoding="utf-8")
|
|
115
|
-
return path
|
|
116
|
-
|
|
117
|
-
def cleanup_mcp(self, workspace: Path, agent_id: str, mcp_path: Path | None = None) -> None:
|
|
118
|
-
return None
|
|
119
|
-
|
|
120
|
-
def status_patterns(self) -> dict[str, str]:
|
|
121
|
-
return {"idle": "", "processing": "", "error": "Error|Traceback|panic"}
|
|
122
|
-
|
|
123
|
-
def exit_text(self) -> str:
|
|
124
|
-
return "/exit"
|
|
125
|
-
|
|
126
|
-
def handle_startup_prompts(
|
|
127
|
-
self,
|
|
128
|
-
session_name: str,
|
|
129
|
-
window_name: str,
|
|
130
|
-
checks: int = 30,
|
|
131
|
-
sleep_s: float = 0.5,
|
|
132
|
-
) -> list[dict[str, Any]]:
|
|
133
|
-
return []
|
|
134
|
-
|
|
135
|
-
def handle_runtime_prompts(self, session_name: str, window_name: str) -> list[dict[str, Any]]:
|
|
136
|
-
return []
|
|
137
|
-
|
|
138
|
-
def validate_model(self, model: str | None) -> dict[str, Any]:
|
|
139
|
-
return {"ok": True, "status": "not_checked", "provider": self.provider, "model": model}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def agent_model(agent: dict[str, Any]) -> str | None:
|
|
143
|
-
if agent.get("model"):
|
|
144
|
-
return str(agent["model"])
|
|
145
|
-
profile_overrides = agent.get("_provider_profile", {}).get("command_overrides", {})
|
|
146
|
-
if profile_overrides.get("model"):
|
|
147
|
-
return str(profile_overrides["model"])
|
|
148
|
-
return None
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def read_json_object(path: Path) -> dict[str, Any]:
|
|
152
|
-
if not path.exists():
|
|
153
|
-
return {}
|
|
154
|
-
data = json.loads(path.read_text(encoding="utf-8"))
|
|
155
|
-
if not isinstance(data, dict):
|
|
156
|
-
raise ValueError(f"{path}: expected a JSON object")
|
|
157
|
-
return data
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def parse_time(value: Any) -> datetime | None:
|
|
161
|
-
if isinstance(value, datetime):
|
|
162
|
-
return value if value.tzinfo else value.replace(tzinfo=timezone.utc)
|
|
163
|
-
if not value:
|
|
164
|
-
return None
|
|
165
|
-
text = str(value)
|
|
166
|
-
if text.endswith("Z"):
|
|
167
|
-
text = text[:-1] + "+00:00"
|
|
168
|
-
try:
|
|
169
|
-
dt = datetime.fromisoformat(text)
|
|
170
|
-
except ValueError:
|
|
171
|
-
return None
|
|
172
|
-
return dt if dt.tzinfo else dt.replace(tzinfo=timezone.utc)
|