@runcore-sh/runcore 0.1.8 → 0.1.10
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/dist/access/manifest.d.ts +59 -0
- package/dist/access/manifest.d.ts.map +1 -0
- package/dist/access/manifest.js +251 -0
- package/dist/access/manifest.js.map +1 -0
- package/dist/activity/log.d.ts +1 -1
- package/dist/activity/log.d.ts.map +1 -1
- package/dist/agents/autonomous.d.ts.map +1 -1
- package/dist/agents/autonomous.js +38 -0
- package/dist/agents/autonomous.js.map +1 -1
- package/dist/agents/governance.d.ts +70 -0
- package/dist/agents/governance.d.ts.map +1 -0
- package/dist/agents/governance.js +220 -0
- package/dist/agents/governance.js.map +1 -0
- package/dist/agents/governed-spawn.d.ts +83 -0
- package/dist/agents/governed-spawn.d.ts.map +1 -0
- package/dist/agents/governed-spawn.js +186 -0
- package/dist/agents/governed-spawn.js.map +1 -0
- package/dist/agents/heartbeat.d.ts +91 -0
- package/dist/agents/heartbeat.d.ts.map +1 -0
- package/dist/agents/heartbeat.js +323 -0
- package/dist/agents/heartbeat.js.map +1 -0
- package/dist/agents/index.d.ts +4 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +6 -1
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/spawn-policy.d.ts +45 -0
- package/dist/agents/spawn-policy.d.ts.map +1 -0
- package/dist/agents/spawn-policy.js +202 -0
- package/dist/agents/spawn-policy.js.map +1 -0
- package/dist/alert.d.ts +16 -0
- package/dist/alert.d.ts.map +1 -0
- package/dist/alert.js +70 -0
- package/dist/alert.js.map +1 -0
- package/dist/cli.js +261 -32
- package/dist/cli.js.map +1 -1
- package/dist/credentials/store.d.ts +1 -1
- package/dist/credentials/store.d.ts.map +1 -1
- package/dist/credentials/store.js +14 -3
- package/dist/credentials/store.js.map +1 -1
- package/dist/crystallizer.d.ts +56 -0
- package/dist/crystallizer.d.ts.map +1 -0
- package/dist/crystallizer.js +159 -0
- package/dist/crystallizer.js.map +1 -0
- package/dist/distiller.d.ts +48 -0
- package/dist/distiller.d.ts.map +1 -0
- package/dist/distiller.js +140 -0
- package/dist/distiller.js.map +1 -0
- package/dist/files/deep-index.d.ts +59 -0
- package/dist/files/deep-index.d.ts.map +1 -0
- package/dist/files/deep-index.js +337 -0
- package/dist/files/deep-index.js.map +1 -0
- package/dist/files/import.d.ts +44 -0
- package/dist/files/import.d.ts.map +1 -0
- package/dist/files/import.js +213 -0
- package/dist/files/import.js.map +1 -0
- package/dist/files/index-local.d.ts +37 -0
- package/dist/files/index-local.d.ts.map +1 -0
- package/dist/files/index-local.js +198 -0
- package/dist/files/index-local.js.map +1 -0
- package/dist/google/auth.d.ts +2 -0
- package/dist/google/auth.d.ts.map +1 -1
- package/dist/google/auth.js +2 -0
- package/dist/google/auth.js.map +1 -1
- package/dist/integrations/gate.d.ts +40 -0
- package/dist/integrations/gate.d.ts.map +1 -0
- package/dist/integrations/gate.js +100 -0
- package/dist/integrations/gate.js.map +1 -0
- package/dist/lib/audit.d.ts +43 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +120 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/brain-io.d.ts.map +1 -1
- package/dist/lib/brain-io.js +52 -0
- package/dist/lib/brain-io.js.map +1 -1
- package/dist/lib/dpapi.d.ts +14 -0
- package/dist/lib/dpapi.d.ts.map +1 -0
- package/dist/lib/dpapi.js +104 -0
- package/dist/lib/dpapi.js.map +1 -0
- package/dist/lib/glob-match.d.ts +22 -0
- package/dist/lib/glob-match.d.ts.map +1 -0
- package/dist/lib/glob-match.js +64 -0
- package/dist/lib/glob-match.js.map +1 -0
- package/dist/lib/locked.d.ts +40 -0
- package/dist/lib/locked.d.ts.map +1 -0
- package/dist/lib/locked.js +130 -0
- package/dist/lib/locked.js.map +1 -0
- package/dist/llm/complete.d.ts.map +1 -1
- package/dist/llm/complete.js +5 -2
- package/dist/llm/complete.js.map +1 -1
- package/dist/llm/fetch-guard.d.ts +16 -0
- package/dist/llm/fetch-guard.d.ts.map +1 -0
- package/dist/llm/fetch-guard.js +61 -0
- package/dist/llm/fetch-guard.js.map +1 -0
- package/dist/llm/guard.d.ts +40 -0
- package/dist/llm/guard.d.ts.map +1 -0
- package/dist/llm/guard.js +88 -0
- package/dist/llm/guard.js.map +1 -0
- package/dist/llm/membrane.d.ts +46 -0
- package/dist/llm/membrane.d.ts.map +1 -0
- package/dist/llm/membrane.js +123 -0
- package/dist/llm/membrane.js.map +1 -0
- package/dist/llm/providers/index.d.ts +5 -1
- package/dist/llm/providers/index.d.ts.map +1 -1
- package/dist/llm/providers/index.js +8 -1
- package/dist/llm/providers/index.js.map +1 -1
- package/dist/llm/redact.d.ts +39 -0
- package/dist/llm/redact.d.ts.map +1 -0
- package/dist/llm/redact.js +155 -0
- package/dist/llm/redact.js.map +1 -0
- package/dist/llm/sensitive-registry.d.ts +33 -0
- package/dist/llm/sensitive-registry.d.ts.map +1 -0
- package/dist/llm/sensitive-registry.js +106 -0
- package/dist/llm/sensitive-registry.js.map +1 -0
- package/dist/mcp-server.d.ts +11 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +520 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mdns.d.ts +17 -0
- package/dist/mdns.d.ts.map +1 -0
- package/dist/mdns.js +110 -0
- package/dist/mdns.js.map +1 -0
- package/dist/nerve/push.d.ts +26 -0
- package/dist/nerve/push.d.ts.map +1 -0
- package/dist/nerve/push.js +170 -0
- package/dist/nerve/push.js.map +1 -0
- package/dist/nerve/state.d.ts +35 -0
- package/dist/nerve/state.d.ts.map +1 -0
- package/dist/nerve/state.js +257 -0
- package/dist/nerve/state.js.map +1 -0
- package/dist/posture/engine.d.ts +41 -0
- package/dist/posture/engine.d.ts.map +1 -0
- package/dist/posture/engine.js +217 -0
- package/dist/posture/engine.js.map +1 -0
- package/dist/posture/index.d.ts +11 -0
- package/dist/posture/index.d.ts.map +1 -0
- package/dist/posture/index.js +10 -0
- package/dist/posture/index.js.map +1 -0
- package/dist/posture/middleware.d.ts +30 -0
- package/dist/posture/middleware.d.ts.map +1 -0
- package/dist/posture/middleware.js +92 -0
- package/dist/posture/middleware.js.map +1 -0
- package/dist/posture/types.d.ts +61 -0
- package/dist/posture/types.d.ts.map +1 -0
- package/dist/posture/types.js +48 -0
- package/dist/posture/types.js.map +1 -0
- package/dist/resend/inbox.d.ts +23 -0
- package/dist/resend/inbox.d.ts.map +1 -0
- package/dist/resend/inbox.js +198 -0
- package/dist/resend/inbox.js.map +1 -0
- package/dist/resend/webhooks.d.ts +30 -0
- package/dist/resend/webhooks.d.ts.map +1 -0
- package/dist/resend/webhooks.js +244 -0
- package/dist/resend/webhooks.js.map +1 -0
- package/dist/server.d.ts +5 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +773 -58
- package/dist/server.js.map +1 -1
- package/dist/settings.d.ts +14 -1
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +32 -1
- package/dist/settings.js.map +1 -1
- package/dist/tier/bond.d.ts +51 -0
- package/dist/tier/bond.d.ts.map +1 -0
- package/dist/tier/bond.js +154 -0
- package/dist/tier/bond.js.map +1 -0
- package/dist/tier/freeze.d.ts +21 -0
- package/dist/tier/freeze.d.ts.map +1 -0
- package/dist/tier/freeze.js +73 -0
- package/dist/tier/freeze.js.map +1 -0
- package/dist/tier/gate.d.ts +11 -0
- package/dist/tier/gate.d.ts.map +1 -0
- package/dist/tier/gate.js +25 -0
- package/dist/tier/gate.js.map +1 -0
- package/dist/tier/heartbeat.d.ts +22 -0
- package/dist/tier/heartbeat.d.ts.map +1 -0
- package/dist/tier/heartbeat.js +128 -0
- package/dist/tier/heartbeat.js.map +1 -0
- package/dist/tier/token.d.ts +22 -0
- package/dist/tier/token.d.ts.map +1 -0
- package/dist/tier/token.js +100 -0
- package/dist/tier/token.js.map +1 -0
- package/dist/tier/types.d.ts +44 -0
- package/dist/tier/types.d.ts.map +1 -0
- package/dist/tier/types.js +61 -0
- package/dist/tier/types.js.map +1 -0
- package/dist/updater.d.ts +32 -0
- package/dist/updater.d.ts.map +1 -0
- package/dist/updater.js +145 -0
- package/dist/updater.js.map +1 -0
- package/dist/vault/policy.d.ts +42 -0
- package/dist/vault/policy.d.ts.map +1 -0
- package/dist/vault/policy.js +159 -0
- package/dist/vault/policy.js.map +1 -0
- package/dist/vault/store.d.ts +6 -0
- package/dist/vault/store.d.ts.map +1 -1
- package/dist/vault/store.js +15 -5
- package/dist/vault/store.js.map +1 -1
- package/dist/vault/transfer.d.ts +33 -0
- package/dist/vault/transfer.d.ts.map +1 -0
- package/dist/vault/transfer.js +187 -0
- package/dist/vault/transfer.js.map +1 -0
- package/dist/voucher.d.ts +39 -0
- package/dist/voucher.d.ts.map +1 -0
- package/dist/voucher.js +105 -0
- package/dist/voucher.js.map +1 -0
- package/dist/webhooks/handlers.d.ts +10 -0
- package/dist/webhooks/handlers.d.ts.map +1 -1
- package/dist/webhooks/handlers.js +53 -0
- package/dist/webhooks/handlers.js.map +1 -1
- package/dist/webhooks/index.d.ts +2 -2
- package/dist/webhooks/index.d.ts.map +1 -1
- package/dist/webhooks/index.js +2 -2
- package/dist/webhooks/index.js.map +1 -1
- package/dist/webhooks/verify.d.ts +8 -0
- package/dist/webhooks/verify.d.ts.map +1 -1
- package/dist/webhooks/verify.js +56 -0
- package/dist/webhooks/verify.js.map +1 -1
- package/package.json +8 -2
- package/public/board.html +8 -3
- package/public/browser.html +8 -3
- package/public/library.html +8 -3
- package/public/observatory.html +8 -3
- package/public/ops.html +8 -3
- package/public/registry.html +627 -0
- package/public/roadmap.html +975 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Heartbeat Monitor — CORE-9
|
|
3
|
+
*
|
|
4
|
+
* Action-based heartbeat: every agent action IS the ping.
|
|
5
|
+
* No polling, no timer-based pings. The append-only log is the tracking.
|
|
6
|
+
*
|
|
7
|
+
* Monitors:
|
|
8
|
+
* 1. **Silence detection** — agent produces no output for too long → terminate
|
|
9
|
+
* 2. **Drift detection** — agent's actions diverge from the assigned task → warn/terminate
|
|
10
|
+
* 3. **Heartbeat logging** — append-only JSONL trail of agent pulses
|
|
11
|
+
*
|
|
12
|
+
* Designed to work with the existing activity log system. Each agent gets
|
|
13
|
+
* a HeartbeatTracker that watches for signs of life and task adherence.
|
|
14
|
+
*/
|
|
15
|
+
import { join, resolve } from "node:path";
|
|
16
|
+
import { createLogger } from "../utils/logger.js";
|
|
17
|
+
import { logActivity, generateTraceId } from "../activity/log.js";
|
|
18
|
+
import { appendBrainLineSync, ensureBrainFileSync, } from "../lib/brain-io.js";
|
|
19
|
+
const log = createLogger("heartbeat");
|
|
20
|
+
const OPS_DIR = resolve(process.cwd(), "brain", "ops");
|
|
21
|
+
const HEARTBEAT_FILE = join(OPS_DIR, "heartbeats.jsonl");
|
|
22
|
+
const SCHEMA_LINE = JSON.stringify({ _schema: "heartbeat", _version: "1.0" });
|
|
23
|
+
// Ensure the file exists on module load
|
|
24
|
+
try {
|
|
25
|
+
ensureBrainFileSync(HEARTBEAT_FILE, SCHEMA_LINE);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Non-fatal — will retry on first write
|
|
29
|
+
}
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Default config
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
const DEFAULT_CONFIG = {
|
|
34
|
+
silenceWarningMs: 120_000, // 2 minutes
|
|
35
|
+
silenceTerminateMs: 300_000, // 5 minutes
|
|
36
|
+
checkIntervalMs: 15_000, // 15 seconds
|
|
37
|
+
taskDescription: "",
|
|
38
|
+
taskKeywords: [],
|
|
39
|
+
maxDriftWarnings: 3,
|
|
40
|
+
};
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Heartbeat persistence
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
function persistPulse(pulse) {
|
|
45
|
+
try {
|
|
46
|
+
ensureBrainFileSync(HEARTBEAT_FILE, SCHEMA_LINE);
|
|
47
|
+
appendBrainLineSync(HEARTBEAT_FILE, JSON.stringify(pulse));
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
log.warn("Failed to persist heartbeat pulse", {
|
|
51
|
+
taskId: pulse.taskId,
|
|
52
|
+
error: err instanceof Error ? err.message : String(err),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Keyword extraction for drift detection
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
/** Extract meaningful keywords from a task description. */
|
|
60
|
+
export function extractTaskKeywords(description) {
|
|
61
|
+
// Remove common stop words and short words, keep meaningful terms
|
|
62
|
+
const stopWords = new Set([
|
|
63
|
+
"the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for",
|
|
64
|
+
"of", "with", "by", "from", "is", "are", "was", "were", "be", "been",
|
|
65
|
+
"being", "have", "has", "had", "do", "does", "did", "will", "would",
|
|
66
|
+
"could", "should", "may", "might", "shall", "can", "this", "that",
|
|
67
|
+
"these", "those", "it", "its", "not", "no", "all", "any", "each",
|
|
68
|
+
"every", "if", "then", "else", "when", "where", "how", "what", "which",
|
|
69
|
+
"who", "whom", "why", "so", "as", "up", "out", "about", "into", "over",
|
|
70
|
+
"after", "before", "between", "under", "above", "below", "just", "also",
|
|
71
|
+
"very", "too", "only", "own", "same", "than", "other", "such", "more",
|
|
72
|
+
"most", "some", "make", "use", "get", "set", "add", "run", "file",
|
|
73
|
+
]);
|
|
74
|
+
return description
|
|
75
|
+
.toLowerCase()
|
|
76
|
+
.replace(/[^a-z0-9\s-]/g, " ")
|
|
77
|
+
.split(/\s+/)
|
|
78
|
+
.filter((w) => w.length > 2 && !stopWords.has(w))
|
|
79
|
+
.slice(0, 20); // Cap at 20 keywords
|
|
80
|
+
}
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Simple drift heuristic
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
/**
|
|
85
|
+
* Score how relevant an action summary is to the original task.
|
|
86
|
+
* Returns 0.0 (no overlap) to 1.0 (perfect overlap).
|
|
87
|
+
* Uses simple keyword overlap — not semantic, but fast and transparent.
|
|
88
|
+
*/
|
|
89
|
+
function driftScore(actionSummary, taskKeywords) {
|
|
90
|
+
if (taskKeywords.length === 0)
|
|
91
|
+
return 1.0; // No keywords = no drift detection
|
|
92
|
+
const actionWords = new Set(actionSummary
|
|
93
|
+
.toLowerCase()
|
|
94
|
+
.replace(/[^a-z0-9\s-]/g, " ")
|
|
95
|
+
.split(/\s+/)
|
|
96
|
+
.filter((w) => w.length > 2));
|
|
97
|
+
let matches = 0;
|
|
98
|
+
for (const kw of taskKeywords) {
|
|
99
|
+
if (actionWords.has(kw))
|
|
100
|
+
matches++;
|
|
101
|
+
}
|
|
102
|
+
return matches / taskKeywords.length;
|
|
103
|
+
}
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// HeartbeatTracker — per-agent instance
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
export class HeartbeatTracker {
|
|
108
|
+
taskId;
|
|
109
|
+
instanceId;
|
|
110
|
+
config;
|
|
111
|
+
onTerminate;
|
|
112
|
+
lastPulseAt;
|
|
113
|
+
driftWarnings = 0;
|
|
114
|
+
totalPulses = 0;
|
|
115
|
+
checkTimer = null;
|
|
116
|
+
terminated = false;
|
|
117
|
+
traceId;
|
|
118
|
+
constructor(taskId, instanceId, config, onTerminate) {
|
|
119
|
+
this.taskId = taskId;
|
|
120
|
+
this.instanceId = instanceId;
|
|
121
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
122
|
+
this.onTerminate = onTerminate;
|
|
123
|
+
this.lastPulseAt = Date.now();
|
|
124
|
+
this.traceId = generateTraceId();
|
|
125
|
+
// If task description provided but no keywords, extract them
|
|
126
|
+
if (this.config.taskDescription && this.config.taskKeywords.length === 0) {
|
|
127
|
+
this.config.taskKeywords = extractTaskKeywords(this.config.taskDescription);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Start monitoring the agent's heartbeat. */
|
|
131
|
+
start() {
|
|
132
|
+
if (this.checkTimer)
|
|
133
|
+
return;
|
|
134
|
+
this.recordPulse("spawn", "Agent spawned, heartbeat tracking started");
|
|
135
|
+
this.checkTimer = setInterval(() => {
|
|
136
|
+
this.check();
|
|
137
|
+
}, this.config.checkIntervalMs);
|
|
138
|
+
log.info("Heartbeat tracking started", {
|
|
139
|
+
taskId: this.taskId,
|
|
140
|
+
instanceId: this.instanceId,
|
|
141
|
+
keywords: this.config.taskKeywords.slice(0, 5),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/** Stop monitoring (call on agent completion). */
|
|
145
|
+
stop(reason = "complete") {
|
|
146
|
+
if (this.checkTimer) {
|
|
147
|
+
clearInterval(this.checkTimer);
|
|
148
|
+
this.checkTimer = null;
|
|
149
|
+
}
|
|
150
|
+
this.recordPulse(reason, `Heartbeat tracking stopped: ${reason}`);
|
|
151
|
+
log.info("Heartbeat tracking stopped", {
|
|
152
|
+
taskId: this.taskId,
|
|
153
|
+
instanceId: this.instanceId,
|
|
154
|
+
reason,
|
|
155
|
+
totalPulses: this.totalPulses,
|
|
156
|
+
driftWarnings: this.driftWarnings,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Record a heartbeat pulse. Call this whenever the agent produces
|
|
161
|
+
* observable output (file write, stdout, checkpoint).
|
|
162
|
+
*/
|
|
163
|
+
recordPulse(type, detail, actionSummary) {
|
|
164
|
+
if (this.terminated)
|
|
165
|
+
return;
|
|
166
|
+
this.lastPulseAt = Date.now();
|
|
167
|
+
this.totalPulses++;
|
|
168
|
+
const pulse = {
|
|
169
|
+
timestamp: new Date().toISOString(),
|
|
170
|
+
taskId: this.taskId,
|
|
171
|
+
instanceId: this.instanceId,
|
|
172
|
+
type,
|
|
173
|
+
detail,
|
|
174
|
+
actionSummary,
|
|
175
|
+
};
|
|
176
|
+
persistPulse(pulse);
|
|
177
|
+
// Drift detection on action pulses
|
|
178
|
+
if (type === "action" && actionSummary && this.config.taskKeywords.length > 0) {
|
|
179
|
+
const score = driftScore(actionSummary, this.config.taskKeywords);
|
|
180
|
+
if (score < 0.1) {
|
|
181
|
+
this.driftWarnings++;
|
|
182
|
+
log.warn("Drift detected", {
|
|
183
|
+
taskId: this.taskId,
|
|
184
|
+
instanceId: this.instanceId,
|
|
185
|
+
score,
|
|
186
|
+
actionSummary: actionSummary.slice(0, 100),
|
|
187
|
+
driftWarnings: this.driftWarnings,
|
|
188
|
+
maxDriftWarnings: this.config.maxDriftWarnings,
|
|
189
|
+
});
|
|
190
|
+
logActivity({
|
|
191
|
+
source: "agent",
|
|
192
|
+
summary: `Drift warning (${this.driftWarnings}/${this.config.maxDriftWarnings}): ${this.taskId}`,
|
|
193
|
+
detail: `Score: ${score.toFixed(2)}, action: ${actionSummary.slice(0, 200)}`,
|
|
194
|
+
traceId: this.traceId,
|
|
195
|
+
actionLabel: "AUTONOMOUS",
|
|
196
|
+
reason: "heartbeat-drift",
|
|
197
|
+
});
|
|
198
|
+
if (this.driftWarnings >= this.config.maxDriftWarnings) {
|
|
199
|
+
this.terminateAgent(`Drift limit exceeded (${this.driftWarnings} warnings)`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (score > 0.3) {
|
|
203
|
+
// Good signal — reset drift counter on clearly relevant actions
|
|
204
|
+
this.driftWarnings = Math.max(0, this.driftWarnings - 1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** Get the current heartbeat status. */
|
|
209
|
+
getStatus() {
|
|
210
|
+
const silenceMs = Date.now() - this.lastPulseAt;
|
|
211
|
+
let state = "healthy";
|
|
212
|
+
if (this.terminated) {
|
|
213
|
+
state = "terminated";
|
|
214
|
+
}
|
|
215
|
+
else if (silenceMs > this.config.silenceTerminateMs) {
|
|
216
|
+
state = "critical";
|
|
217
|
+
}
|
|
218
|
+
else if (silenceMs > this.config.silenceWarningMs ||
|
|
219
|
+
this.driftWarnings >= this.config.maxDriftWarnings - 1) {
|
|
220
|
+
state = "warning";
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
taskId: this.taskId,
|
|
224
|
+
instanceId: this.instanceId,
|
|
225
|
+
alive: !this.terminated,
|
|
226
|
+
lastPulseAt: this.lastPulseAt ? new Date(this.lastPulseAt).toISOString() : null,
|
|
227
|
+
silenceMs,
|
|
228
|
+
driftWarnings: this.driftWarnings,
|
|
229
|
+
totalPulses: this.totalPulses,
|
|
230
|
+
state,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// -------------------------------------------------------------------------
|
|
234
|
+
// Internal
|
|
235
|
+
// -------------------------------------------------------------------------
|
|
236
|
+
check() {
|
|
237
|
+
if (this.terminated)
|
|
238
|
+
return;
|
|
239
|
+
const silenceMs = Date.now() - this.lastPulseAt;
|
|
240
|
+
if (silenceMs > this.config.silenceTerminateMs) {
|
|
241
|
+
this.terminateAgent(`Silence timeout: no activity for ${Math.round(silenceMs / 1000)}s ` +
|
|
242
|
+
`(limit: ${Math.round(this.config.silenceTerminateMs / 1000)}s)`);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (silenceMs > this.config.silenceWarningMs) {
|
|
246
|
+
this.recordPulse("silence-warning", `No activity for ${Math.round(silenceMs / 1000)}s`);
|
|
247
|
+
logActivity({
|
|
248
|
+
source: "agent",
|
|
249
|
+
summary: `Silence warning: ${this.taskId}`,
|
|
250
|
+
detail: `No activity for ${Math.round(silenceMs / 1000)}s`,
|
|
251
|
+
traceId: this.traceId,
|
|
252
|
+
actionLabel: "AUTONOMOUS",
|
|
253
|
+
reason: "heartbeat-silence",
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
terminateAgent(reason) {
|
|
258
|
+
if (this.terminated)
|
|
259
|
+
return;
|
|
260
|
+
this.terminated = true;
|
|
261
|
+
log.warn("Terminating agent via heartbeat", {
|
|
262
|
+
taskId: this.taskId,
|
|
263
|
+
instanceId: this.instanceId,
|
|
264
|
+
reason,
|
|
265
|
+
});
|
|
266
|
+
this.recordPulse("terminate", reason);
|
|
267
|
+
logActivity({
|
|
268
|
+
source: "agent",
|
|
269
|
+
summary: `Heartbeat termination: ${this.taskId}`,
|
|
270
|
+
detail: reason,
|
|
271
|
+
traceId: this.traceId,
|
|
272
|
+
actionLabel: "AUTONOMOUS",
|
|
273
|
+
reason: "heartbeat-terminate",
|
|
274
|
+
});
|
|
275
|
+
this.stop("terminate");
|
|
276
|
+
// Fire-and-forget termination callback
|
|
277
|
+
this.onTerminate(this.instanceId, reason).catch((err) => {
|
|
278
|
+
log.error("Failed to terminate agent", {
|
|
279
|
+
instanceId: this.instanceId,
|
|
280
|
+
error: err instanceof Error ? err.message : String(err),
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
// Tracker registry — manage all active heartbeat trackers
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
const activeTrackers = new Map();
|
|
289
|
+
/** Create and register a heartbeat tracker for an agent instance. */
|
|
290
|
+
export function createHeartbeatTracker(taskId, instanceId, config, onTerminate) {
|
|
291
|
+
// Clean up any existing tracker for this instance
|
|
292
|
+
const existing = activeTrackers.get(instanceId);
|
|
293
|
+
if (existing) {
|
|
294
|
+
existing.stop("terminate");
|
|
295
|
+
}
|
|
296
|
+
const tracker = new HeartbeatTracker(taskId, instanceId, config, onTerminate);
|
|
297
|
+
activeTrackers.set(instanceId, tracker);
|
|
298
|
+
return tracker;
|
|
299
|
+
}
|
|
300
|
+
/** Get a tracker by instance ID. */
|
|
301
|
+
export function getHeartbeatTracker(instanceId) {
|
|
302
|
+
return activeTrackers.get(instanceId);
|
|
303
|
+
}
|
|
304
|
+
/** Remove a tracker (call on agent completion). */
|
|
305
|
+
export function removeHeartbeatTracker(instanceId) {
|
|
306
|
+
const tracker = activeTrackers.get(instanceId);
|
|
307
|
+
if (tracker) {
|
|
308
|
+
tracker.stop();
|
|
309
|
+
activeTrackers.delete(instanceId);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/** Get status of all tracked agents. */
|
|
313
|
+
export function getAllHeartbeatStatuses() {
|
|
314
|
+
return Array.from(activeTrackers.values()).map((t) => t.getStatus());
|
|
315
|
+
}
|
|
316
|
+
/** Stop all trackers (call on shutdown). */
|
|
317
|
+
export function shutdownHeartbeats() {
|
|
318
|
+
for (const [id, tracker] of activeTrackers) {
|
|
319
|
+
tracker.stop("terminate");
|
|
320
|
+
activeTrackers.delete(id);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/agents/heartbeat.ts"],"names":[],"mappings":"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"}
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ export { acquireLocks, releaseLocks, releaseFileLock, forceReleaseLock, listLock
|
|
|
7
7
|
export { Orchestrator, parseFilesFromOutput, type WorkflowTaskDef, type CreateWorkflowInput, type Workflow, type WorkflowTask, type WorkflowResult, type WorkflowStatus, type TaskStatus, type FileConflict, type ConflictStrategy, type ExecutionMode, type OrchestratorReport, } from "./orchestration.js";
|
|
8
8
|
export { TaskCooldownManager, type CooldownConfig, type CooldownEntry, type CooldownStatus, } from "./cooldown.js";
|
|
9
9
|
export { WorkflowEngine, parseWorkflowFile, type WorkflowDefinition, type StepDef, type Condition, type FailurePolicy, type WorkflowRun, type StepResult, type StepStatus, } from "./workflow.js";
|
|
10
|
+
export { governanceGate, revokeGovernanceVoucher, validateGovernanceVoucher, type GovernanceDecision, type GovernanceAuditEntry, type GovernanceOptions, } from "./governance.js";
|
|
11
|
+
export { HeartbeatTracker, createHeartbeatTracker, getHeartbeatTracker, removeHeartbeatTracker, getAllHeartbeatStatuses, shutdownHeartbeats, extractTaskKeywords, type HeartbeatPulse, type HeartbeatConfig, type HeartbeatStatus, type TerminateCallback, } from "./heartbeat.js";
|
|
12
|
+
export { governedSpawn, type GovernedSpawnRequest, type GovernedSpawnResult, type GovernedSpawnDeps, } from "./governed-spawn.js";
|
|
10
13
|
import type { CreateTaskInput, AgentTask } from "./types.js";
|
|
11
14
|
/** Create a task and immediately spawn it. */
|
|
12
15
|
export declare function submitTask(input: CreateTaskInput): Promise<AgentTask>;
|
|
@@ -29,6 +32,6 @@ export declare function initAgents(): Promise<void>;
|
|
|
29
32
|
* This prevents the double-recovery race that caused DASH-82.
|
|
30
33
|
*/
|
|
31
34
|
export declare function recoverAndStartMonitor(): Promise<void>;
|
|
32
|
-
/** Shutdown: stop the monitor. Does NOT kill detached processes. */
|
|
35
|
+
/** Shutdown: stop the monitor and heartbeat trackers. Does NOT kill detached processes. */
|
|
33
36
|
export declare function shutdownAgents(): void;
|
|
34
37
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC9E,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EACL,UAAU,EACV,UAAU,EACV,QAAQ,EACR,SAAS,EACT,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACzH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC9E,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EACL,UAAU,EACV,UAAU,EACV,QAAQ,EACR,SAAS,EACT,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACzH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,GACvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAM7D,8CAA8C;AAC9C,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAI3E;AAED,+BAA+B;AAC/B,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAEnE;AAED,oCAAoC;AACpC,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE/D;AAED,6BAA6B;AAC7B,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7D;AAED;;;;GAIG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAEhD;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG5D;AAED,2FAA2F;AAC3F,wBAAgB,cAAc,IAAI,IAAI,CAGrC"}
|
package/dist/agents/index.js
CHANGED
|
@@ -5,9 +5,13 @@ export { acquireLocks, releaseLocks, releaseFileLock, forceReleaseLock, listLock
|
|
|
5
5
|
export { Orchestrator, parseFilesFromOutput, } from "./orchestration.js";
|
|
6
6
|
export { TaskCooldownManager, } from "./cooldown.js";
|
|
7
7
|
export { WorkflowEngine, parseWorkflowFile, } from "./workflow.js";
|
|
8
|
+
export { governanceGate, revokeGovernanceVoucher, validateGovernanceVoucher, } from "./governance.js";
|
|
9
|
+
export { HeartbeatTracker, createHeartbeatTracker, getHeartbeatTracker, removeHeartbeatTracker, getAllHeartbeatStatuses, shutdownHeartbeats, extractTaskKeywords, } from "./heartbeat.js";
|
|
10
|
+
export { governedSpawn, } from "./governed-spawn.js";
|
|
8
11
|
import { ensureDirs, createTask, readTask, readTaskOutput } from "./store.js";
|
|
9
12
|
import { spawnAgent, cancelAgent } from "./spawn.js";
|
|
10
13
|
import { recoverTasks, startAgentMonitor, stopAgentMonitor } from "./monitor.js";
|
|
14
|
+
import { shutdownHeartbeats } from "./heartbeat.js";
|
|
11
15
|
/** Create a task and immediately spawn it. */
|
|
12
16
|
export async function submitTask(input) {
|
|
13
17
|
const task = await createTask(input);
|
|
@@ -44,8 +48,9 @@ export async function recoverAndStartMonitor() {
|
|
|
44
48
|
await recoverTasks();
|
|
45
49
|
startAgentMonitor();
|
|
46
50
|
}
|
|
47
|
-
/** Shutdown: stop the monitor. Does NOT kill detached processes. */
|
|
51
|
+
/** Shutdown: stop the monitor and heartbeat trackers. Does NOT kill detached processes. */
|
|
48
52
|
export function shutdownAgents() {
|
|
49
53
|
stopAgentMonitor();
|
|
54
|
+
shutdownHeartbeats();
|
|
50
55
|
}
|
|
51
56
|
//# sourceMappingURL=index.js.map
|
package/dist/agents/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,UAAU,EACV,UAAU,EACV,QAAQ,EACR,SAAS,EACT,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACzH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,kBAAkB,GAInB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,oBAAoB,GAYrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,GAIpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,iBAAiB,GAQlB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,UAAU,EACV,UAAU,EACV,QAAQ,EACR,SAAS,EACT,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACzH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,kBAAkB,GAInB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,oBAAoB,GAYrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,GAIpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,iBAAiB,GAQlB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,yBAAyB,GAI1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,GAKpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,GAId,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAA6B,cAAc,EAAE,MAAM,YAAY,CAAC;AACzG,OAAO,EAAE,UAAU,EAAE,WAAW,EAAgB,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,8CAA8C;AAC9C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAsB;IACrD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+BAA+B;AAC/B,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,EAAU;IACtC,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC;AAED,oCAAoC;AACpC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAU;IAC5C,OAAO,cAAc,CAAC,EAAE,CAAC,CAAC;AAC5B,CAAC;AAED,6BAA6B;AAC7B,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAU;IACzC,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,UAAU,EAAE,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB;IAC1C,MAAM,YAAY,EAAE,CAAC;IACrB,iBAAiB,EAAE,CAAC;AACtB,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,cAAc;IAC5B,gBAAgB,EAAE,CAAC;IACnB,kBAAkB,EAAE,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawn Policy Loader — governs what agent types can be spawned and how many.
|
|
3
|
+
*
|
|
4
|
+
* Reads brain/templates/spawn-policy.yaml (or instance-specific path).
|
|
5
|
+
* Provides checkSpawnPolicy() for the governance gate to call before spawn.
|
|
6
|
+
*
|
|
7
|
+
* Hand-rolled YAML parser (no external deps).
|
|
8
|
+
*/
|
|
9
|
+
export interface AllowedType {
|
|
10
|
+
max: number;
|
|
11
|
+
autoSpawn: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface SpawnPolicy {
|
|
14
|
+
owner: string;
|
|
15
|
+
governedBy: string;
|
|
16
|
+
maxAgents: number;
|
|
17
|
+
allowedTypes: Map<string, AllowedType>;
|
|
18
|
+
denyTypes: string[];
|
|
19
|
+
requireOwnerApproval: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface SpawnPolicyCheck {
|
|
22
|
+
allowed: boolean;
|
|
23
|
+
reason?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Load and cache the spawn policy.
|
|
27
|
+
*/
|
|
28
|
+
export declare function loadSpawnPolicy(policyPath?: string): Promise<SpawnPolicy>;
|
|
29
|
+
/**
|
|
30
|
+
* Get the cached spawn policy (loads synchronously if not cached).
|
|
31
|
+
*/
|
|
32
|
+
export declare function getSpawnPolicy(): SpawnPolicy;
|
|
33
|
+
/**
|
|
34
|
+
* Check whether spawning an agent of the given type is allowed.
|
|
35
|
+
*
|
|
36
|
+
* @param agentType - The agent type to spawn (e.g. "administration", "brand")
|
|
37
|
+
* @param currentCount - Total number of currently running agents
|
|
38
|
+
* @param typeCount - Number of currently running agents of this specific type
|
|
39
|
+
*/
|
|
40
|
+
export declare function checkSpawnPolicy(agentType: string, currentCount: number, typeCount: number): SpawnPolicyCheck;
|
|
41
|
+
/**
|
|
42
|
+
* Force reload of spawn policy.
|
|
43
|
+
*/
|
|
44
|
+
export declare function reloadSpawnPolicy(policyPath?: string): Promise<SpawnPolicy>;
|
|
45
|
+
//# sourceMappingURL=spawn-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-policy.d.ts","sourceRoot":"","sources":["../../src/agents/spawn-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAcH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACvC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAmGD;;GAEG;AACH,wBAAsB,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA2B/E;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAsB5C;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,gBAAgB,CAyBlB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAGjF"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawn Policy Loader — governs what agent types can be spawned and how many.
|
|
3
|
+
*
|
|
4
|
+
* Reads brain/templates/spawn-policy.yaml (or instance-specific path).
|
|
5
|
+
* Provides checkSpawnPolicy() for the governance gate to call before spawn.
|
|
6
|
+
*
|
|
7
|
+
* Hand-rolled YAML parser (no external deps).
|
|
8
|
+
*/
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
10
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
11
|
+
import { join, resolve } from "node:path";
|
|
12
|
+
import { createLogger } from "../utils/logger.js";
|
|
13
|
+
const log = createLogger("agents.spawn-policy");
|
|
14
|
+
const BRAIN_DIR = resolve(process.cwd(), "brain");
|
|
15
|
+
const DEFAULT_POLICY_PATH = join(BRAIN_DIR, "templates", "spawn-policy.yaml");
|
|
16
|
+
// ── Cache ────────────────────────────────────────────────────────────────────
|
|
17
|
+
let cached = null;
|
|
18
|
+
// ── YAML parser ──────────────────────────────────────────────────────────────
|
|
19
|
+
function parseSpawnPolicy(raw) {
|
|
20
|
+
const lines = raw.split("\n");
|
|
21
|
+
let owner = "";
|
|
22
|
+
let governedBy = "";
|
|
23
|
+
let maxAgents = 4;
|
|
24
|
+
let requireOwnerApproval = true;
|
|
25
|
+
const allowedTypes = new Map();
|
|
26
|
+
const denyTypes = [];
|
|
27
|
+
let section = "root";
|
|
28
|
+
let currentType = "";
|
|
29
|
+
for (const line of lines) {
|
|
30
|
+
const trimmed = line.trimEnd();
|
|
31
|
+
if (!trimmed || trimmed.match(/^\s*#/))
|
|
32
|
+
continue;
|
|
33
|
+
const indent = (line.match(/^(\s*)/) ?? ["", ""])[1].length;
|
|
34
|
+
// Top-level key: value
|
|
35
|
+
if (indent === 0) {
|
|
36
|
+
const kv = trimmed.match(/^(\w[\w_]*)\s*:\s*(.+)$/);
|
|
37
|
+
if (kv) {
|
|
38
|
+
const [, key, val] = kv;
|
|
39
|
+
const unquoted = val.replace(/^["']|["']$/g, "").trim();
|
|
40
|
+
if (key === "owner")
|
|
41
|
+
owner = unquoted;
|
|
42
|
+
else if (key === "governed_by")
|
|
43
|
+
governedBy = unquoted;
|
|
44
|
+
else if (key === "max_agents")
|
|
45
|
+
maxAgents = parseInt(unquoted, 10) || 4;
|
|
46
|
+
else if (key === "require_owner_approval")
|
|
47
|
+
requireOwnerApproval = unquoted === "true";
|
|
48
|
+
section = "root";
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
// Section header
|
|
52
|
+
const sectionHeader = trimmed.match(/^(\w[\w_]*)\s*:\s*$/);
|
|
53
|
+
if (sectionHeader) {
|
|
54
|
+
if (sectionHeader[1] === "allowed_types")
|
|
55
|
+
section = "allowed_types";
|
|
56
|
+
else if (sectionHeader[1] === "deny_types")
|
|
57
|
+
section = "deny_types";
|
|
58
|
+
else
|
|
59
|
+
section = "root";
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Under allowed_types — type name headers (indent 2)
|
|
64
|
+
if (section === "allowed_types" && indent >= 2) {
|
|
65
|
+
const typeHeader = trimmed.trim().match(/^([\w-]+)\s*:\s*$/);
|
|
66
|
+
if (typeHeader) {
|
|
67
|
+
currentType = typeHeader[1];
|
|
68
|
+
allowedTypes.set(currentType, { max: 1, autoSpawn: false });
|
|
69
|
+
section = "allowed_type_entry";
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Under a specific allowed type entry (indent 4)
|
|
74
|
+
if (section === "allowed_type_entry" && indent >= 4) {
|
|
75
|
+
const kv = trimmed.trim().match(/^(\w[\w_]*)\s*:\s*(.+)$/);
|
|
76
|
+
if (kv) {
|
|
77
|
+
const [, key, val] = kv;
|
|
78
|
+
const entry = allowedTypes.get(currentType);
|
|
79
|
+
if (entry) {
|
|
80
|
+
if (key === "max")
|
|
81
|
+
entry.max = parseInt(val, 10) || 1;
|
|
82
|
+
else if (key === "auto_spawn")
|
|
83
|
+
entry.autoSpawn = val.trim() === "true";
|
|
84
|
+
}
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
// New type header at indent 2 — go back to allowed_types
|
|
88
|
+
const typeHeader = trimmed.trim().match(/^([\w-]+)\s*:\s*$/);
|
|
89
|
+
if (typeHeader) {
|
|
90
|
+
currentType = typeHeader[1];
|
|
91
|
+
allowedTypes.set(currentType, { max: 1, autoSpawn: false });
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// deny_types list items
|
|
96
|
+
if (section === "deny_types") {
|
|
97
|
+
const listItem = trimmed.match(/^\s+-\s+(.+)$/);
|
|
98
|
+
if (listItem) {
|
|
99
|
+
denyTypes.push(listItem[1].replace(/^["']|["']$/g, "").trim());
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { owner, governedBy, maxAgents, allowedTypes, denyTypes, requireOwnerApproval };
|
|
104
|
+
}
|
|
105
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
106
|
+
/**
|
|
107
|
+
* Load and cache the spawn policy.
|
|
108
|
+
*/
|
|
109
|
+
export async function loadSpawnPolicy(policyPath) {
|
|
110
|
+
if (cached)
|
|
111
|
+
return cached;
|
|
112
|
+
const filePath = policyPath ?? DEFAULT_POLICY_PATH;
|
|
113
|
+
try {
|
|
114
|
+
const raw = await readFile(filePath, "utf-8");
|
|
115
|
+
cached = parseSpawnPolicy(raw);
|
|
116
|
+
log.info("Spawn policy loaded", {
|
|
117
|
+
maxAgents: cached.maxAgents,
|
|
118
|
+
allowedTypes: Array.from(cached.allowedTypes.keys()),
|
|
119
|
+
denyTypes: cached.denyTypes,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
if (err.code === "ENOENT") {
|
|
124
|
+
log.warn("No spawn-policy.yaml found — using permissive defaults");
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
log.warn("Failed to parse spawn-policy.yaml", { error: err.message });
|
|
128
|
+
}
|
|
129
|
+
cached = {
|
|
130
|
+
owner: "",
|
|
131
|
+
governedBy: "",
|
|
132
|
+
maxAgents: 4,
|
|
133
|
+
allowedTypes: new Map(),
|
|
134
|
+
denyTypes: [],
|
|
135
|
+
requireOwnerApproval: false,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return cached;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get the cached spawn policy (loads synchronously if not cached).
|
|
142
|
+
*/
|
|
143
|
+
export function getSpawnPolicy() {
|
|
144
|
+
if (!cached) {
|
|
145
|
+
try {
|
|
146
|
+
if (existsSync(DEFAULT_POLICY_PATH)) {
|
|
147
|
+
const raw = readFileSync(DEFAULT_POLICY_PATH, "utf-8");
|
|
148
|
+
cached = parseSpawnPolicy(raw);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Fall through to defaults
|
|
153
|
+
}
|
|
154
|
+
if (!cached) {
|
|
155
|
+
cached = {
|
|
156
|
+
owner: "",
|
|
157
|
+
governedBy: "",
|
|
158
|
+
maxAgents: 4,
|
|
159
|
+
allowedTypes: new Map(),
|
|
160
|
+
denyTypes: [],
|
|
161
|
+
requireOwnerApproval: false,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return cached;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Check whether spawning an agent of the given type is allowed.
|
|
169
|
+
*
|
|
170
|
+
* @param agentType - The agent type to spawn (e.g. "administration", "brand")
|
|
171
|
+
* @param currentCount - Total number of currently running agents
|
|
172
|
+
* @param typeCount - Number of currently running agents of this specific type
|
|
173
|
+
*/
|
|
174
|
+
export function checkSpawnPolicy(agentType, currentCount, typeCount) {
|
|
175
|
+
const policy = getSpawnPolicy();
|
|
176
|
+
// Check deny list
|
|
177
|
+
if (policy.denyTypes.includes(agentType)) {
|
|
178
|
+
return { allowed: false, reason: `Agent type "${agentType}" is denied by spawn policy` };
|
|
179
|
+
}
|
|
180
|
+
// Check global max
|
|
181
|
+
if (currentCount >= policy.maxAgents) {
|
|
182
|
+
return { allowed: false, reason: `Max agents reached (${policy.maxAgents})` };
|
|
183
|
+
}
|
|
184
|
+
// Check type-specific max
|
|
185
|
+
const typeConfig = policy.allowedTypes.get(agentType);
|
|
186
|
+
if (typeConfig && typeCount >= typeConfig.max) {
|
|
187
|
+
return { allowed: false, reason: `Max "${agentType}" agents reached (${typeConfig.max})` };
|
|
188
|
+
}
|
|
189
|
+
// If the type isn't in allowed_types and the map is non-empty, it's implicitly denied
|
|
190
|
+
if (policy.allowedTypes.size > 0 && !typeConfig) {
|
|
191
|
+
return { allowed: false, reason: `Agent type "${agentType}" is not in allowed_types` };
|
|
192
|
+
}
|
|
193
|
+
return { allowed: true };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Force reload of spawn policy.
|
|
197
|
+
*/
|
|
198
|
+
export async function reloadSpawnPolicy(policyPath) {
|
|
199
|
+
cached = null;
|
|
200
|
+
return loadSpawnPolicy(policyPath);
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=spawn-policy.js.map
|