@team-agent/installer 0.5.113 → 0.5.114
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 +252 -84
- package/Cargo.toml +3 -1
- package/crates/team-agent/Cargo.toml +3 -0
- package/crates/team-agent/src/cli/adapters.rs +28 -15
- package/crates/team-agent/src/cli/diagnose.rs +36 -13
- package/crates/team-agent/src/cli/emit.rs +55 -20
- package/crates/team-agent/src/cli/leader.rs +16 -0
- package/crates/team-agent/src/cli/leader_prompt.rs +261 -0
- package/crates/team-agent/src/cli/mod.rs +152 -8
- package/crates/team-agent/src/cli/models.rs +68 -18
- package/crates/team-agent/src/cli/spec.rs +7 -1
- package/crates/team-agent/src/cli/status_port/brief.rs +268 -101
- package/crates/team-agent/src/cli/tests/base.rs +88 -0
- package/crates/team-agent/src/cli/tests/status_send.rs +2 -0
- package/crates/team-agent/src/compiler.rs +66 -18
- package/crates/team-agent/src/contract_runtime/backend.rs +646 -0
- package/crates/team-agent/src/contract_runtime/config.rs +309 -0
- package/crates/team-agent/src/contract_runtime/discovery.rs +237 -0
- package/crates/team-agent/src/contract_runtime/forward.rs +229 -0
- package/crates/team-agent/src/contract_runtime/framework.rs +261 -0
- package/crates/team-agent/src/contract_runtime/mcp.rs +396 -0
- package/crates/team-agent/src/contract_runtime/mod.rs +14 -0
- package/crates/team-agent/src/contract_runtime/prompt.rs +216 -0
- package/crates/team-agent/src/contract_runtime/pump.rs +207 -0
- package/crates/team-agent/src/contract_runtime/registry.rs +30 -0
- package/crates/team-agent/src/coordinator/tick.rs +26 -8
- package/crates/team-agent/src/leader/mod.rs +1 -0
- package/crates/team-agent/src/leader/prompt.rs +906 -0
- package/crates/team-agent/src/leader/start.rs +260 -16
- package/crates/team-agent/src/leader/tests/mod.rs +2 -0
- package/crates/team-agent/src/leader/tests/prompt_contract.rs +268 -0
- package/crates/team-agent/src/leader/types.rs +6 -0
- package/crates/team-agent/src/lib.rs +4 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +472 -388
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +38 -9
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +17 -0
- package/crates/team-agent/src/lifecycle/launch.rs +39 -28
- package/crates/team-agent/src/lifecycle/types.rs +4 -1
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +37 -27
- package/crates/team-agent/src/main.rs +6 -0
- package/crates/team-agent/src/messaging/delivery.rs +50 -1
- package/crates/team-agent/src/messaging/send.rs +10 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +131 -0
- package/crates/team-agent/src/model/spec.rs +24 -12
- package/crates/team-agent/src/state/persist.rs +73 -0
- package/package.json +4 -4
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
//! One coordinator-owned consumer of the K3 outbox. No CLI send side channel,
|
|
2
|
+
//! implicit business retry, queue flush, or replay of uncertain messages.
|
|
3
|
+
use super::{
|
|
4
|
+
backend::{self, Backend, BackendError},
|
|
5
|
+
forward, framework,
|
|
6
|
+
};
|
|
7
|
+
use serde_json::Value;
|
|
8
|
+
use std::path::Path;
|
|
9
|
+
use std::time::Duration;
|
|
10
|
+
use team_agent_contract::contract::{delivery::InputSurface, probe::*, types::*};
|
|
11
|
+
use team_agent_contract::host::{
|
|
12
|
+
clock::{Clock, RealClock},
|
|
13
|
+
process::verify_native_ancestry,
|
|
14
|
+
};
|
|
15
|
+
use team_agent_contract::kiro::{interaction, KIRO_DESCRIPTOR};
|
|
16
|
+
use team_agent_contract::orchestration::{physical::*, store::*, supervisor::*};
|
|
17
|
+
use team_agent_contract::runtime::probes::*;
|
|
18
|
+
|
|
19
|
+
fn protocol(
|
|
20
|
+
backend: &mut Backend,
|
|
21
|
+
seat: &SeatRecord,
|
|
22
|
+
clock: &RealClock,
|
|
23
|
+
) -> Result<ProtocolSnapshot, BackendError> {
|
|
24
|
+
let target = seat.physical.as_ref().ok_or(BackendError::Metadata)?;
|
|
25
|
+
let active: Vec<_> = backend
|
|
26
|
+
.store
|
|
27
|
+
.connections(&seat.identity)?
|
|
28
|
+
.into_iter()
|
|
29
|
+
.filter(|record| {
|
|
30
|
+
!record.closed
|
|
31
|
+
&& record.native_process == target.process
|
|
32
|
+
&& verify_native_ancestry(&record.process, &record.native_process).is_ok()
|
|
33
|
+
})
|
|
34
|
+
.collect();
|
|
35
|
+
if active.len() != 1 {
|
|
36
|
+
return Ok(collect_protocol(
|
|
37
|
+
target,
|
|
38
|
+
&seat.identity.instance,
|
|
39
|
+
&seat.server_key,
|
|
40
|
+
&[],
|
|
41
|
+
clock.now(),
|
|
42
|
+
Duration::from_secs(2),
|
|
43
|
+
));
|
|
44
|
+
}
|
|
45
|
+
let connection = &active[0];
|
|
46
|
+
let facts = backend
|
|
47
|
+
.store
|
|
48
|
+
.connection_facts(&seat.identity, &connection.connection)?;
|
|
49
|
+
let mut events = Vec::new();
|
|
50
|
+
// T3a is connection state, sampled against BOTH captured process births.
|
|
51
|
+
// Completed server writes are never promoted to client discovery.
|
|
52
|
+
for (fact, protocol) in [
|
|
53
|
+
(
|
|
54
|
+
"initialize_response_written",
|
|
55
|
+
ProtocolFact::InitializeResponseWritten {
|
|
56
|
+
server_instance: connection.connection.clone(),
|
|
57
|
+
},
|
|
58
|
+
),
|
|
59
|
+
(
|
|
60
|
+
"tools_list_response_written",
|
|
61
|
+
ProtocolFact::ToolsListResponseWritten {
|
|
62
|
+
server_instance: connection.connection.clone(),
|
|
63
|
+
},
|
|
64
|
+
),
|
|
65
|
+
] {
|
|
66
|
+
if facts.iter().any(|value| value == fact) {
|
|
67
|
+
events.push(ProtocolEvent {
|
|
68
|
+
scope: target.scope(
|
|
69
|
+
events.len() as u64,
|
|
70
|
+
clock.now(),
|
|
71
|
+
Duration::from_secs(2),
|
|
72
|
+
"live-owned-stdio-state",
|
|
73
|
+
),
|
|
74
|
+
fact: protocol,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
Ok(collect_protocol(
|
|
79
|
+
target,
|
|
80
|
+
&connection.connection,
|
|
81
|
+
&seat.server_key,
|
|
82
|
+
&events,
|
|
83
|
+
clock.now(),
|
|
84
|
+
Duration::from_secs(2),
|
|
85
|
+
))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn deliver_one(backend: &mut Backend, seat: &SeatRecord) -> Result<(), BackendError> {
|
|
89
|
+
if matches!(seat.status, SeatStatus::Stopped | SeatStatus::Unknown)
|
|
90
|
+
|| !backend.store.has_queued_for(&seat.identity)?
|
|
91
|
+
{
|
|
92
|
+
return Ok(());
|
|
93
|
+
}
|
|
94
|
+
let clock = RealClock::new();
|
|
95
|
+
let adapter = backend::adapter(&backend.binding, &seat.identity)?;
|
|
96
|
+
let hooks = adapter.hooks();
|
|
97
|
+
let operation = OperationId::new(format!(
|
|
98
|
+
"deliver-{}",
|
|
99
|
+
backend
|
|
100
|
+
.store
|
|
101
|
+
.propose_identity(&seat.identity.seat)?
|
|
102
|
+
.instance
|
|
103
|
+
.as_str()
|
|
104
|
+
))?;
|
|
105
|
+
let mut physical = PhysicalRuntime::new(
|
|
106
|
+
Registration {
|
|
107
|
+
descriptor: &KIRO_DESCRIPTOR,
|
|
108
|
+
hooks: &hooks,
|
|
109
|
+
controls: interaction::CONTROLS,
|
|
110
|
+
},
|
|
111
|
+
backend::settings(&backend.binding),
|
|
112
|
+
operation.clone(),
|
|
113
|
+
&clock,
|
|
114
|
+
)?;
|
|
115
|
+
physical.evidence = backend::policy_evidence(&seat.native, backend.binding.candidate_sha256);
|
|
116
|
+
physical.protocol = Some(protocol(backend, seat, &clock)?);
|
|
117
|
+
let sample = physical.readiness(seat)?;
|
|
118
|
+
if !matches!(sample.pane.outcome, ProbeOutcome::Observed(ref pane) if pane.surface == InputSurface::ComposerReady)
|
|
119
|
+
|| !matches!(sample.server.outcome, ProbeOutcome::Observed(ref server) if server.initialize_response_written && server.tools_list_response_written)
|
|
120
|
+
{
|
|
121
|
+
return Ok(());
|
|
122
|
+
}
|
|
123
|
+
backend
|
|
124
|
+
.store
|
|
125
|
+
.begin_observation(&seat.identity, &operation)?;
|
|
126
|
+
// A completed-but-uncertain inspector quarantines the seat, without an
|
|
127
|
+
// orphan lease that would prevent explicit scoped shutdown. No replay or
|
|
128
|
+
// blind Escape to clear an unrelated approval/composer is permitted.
|
|
129
|
+
let binding = match physical.client_binding(seat, &interaction::TOOL_PANEL) {
|
|
130
|
+
Ok(binding) => binding,
|
|
131
|
+
Err(error) => {
|
|
132
|
+
backend.store.fail_observation(&seat.identity, &operation)?;
|
|
133
|
+
return Err(error.into());
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
backend
|
|
137
|
+
.store
|
|
138
|
+
.finish_observation(&seat.identity, &operation)?;
|
|
139
|
+
if !matches!(binding.outcome, ProbeOutcome::Observed(_)) {
|
|
140
|
+
return Ok(());
|
|
141
|
+
}
|
|
142
|
+
let mut current_protocol = protocol(backend, seat, &clock)?;
|
|
143
|
+
current_protocol.binding = binding;
|
|
144
|
+
physical.protocol = Some(current_protocol);
|
|
145
|
+
let mut bootstrap = OutboxBootstrap::new(ContractStore::open(
|
|
146
|
+
&backend.binding.transport_root,
|
|
147
|
+
backend.binding.scope.clone(),
|
|
148
|
+
&backend.binding.endpoint,
|
|
149
|
+
)?);
|
|
150
|
+
physical.bootstrap = Some(&mut bootstrap);
|
|
151
|
+
let sample = physical.readiness(seat)?;
|
|
152
|
+
let authorization = DeliveryAuthorization {
|
|
153
|
+
allow_first_business_bootstrap: false,
|
|
154
|
+
require_server_handshake: true,
|
|
155
|
+
channel: Channel::Tmux,
|
|
156
|
+
policy_sha256: interaction::POLICY,
|
|
157
|
+
};
|
|
158
|
+
let _ = team_agent_contract::orchestration::supervisor::tick(
|
|
159
|
+
&mut backend.store,
|
|
160
|
+
&seat.identity,
|
|
161
|
+
&sample,
|
|
162
|
+
&authorization,
|
|
163
|
+
&mut physical,
|
|
164
|
+
)?;
|
|
165
|
+
Ok(())
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
pub fn tick(workspace: &Path, state: &mut Value) -> Result<(), BackendError> {
|
|
169
|
+
if !framework::has_contract(state) {
|
|
170
|
+
return Ok(());
|
|
171
|
+
}
|
|
172
|
+
let team = crate::state::projection::team_state_key(state);
|
|
173
|
+
let mut backend = Backend::open(workspace, &team)?;
|
|
174
|
+
let ids: Vec<_> = state
|
|
175
|
+
.get("agents")
|
|
176
|
+
.and_then(Value::as_object)
|
|
177
|
+
.into_iter()
|
|
178
|
+
.flat_map(|agents| agents.iter())
|
|
179
|
+
.filter(|(_, agent)| framework::is_contract(agent))
|
|
180
|
+
.map(|(id, _)| id.clone())
|
|
181
|
+
.collect();
|
|
182
|
+
let mut failure = None;
|
|
183
|
+
for id in ids {
|
|
184
|
+
let Some(seat) = backend.store.seat(&SeatId::new(&id)?)? else {
|
|
185
|
+
continue;
|
|
186
|
+
};
|
|
187
|
+
if let Err(error) = deliver_one(&mut backend, &seat) {
|
|
188
|
+
failure = Some(error);
|
|
189
|
+
}
|
|
190
|
+
if let Some(agent) = state
|
|
191
|
+
.get_mut("agents")
|
|
192
|
+
.and_then(|agents| agents.get_mut(&id))
|
|
193
|
+
{
|
|
194
|
+
match framework::project_seat(workspace, &team, &id) {
|
|
195
|
+
Ok(projected) => *agent = projected,
|
|
196
|
+
Err(error) => failure = Some(error),
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// One blocked seat must not prevent already committed results from reaching
|
|
201
|
+
// the shared framework task-finalization/leader-presentation path.
|
|
202
|
+
forward::drain(&backend.binding.framework(), &mut backend.store, 32)?;
|
|
203
|
+
match failure {
|
|
204
|
+
Some(error) => Err(error),
|
|
205
|
+
None => Ok(()),
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//! The sole new-runtime provider registration. This is not an admission claim:
|
|
2
|
+
//! the descriptor's Unverified gates still apply to each requested operation.
|
|
3
|
+
use team_agent_contract::contract::descriptor::ProviderDescriptor;
|
|
4
|
+
use team_agent_contract::kiro::KIRO_DESCRIPTOR;
|
|
5
|
+
|
|
6
|
+
const DESCRIPTORS: &[&ProviderDescriptor] = &[&KIRO_DESCRIPTOR];
|
|
7
|
+
|
|
8
|
+
pub(crate) fn descriptors() -> &'static [&'static ProviderDescriptor] {
|
|
9
|
+
DESCRIPTORS
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
pub fn descriptor(key: &str) -> Option<&'static ProviderDescriptor> {
|
|
13
|
+
DESCRIPTORS.iter().copied().find(|descriptor| {
|
|
14
|
+
descriptor.identity.id == key || descriptor.identity.aliases.contains(&key)
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Recognize a misspelled/case-modified contract declaration so it can fail
|
|
19
|
+
/// explicitly instead of accidentally selecting a legacy default provider.
|
|
20
|
+
pub fn recognizes(key: &str) -> bool {
|
|
21
|
+
let key = key.trim();
|
|
22
|
+
DESCRIPTORS.iter().any(|descriptor| {
|
|
23
|
+
descriptor.identity.id.eq_ignore_ascii_case(key)
|
|
24
|
+
|| descriptor
|
|
25
|
+
.identity
|
|
26
|
+
.aliases
|
|
27
|
+
.iter()
|
|
28
|
+
.any(|alias| alias.eq_ignore_ascii_case(key))
|
|
29
|
+
})
|
|
30
|
+
}
|
|
@@ -359,6 +359,19 @@ impl Coordinator {
|
|
|
359
359
|
let event_log = EventLog::new(self.workspace.as_path());
|
|
360
360
|
increment_coordinator_tick_iteration_count(&self.workspace);
|
|
361
361
|
|
|
362
|
+
#[cfg(unix)]
|
|
363
|
+
if let Err(error) =
|
|
364
|
+
crate::contract_runtime::pump::tick(self.workspace.as_path(), &mut state)
|
|
365
|
+
{
|
|
366
|
+
let _ = event_log.write(
|
|
367
|
+
"contract.tick.failed",
|
|
368
|
+
serde_json::json!({"error":error.to_string()}),
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
#[cfg(unix)]
|
|
372
|
+
let contract_only = crate::contract_runtime::framework::only_contract(&state);
|
|
373
|
+
#[cfg(not(unix))]
|
|
374
|
+
let contract_only = false;
|
|
362
375
|
self.record_step(TickStepGroup::SessionGate, "tmux_session_gate");
|
|
363
376
|
if let Some(session_name) = state
|
|
364
377
|
.get("session_name")
|
|
@@ -367,7 +380,7 @@ impl Coordinator {
|
|
|
367
380
|
.map(str::to_owned)
|
|
368
381
|
{
|
|
369
382
|
let session = crate::transport::SessionName::new(&session_name);
|
|
370
|
-
if !self.transport.has_session(&session)? {
|
|
383
|
+
if !contract_only && !self.transport.has_session(&session)? {
|
|
371
384
|
event_log.write(
|
|
372
385
|
"coordinator.session_missing",
|
|
373
386
|
serde_json::json!({"session": session_name}),
|
|
@@ -564,13 +577,14 @@ impl Coordinator {
|
|
|
564
577
|
// Keep the sampled topology guard, but commit only this tick's
|
|
565
578
|
// observations onto latest so task/note writers cannot be lost.
|
|
566
579
|
let team_key = crate::state::projection::team_state_key(&state);
|
|
567
|
-
crate::state::repository::StateRepository::new(self.workspace.as_path())
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
580
|
+
crate::state::repository::StateRepository::new(self.workspace.as_path())
|
|
581
|
+
.commit_observations(
|
|
582
|
+
crate::state::repository::StateWriteIntent::CoordinatorTick {
|
|
583
|
+
team_key: team_key.as_str(),
|
|
584
|
+
},
|
|
585
|
+
&before_observation,
|
|
586
|
+
&state,
|
|
587
|
+
)
|
|
574
588
|
}
|
|
575
589
|
};
|
|
576
590
|
if saved.is_err() {
|
|
@@ -1876,6 +1890,10 @@ fn tick_has_work_obligation(store: &crate::message_store::MessageStore) -> bool
|
|
|
1876
1890
|
}
|
|
1877
1891
|
|
|
1878
1892
|
fn agent_probe_base_eligible(agent: &Value) -> bool {
|
|
1893
|
+
#[cfg(unix)]
|
|
1894
|
+
if crate::contract_runtime::framework::is_contract(agent) {
|
|
1895
|
+
return false;
|
|
1896
|
+
}
|
|
1879
1897
|
let status = agent.get("status").and_then(Value::as_str);
|
|
1880
1898
|
!matches!(
|
|
1881
1899
|
status,
|