Loading...
Loading...
Compare original and translation side by side
SCAN EVALUATE ACT WAIT
| | | |
v v v v
list agents -> check each -> nudge/escalate -> sleep interval
(active ones) for stalls if needed (default 5 min)const state = new StateManager({ stateDir: '.chipset/state/' });
const patrolInterval = 5 * 60 * 1000; // 5 minutes (configurable)
const stallThreshold = 30 * 60 * 1000; // 30 minutes (configurable)
async function patrol(): Promise<void> {
// Get all agents that should be working
const agents = await state.listAgents({ role: 'polecat' });
const active = agents.filter(a => a.status === 'active');
for (const agent of active) {
const hook = await state.getHook(agent.id);
if (!hook || hook.status !== 'active') continue;
// Check last activity timestamp
const lastActivity = new Date(hook.lastActivity).getTime();
const elapsed = Date.now() - lastActivity;
if (elapsed > stallThreshold) {
await handleStall(agent, hook, elapsed);
}
}
}SCAN EVALUATE ACT WAIT
| | | |
v v v v
list agents -> check each -> nudge/escalate -> sleep interval
(active ones) for stalls if needed (default 5 min)const state = new StateManager({ stateDir: '.chipset/state/' });
const patrolInterval = 5 * 60 * 1000; // 5 minutes (configurable)
const stallThreshold = 30 * 60 * 1000; // 30 minutes (configurable)
async function patrol(): Promise<void> {
// Get all agents that should be working
const agents = await state.listAgents({ role: 'polecat' });
const active = agents.filter(a => a.status === 'active');
for (const agent of active) {
const hook = await state.getHook(agent.id);
if (!hook || hook.status !== 'active') continue;
// Check last activity timestamp
const lastActivity = new Date(hook.lastActivity).getTime();
const elapsed = Date.now() - lastActivity;
if (elapsed > stallThreshold) {
await handleStall(agent, hook, elapsed);
}
}
}| Indicator | What It Means |
|---|---|
| Hook active, no activity for 30+ min | Agent may be stuck, crashed, or idle |
| Agent status is 'active' but hook timestamp stale | Session may have ended without cleanup |
| Multiple consecutive patrol cycles with no change | Persistent stall, needs escalation |
type StallSeverity = 'warning' | 'alert' | 'critical';
function classifyStall(elapsed: number, nudgesSent: number): StallSeverity {
if (nudgesSent >= 2) return 'critical'; // Nudged twice, still stalled
if (elapsed > 60 * 60 * 1000) return 'alert'; // Over 1 hour
return 'warning'; // First detection
}| 指标 | 含义 |
|---|---|
| Hook处于活跃状态,但30分钟以上无活动 | Agent可能已卡住、崩溃或处于空闲状态 |
| Agent状态为“活跃”但Hook时间戳已过期 | 会话可能已结束但未清理 |
| 连续多个巡检周期无变化 | 持续停滞,需要上报 |
type StallSeverity = 'warning' | 'alert' | 'critical';
function classifyStall(elapsed: number, nudgesSent: number): StallSeverity {
if (nudgesSent >= 2) return 'critical'; // Nudged twice, still stalled
if (elapsed > 60 * 60 * 1000) return 'alert'; // Over 1 hour
return 'warning'; // First detection
}async function handleStall(
agent: AgentIdentity,
hook: HookState,
elapsed: number
): Promise<void> {
const severity = classifyStall(elapsed, getNudgeCount(agent.id));
if (severity === 'warning') {
// First nudge: ask agent if it's still working
const nudge: AgentMessage = {
from: witnessId,
to: agent.id,
channel: 'nudge',
payload: `HEALTH_CHECK: no activity for ${Math.floor(elapsed / 60000)}m on ${hook.workItem?.beadId}`,
timestamp: new Date().toISOString(),
durable: false,
};
// Write nudge file
recordNudge(agent.id);
return;
}
if (severity === 'alert' || severity === 'critical') {
// Escalate to mayor
await escalateToMayor(agent, hook, severity, elapsed);
}
}async function escalateToMayor(
agent: AgentIdentity,
hook: HookState,
severity: StallSeverity,
elapsed: number
): Promise<void> {
const escalation: AgentMessage = {
from: witnessId,
to: 'mayor',
channel: 'mail',
payload: `STALL_${severity.toUpperCase()}: ${agent.id} idle ${Math.floor(elapsed / 60000)}m on ${hook.workItem?.beadId}`,
timestamp: new Date().toISOString(),
durable: true,
};
// Write escalation to .chipset/state/mail/mayor/{timestamp}-{witnessId}.json
}async function handleStall(
agent: AgentIdentity,
hook: HookState,
elapsed: number
): Promise<void> {
const severity = classifyStall(elapsed, getNudgeCount(agent.id));
if (severity === 'warning') {
// First nudge: ask agent if it's still working
const nudge: AgentMessage = {
from: witnessId,
to: agent.id,
channel: 'nudge',
payload: `HEALTH_CHECK: no activity for ${Math.floor(elapsed / 60000)}m on ${hook.workItem?.beadId}`,
timestamp: new Date().toISOString(),
durable: false,
};
// Write nudge file
recordNudge(agent.id);
return;
}
if (severity === 'alert' || severity === 'critical') {
// Escalate to mayor
await escalateToMayor(agent, hook, severity, elapsed);
}
}async function escalateToMayor(
agent: AgentIdentity,
hook: HookState,
severity: StallSeverity,
elapsed: number
): Promise<void> {
const escalation: AgentMessage = {
from: witnessId,
to: 'mayor',
channel: 'mail',
payload: `STALL_${severity.toUpperCase()}: ${agent.id} idle ${Math.floor(elapsed / 60000)}m on ${hook.workItem?.beadId}`,
timestamp: new Date().toISOString(),
durable: true,
};
// Write escalation to .chipset/state/mail/mayor/{timestamp}-{witnessId}.json
}interface RigHealthReport {
rigName: string;
timestamp: string;
totalAgents: number;
activeAgents: number;
stalledAgents: number;
idleAgents: number;
terminatedAgents: number;
stalledDetails: Array<{
agentId: string;
beadId: string;
stalledMinutes: number;
nudgesSent: number;
}>;
}interface RigHealthReport {
rigName: string;
timestamp: string;
totalAgents: number;
activeAgents: number;
stalledAgents: number;
idleAgents: number;
terminatedAgents: number;
stalledDetails: Array<{
agentId: string;
beadId: string;
stalledMinutes: number;
nudgesSent: number;
}>;
}| Channel | Target | Purpose | Durability |
|---|---|---|---|
| Stalled polecats | "Are you still working?" health check | Non-durable |
| Mayor | Stall alerts (warning, alert, critical) | Durable |
| Mayor | Health report summaries | Durable |
| 通道 | 目标 | 用途 | 持久性 |
|---|---|---|---|
| 停滞的polecat | 健康检查:“你是否仍在工作?” | 非持久化 |
| Mayor | 停滞警报(警告、警示、严重) | 持久化 |
| Mayor | 健康状态汇总报告 | 持久化 |
| Channel | Source | Content |
|---|---|---|
| Mayor | Instructions (adjust thresholds, focus on specific agent) |
| Polecats | Status responses to nudges |
| 通道 | 来源 | 内容 |
|---|---|---|
| Mayor | 指令(调整阈值、重点监控特定Agent) |
| Polecat | 对提示信息的状态回复 |
critical| Skill | Relationship |
|---|---|
| Witness reports stalls and health TO mayor |
| Witness monitors polecat health, sends nudges |
| Witness can observe refinery queue depth and merge failures |
| Witness reads state via StateManager (read-only) |
| 技能 | 关系 |
|---|---|
| Witness向Mayor上报停滞情况和健康状态 |
| Witness监控polecat的健康状态并发送提示信息 |
| Witness可观察refinery队列深度和合并失败情况 |
| Witness通过StateManager读取状态(只读) |
references/gastown-origin.mdreferences/boundaries.mdreferences/gastown-origin.mdreferences/boundaries.md