@cleocode/runtime 2026.4.0 → 2026.4.4
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/index.js +30 -25
- package/dist/index.js.map +1 -1
- package/package.json +8 -3
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { conduit } from "@cleocode/core";
|
|
3
3
|
|
|
4
|
-
// src/services/agent-poller.
|
|
4
|
+
// src/services/agent-poller.js
|
|
5
5
|
var DEFAULT_POLL_INTERVAL = 5e3;
|
|
6
6
|
var DEFAULT_GROUP_POLL_LIMIT = 15;
|
|
7
7
|
var AgentPoller = class {
|
|
@@ -19,7 +19,8 @@ var AgentPoller = class {
|
|
|
19
19
|
}
|
|
20
20
|
/** Start the polling loop. */
|
|
21
21
|
start() {
|
|
22
|
-
if (this.running)
|
|
22
|
+
if (this.running)
|
|
23
|
+
return;
|
|
23
24
|
this.running = true;
|
|
24
25
|
const intervalMs = this.config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL;
|
|
25
26
|
void this.pollCycle();
|
|
@@ -44,7 +45,8 @@ var AgentPoller = class {
|
|
|
44
45
|
}
|
|
45
46
|
/** Single poll cycle — peek + group conversations. */
|
|
46
47
|
async pollCycle() {
|
|
47
|
-
if (!this.handler)
|
|
48
|
+
if (!this.handler)
|
|
49
|
+
return;
|
|
48
50
|
const newMessages = [];
|
|
49
51
|
try {
|
|
50
52
|
const peekMessages = await this.peekMessages();
|
|
@@ -90,7 +92,8 @@ var AgentPoller = class {
|
|
|
90
92
|
method: "GET",
|
|
91
93
|
headers: this.headers()
|
|
92
94
|
});
|
|
93
|
-
if (!response.ok)
|
|
95
|
+
if (!response.ok)
|
|
96
|
+
return [];
|
|
94
97
|
const data = await response.json();
|
|
95
98
|
return (data.data?.messages ?? []).map((m) => ({
|
|
96
99
|
id: m.id,
|
|
@@ -111,7 +114,8 @@ var AgentPoller = class {
|
|
|
111
114
|
method: "GET",
|
|
112
115
|
headers: this.headers()
|
|
113
116
|
});
|
|
114
|
-
if (!response.ok)
|
|
117
|
+
if (!response.ok)
|
|
118
|
+
return [];
|
|
115
119
|
const data = await response.json();
|
|
116
120
|
const mentionPattern = new RegExp(`@${this.config.agentId}\\b|@all\\b`, "i");
|
|
117
121
|
return (data.data?.messages ?? []).filter((m) => {
|
|
@@ -135,7 +139,7 @@ var AgentPoller = class {
|
|
|
135
139
|
}
|
|
136
140
|
};
|
|
137
141
|
|
|
138
|
-
// src/services/heartbeat.
|
|
142
|
+
// src/services/heartbeat.js
|
|
139
143
|
var DEFAULT_INTERVAL_MS = 3e4;
|
|
140
144
|
var HeartbeatService = class {
|
|
141
145
|
config;
|
|
@@ -147,7 +151,8 @@ var HeartbeatService = class {
|
|
|
147
151
|
}
|
|
148
152
|
/** Start sending heartbeats at the configured interval. */
|
|
149
153
|
start() {
|
|
150
|
-
if (this.running)
|
|
154
|
+
if (this.running)
|
|
155
|
+
return;
|
|
151
156
|
this.running = true;
|
|
152
157
|
this.consecutiveFailures = 0;
|
|
153
158
|
const intervalMs = this.config.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
@@ -174,19 +179,16 @@ var HeartbeatService = class {
|
|
|
174
179
|
/** Send a single heartbeat to the cloud API. */
|
|
175
180
|
async sendHeartbeat() {
|
|
176
181
|
try {
|
|
177
|
-
const response = await fetch(
|
|
178
|
-
|
|
179
|
-
{
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
signal: AbortSignal.timeout(1e4)
|
|
188
|
-
}
|
|
189
|
-
);
|
|
182
|
+
const response = await fetch(`${this.config.apiBaseUrl}/agents/${this.config.agentId}/heartbeat`, {
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: {
|
|
185
|
+
"Content-Type": "application/json",
|
|
186
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
187
|
+
"X-Agent-Id": this.config.agentId
|
|
188
|
+
},
|
|
189
|
+
body: JSON.stringify({ status: "online" }),
|
|
190
|
+
signal: AbortSignal.timeout(1e4)
|
|
191
|
+
});
|
|
190
192
|
if (response.ok) {
|
|
191
193
|
this.consecutiveFailures = 0;
|
|
192
194
|
} else {
|
|
@@ -198,7 +200,7 @@ var HeartbeatService = class {
|
|
|
198
200
|
}
|
|
199
201
|
};
|
|
200
202
|
|
|
201
|
-
// src/services/key-rotation.
|
|
203
|
+
// src/services/key-rotation.js
|
|
202
204
|
var DEFAULT_CHECK_INTERVAL_MS = 36e5;
|
|
203
205
|
var DEFAULT_MAX_KEY_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
204
206
|
var KeyRotationService = class {
|
|
@@ -211,7 +213,8 @@ var KeyRotationService = class {
|
|
|
211
213
|
}
|
|
212
214
|
/** Start monitoring key age at the configured interval. */
|
|
213
215
|
start() {
|
|
214
|
-
if (this.running)
|
|
216
|
+
if (this.running)
|
|
217
|
+
return;
|
|
215
218
|
this.running = true;
|
|
216
219
|
const intervalMs = this.config.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;
|
|
217
220
|
setTimeout(() => {
|
|
@@ -240,7 +243,8 @@ var KeyRotationService = class {
|
|
|
240
243
|
async checkAndRotate() {
|
|
241
244
|
try {
|
|
242
245
|
const credential = await this.config.registry.get(this.config.agentId);
|
|
243
|
-
if (!credential)
|
|
246
|
+
if (!credential)
|
|
247
|
+
return;
|
|
244
248
|
const maxAge = this.config.maxKeyAgeMs ?? DEFAULT_MAX_KEY_AGE_MS;
|
|
245
249
|
const credentialAge = Date.now() - new Date(credential.updatedAt).getTime();
|
|
246
250
|
if (credentialAge > maxAge) {
|
|
@@ -252,7 +256,7 @@ var KeyRotationService = class {
|
|
|
252
256
|
}
|
|
253
257
|
};
|
|
254
258
|
|
|
255
|
-
// src/services/sse-connection.
|
|
259
|
+
// src/services/sse-connection.js
|
|
256
260
|
var SseConnectionService = class {
|
|
257
261
|
config;
|
|
258
262
|
handler = null;
|
|
@@ -267,7 +271,8 @@ var SseConnectionService = class {
|
|
|
267
271
|
}
|
|
268
272
|
/** Start the SSE connection. */
|
|
269
273
|
async start() {
|
|
270
|
-
if (this.running)
|
|
274
|
+
if (this.running)
|
|
275
|
+
return;
|
|
271
276
|
this.running = true;
|
|
272
277
|
await this.config.transport.connect({
|
|
273
278
|
agentId: this.config.agentId,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/services/agent-poller.ts","../src/services/heartbeat.ts","../src/services/key-rotation.ts","../src/services/sse-connection.ts"],"sourcesContent":["/**\n * @cleocode/runtime — Long-running process layer for CLEO.\n *\n * Provides background services: agent polling, SSE connections,\n * heartbeat intervals, and credential rotation.\n *\n * @module runtime\n */\n\nimport type { AgentRegistryAPI, Transport } from '@cleocode/contracts';\nimport { conduit } from '@cleocode/core';\n\nconst { resolveTransport } = conduit;\nimport type { AgentPollerConfig } from './services/agent-poller.js';\nimport { AgentPoller } from './services/agent-poller.js';\nimport { HeartbeatService } from './services/heartbeat.js';\nimport { KeyRotationService } from './services/key-rotation.js';\nimport { SseConnectionService } from './services/sse-connection.js';\n\nexport type { AgentPollerConfig, MessageHandler } from './services/agent-poller.js';\nexport { AgentPoller } from './services/agent-poller.js';\nexport type { HeartbeatConfig } from './services/heartbeat.js';\nexport { HeartbeatService } from './services/heartbeat.js';\nexport type { KeyRotationConfig } from './services/key-rotation.js';\nexport { KeyRotationService } from './services/key-rotation.js';\nexport type { SseConnectionConfig, SseMessageHandler } from './services/sse-connection.js';\nexport { SseConnectionService } from './services/sse-connection.js';\n\n/** Configuration for createRuntime(). */\nexport interface RuntimeConfig {\n /** Agent ID to run as. If omitted, uses the most recently used active agent. */\n agentId?: string;\n /** Poll interval in milliseconds. Default: 5000. */\n pollIntervalMs?: number;\n /** Known group conversation IDs to monitor for @mentions. */\n groupConversationIds?: string[];\n /** Max messages per group conversation poll. Default: 15. */\n groupPollLimit?: number;\n /** Heartbeat interval in milliseconds. Default: 30000. Set to 0 to disable. */\n heartbeatIntervalMs?: number;\n /** Max key age in milliseconds before rotation. Default: 30 days. Set to 0 to disable. */\n maxKeyAgeMs?: number;\n /** SSE endpoint URL. If set, enables persistent SSE connection. */\n sseEndpoint?: string;\n /** Transport factory for SSE connection. Caller provides to avoid circular deps. */\n createSseTransport?: () => import('@cleocode/contracts').Transport;\n /**\n * Pre-created transport instance. When provided, bypasses auto-resolution.\n * The transport must NOT be connected yet — createRuntime handles connection.\n */\n transport?: Transport;\n}\n\n/** Handle returned by createRuntime(). */\nexport interface RuntimeHandle {\n /** The AgentPoller instance. */\n poller: AgentPoller;\n /** The HeartbeatService instance (null if disabled). */\n heartbeat: HeartbeatService | null;\n /** The KeyRotationService instance (null if disabled). */\n keyRotation: KeyRotationService | null;\n /** The SseConnectionService instance (null if no SSE endpoint). */\n sseConnection: SseConnectionService | null;\n /** The resolved transport (local, sse, or http). */\n transport: Transport;\n /** The agent ID the runtime is running as. */\n agentId: string;\n /** Stop all runtime services and clean up. */\n stop: () => void;\n}\n\n/**\n * Create and start a runtime from the agent registry.\n *\n * Resolves the agent credential, configures the poller, and starts polling.\n * Returns a handle to register message handlers and stop the runtime.\n *\n * @param registry - AgentRegistryAPI instance for credential lookup.\n * @param config - Optional runtime configuration overrides.\n * @returns A RuntimeHandle with the poller, agentId, and stop function.\n */\nexport async function createRuntime(\n registry: AgentRegistryAPI,\n config?: RuntimeConfig,\n): Promise<RuntimeHandle> {\n const credential = config?.agentId\n ? await registry.get(config.agentId)\n : await registry.getActive();\n\n if (!credential) {\n throw new Error(\n 'No agent credential found. Run: cleo agent register --id <id> --api-key <key>',\n );\n }\n\n // Resolve transport: caller-provided > auto-detected (Local > SSE > HTTP)\n const transport = config?.transport ?? resolveTransport(credential);\n await transport.connect({\n agentId: credential.agentId,\n apiKey: credential.apiKey,\n apiBaseUrl: credential.apiBaseUrl,\n ...credential.transportConfig,\n });\n\n const pollerConfig: AgentPollerConfig = {\n agentId: credential.agentId,\n apiKey: credential.apiKey,\n apiBaseUrl: credential.apiBaseUrl,\n pollIntervalMs: config?.pollIntervalMs ?? credential.transportConfig.pollIntervalMs ?? 5000,\n groupConversationIds: config?.groupConversationIds,\n groupPollLimit: config?.groupPollLimit,\n transport,\n };\n\n const poller = new AgentPoller(pollerConfig);\n\n // Heartbeat service (disabled when intervalMs is 0)\n let heartbeat: HeartbeatService | null = null;\n if (config?.heartbeatIntervalMs !== 0) {\n heartbeat = new HeartbeatService({\n agentId: credential.agentId,\n apiKey: credential.apiKey,\n apiBaseUrl: credential.apiBaseUrl,\n intervalMs: config?.heartbeatIntervalMs,\n });\n heartbeat.start();\n }\n\n // Key rotation service (disabled when maxKeyAgeMs is 0)\n let keyRotation: KeyRotationService | null = null;\n if (config?.maxKeyAgeMs !== 0) {\n keyRotation = new KeyRotationService({\n agentId: credential.agentId,\n registry,\n maxKeyAgeMs: config?.maxKeyAgeMs,\n });\n keyRotation.start();\n }\n\n // SSE connection service (enabled when sseEndpoint + transport factory provided)\n let sseConnection: SseConnectionService | null = null;\n const sseEndpoint = config?.sseEndpoint ?? credential.transportConfig.sseEndpoint;\n if (sseEndpoint && config?.createSseTransport) {\n sseConnection = new SseConnectionService({\n agentId: credential.agentId,\n apiKey: credential.apiKey,\n apiBaseUrl: credential.apiBaseUrl,\n sseEndpoint,\n transport: config.createSseTransport(),\n });\n // Start is async but we don't block createRuntime on it\n void sseConnection.start();\n }\n\n return {\n poller,\n heartbeat,\n keyRotation,\n sseConnection,\n transport,\n agentId: credential.agentId,\n stop: () => {\n poller.stop();\n heartbeat?.stop();\n keyRotation?.stop();\n void sseConnection?.stop();\n void transport.disconnect();\n },\n };\n}\n","/**\n * AgentPoller — Polls for messages via HttpTransport AND group conversations.\n *\n * Fixes the group @mention blind spot: the peek endpoint only matches\n * to_agent_id (DMs). This poller ALSO checks known group conversation\n * messages for @agentId content matches.\n *\n * @task T183\n */\n\nimport type { ConduitMessage, Transport } from '@cleocode/contracts';\n\n/** Message handler callback. */\nexport type MessageHandler = (message: ConduitMessage) => void;\n\n/** Poller configuration. */\nexport interface AgentPollerConfig {\n /** Agent ID to poll as. */\n agentId: string;\n /** API key for authentication. */\n apiKey: string;\n /** API base URL. */\n apiBaseUrl: string;\n /** Poll interval in milliseconds. Default: 5000. */\n pollIntervalMs?: number;\n /** Known group conversation IDs to monitor for @mentions. */\n groupConversationIds?: string[];\n /** Max messages to fetch per group conversation poll. Default: 15. */\n groupPollLimit?: number;\n /**\n * Transport instance for polling messages.\n * When provided, poll() delegates to transport.poll() instead of raw HTTP.\n * The transport must already be connected before passing to AgentPoller.\n */\n transport?: Transport;\n}\n\n/** Tracks seen message IDs for dedup. */\nconst DEFAULT_POLL_INTERVAL = 5000;\nconst DEFAULT_GROUP_POLL_LIMIT = 15;\n\n/**\n * AgentPoller service — polls peek endpoint AND group conversations.\n * Deduplicates messages by ID across both sources.\n */\nexport class AgentPoller {\n private config: AgentPollerConfig;\n private handler: MessageHandler | null = null;\n private interval: ReturnType<typeof setInterval> | null = null;\n private seenMessageIds = new Set<string>();\n private running = false;\n\n constructor(config: AgentPollerConfig) {\n this.config = config;\n }\n\n /** Register a message handler. */\n onMessage(handler: MessageHandler): void {\n this.handler = handler;\n }\n\n /** Start the polling loop. */\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const intervalMs = this.config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL;\n\n // Initial poll immediately\n void this.pollCycle();\n\n this.interval = setInterval(() => {\n void this.pollCycle();\n }, intervalMs);\n }\n\n /** Stop the polling loop. */\n stop(): void {\n this.running = false;\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n }\n\n /** Get poller status. */\n status(): { running: boolean; seenCount: number } {\n return {\n running: this.running,\n seenCount: this.seenMessageIds.size,\n };\n }\n\n /** Single poll cycle — peek + group conversations. */\n private async pollCycle(): Promise<void> {\n if (!this.handler) return;\n\n const newMessages: ConduitMessage[] = [];\n\n // Track 1: Standard peek endpoint (catches DMs)\n try {\n const peekMessages = await this.peekMessages();\n for (const msg of peekMessages) {\n if (!this.seenMessageIds.has(msg.id)) {\n this.seenMessageIds.add(msg.id);\n newMessages.push(msg);\n }\n }\n } catch {\n // Best-effort — don't crash the loop\n }\n\n // Track 2: Group conversation polling (catches @mentions in group messages)\n const groupIds = this.config.groupConversationIds ?? [];\n for (const convId of groupIds) {\n try {\n const groupMessages = await this.pollGroupConversation(convId);\n for (const msg of groupMessages) {\n if (!this.seenMessageIds.has(msg.id)) {\n this.seenMessageIds.add(msg.id);\n newMessages.push(msg);\n }\n }\n } catch {\n // Best-effort per conversation\n }\n }\n\n // Deliver new messages to handler\n for (const msg of newMessages) {\n this.handler(msg);\n }\n\n // Prevent unbounded growth of seen set (keep last 5000)\n if (this.seenMessageIds.size > 5000) {\n const entries = [...this.seenMessageIds];\n this.seenMessageIds = new Set(entries.slice(-3000));\n }\n }\n\n /** Peek for messages mentioning this agent. Delegates to transport when available. */\n private async peekMessages(): Promise<ConduitMessage[]> {\n if (this.config.transport) {\n return this.config.transport.poll({ limit: 50 });\n }\n\n // Fallback: raw HTTP when no transport injected\n const params = new URLSearchParams();\n params.set('mentioned', this.config.agentId);\n params.set('limit', '50');\n\n const url = `${this.config.apiBaseUrl}/messages/peek?${params}`;\n const response = await fetch(url, {\n method: 'GET',\n headers: this.headers(),\n });\n\n if (!response.ok) return [];\n\n const data = (await response.json()) as {\n data?: {\n messages?: Array<{\n id: string;\n fromAgentId?: string;\n content?: string;\n conversationId?: string;\n createdAt?: string;\n }>;\n };\n };\n\n return (data.data?.messages ?? []).map((m) => ({\n id: m.id,\n from: m.fromAgentId ?? 'unknown',\n content: m.content ?? '',\n threadId: m.conversationId,\n timestamp: m.createdAt ?? new Date().toISOString(),\n }));\n }\n\n /**\n * Poll a group conversation for recent messages that @mention this agent.\n * This is the fix for the group @mention blind spot.\n */\n private async pollGroupConversation(conversationId: string): Promise<ConduitMessage[]> {\n const limit = this.config.groupPollLimit ?? DEFAULT_GROUP_POLL_LIMIT;\n const url = `${this.config.apiBaseUrl}/conversations/${conversationId}/messages?sort=desc&limit=${limit}`;\n\n const response = await fetch(url, {\n method: 'GET',\n headers: this.headers(),\n });\n\n if (!response.ok) return [];\n\n const data = (await response.json()) as {\n data?: {\n messages?: Array<{\n id: string;\n fromAgentId?: string;\n content?: string;\n conversationId?: string;\n createdAt?: string;\n }>;\n };\n };\n\n const mentionPattern = new RegExp(`@${this.config.agentId}\\\\b|@all\\\\b`, 'i');\n\n return (data.data?.messages ?? [])\n .filter((m) => {\n // Only deliver messages that mention us or @all\n const content = m.content ?? '';\n return mentionPattern.test(content) && m.fromAgentId !== this.config.agentId;\n })\n .map((m) => ({\n id: m.id,\n from: m.fromAgentId ?? 'unknown',\n content: m.content ?? '',\n threadId: m.conversationId ?? conversationId,\n timestamp: m.createdAt ?? new Date().toISOString(),\n }));\n }\n\n /** Build auth headers. */\n private headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n 'X-Agent-Id': this.config.agentId,\n };\n }\n}\n","/**\n * HeartbeatService — Periodic online status heartbeat.\n *\n * Sends a heartbeat to the SignalDock API at a configurable interval\n * to maintain the agent's online status. If the heartbeat fails,\n * it retries silently — the agent continues operating regardless.\n *\n * @task T218\n */\n\n/** Heartbeat service configuration. */\nexport interface HeartbeatConfig {\n /** Agent ID to send heartbeats for. */\n agentId: string;\n /** API key for authentication. */\n apiKey: string;\n /** API base URL. */\n apiBaseUrl: string;\n /** Heartbeat interval in milliseconds. Default: 30000 (30s). */\n intervalMs?: number;\n}\n\n/** Default heartbeat interval: 30 seconds. */\nconst DEFAULT_INTERVAL_MS = 30_000;\n\n/** HeartbeatService sends periodic online status to the cloud API. */\nexport class HeartbeatService {\n private config: HeartbeatConfig;\n private timer: ReturnType<typeof setInterval> | null = null;\n private running = false;\n private consecutiveFailures = 0;\n\n constructor(config: HeartbeatConfig) {\n this.config = config;\n }\n\n /** Start sending heartbeats at the configured interval. */\n start(): void {\n if (this.running) return;\n this.running = true;\n this.consecutiveFailures = 0;\n\n const intervalMs = this.config.intervalMs ?? DEFAULT_INTERVAL_MS;\n\n // Send initial heartbeat immediately\n void this.sendHeartbeat();\n\n this.timer = setInterval(() => {\n void this.sendHeartbeat();\n }, intervalMs);\n }\n\n /** Stop sending heartbeats. */\n stop(): void {\n this.running = false;\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Get heartbeat service status. */\n status(): { running: boolean; consecutiveFailures: number } {\n return {\n running: this.running,\n consecutiveFailures: this.consecutiveFailures,\n };\n }\n\n /** Send a single heartbeat to the cloud API. */\n private async sendHeartbeat(): Promise<void> {\n try {\n const response = await fetch(\n `${this.config.apiBaseUrl}/agents/${this.config.agentId}/heartbeat`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n 'X-Agent-Id': this.config.agentId,\n },\n body: JSON.stringify({ status: 'online' }),\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (response.ok) {\n this.consecutiveFailures = 0;\n } else {\n this.consecutiveFailures++;\n }\n } catch {\n this.consecutiveFailures++;\n }\n }\n}\n","/**\n * KeyRotationService — Automatic API key rotation based on credential age.\n *\n * Monitors the age of the agent's API key and triggers rotation when\n * the key exceeds the configured threshold. Uses the AgentRegistryAPI\n * to perform the actual rotation (which calls the cloud API and\n * re-encrypts the new key locally).\n *\n * @task T218\n */\n\nimport type { AgentRegistryAPI } from '@cleocode/contracts';\n\n/** Key rotation service configuration. */\nexport interface KeyRotationConfig {\n /** Agent ID to monitor. */\n agentId: string;\n /** AgentRegistryAPI instance for credential lookup and rotation. */\n registry: AgentRegistryAPI;\n /** Check interval in milliseconds. Default: 3600000 (1 hour). */\n checkIntervalMs?: number;\n /** Max key age in milliseconds before rotation. Default: 2592000000 (30 days). */\n maxKeyAgeMs?: number;\n}\n\n/** Default check interval: 1 hour. */\nconst DEFAULT_CHECK_INTERVAL_MS = 3_600_000;\n\n/** Default max key age: 30 days. */\nconst DEFAULT_MAX_KEY_AGE_MS = 30 * 24 * 60 * 60 * 1000;\n\n/** KeyRotationService monitors credential age and auto-rotates when threshold is exceeded. */\nexport class KeyRotationService {\n private config: KeyRotationConfig;\n private timer: ReturnType<typeof setInterval> | null = null;\n private running = false;\n private lastRotationAt: string | null = null;\n\n constructor(config: KeyRotationConfig) {\n this.config = config;\n }\n\n /** Start monitoring key age at the configured interval. */\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const intervalMs = this.config.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;\n\n // Initial check after a short delay (don't rotate immediately on startup)\n setTimeout(() => {\n void this.checkAndRotate();\n }, 5000);\n\n this.timer = setInterval(() => {\n void this.checkAndRotate();\n }, intervalMs);\n }\n\n /** Stop monitoring. */\n stop(): void {\n this.running = false;\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Get rotation service status. */\n status(): { running: boolean; lastRotationAt: string | null } {\n return {\n running: this.running,\n lastRotationAt: this.lastRotationAt,\n };\n }\n\n /** Check credential age and rotate if needed. */\n private async checkAndRotate(): Promise<void> {\n try {\n const credential = await this.config.registry.get(this.config.agentId);\n if (!credential) return;\n\n const maxAge = this.config.maxKeyAgeMs ?? DEFAULT_MAX_KEY_AGE_MS;\n const credentialAge = Date.now() - new Date(credential.updatedAt).getTime();\n\n if (credentialAge > maxAge) {\n await this.config.registry.rotateKey(this.config.agentId);\n this.lastRotationAt = new Date().toISOString();\n }\n } catch {\n // Rotation failure is non-fatal — will retry next interval\n }\n }\n}\n","/**\n * SseConnectionService — Persistent SSE connection manager.\n *\n * Maintains a persistent SSE connection to the SignalDock API for\n * real-time message delivery. Wraps SseTransport with lifecycle\n * management: start, stop, reconnect, and message forwarding.\n *\n * When SSE is available, messages arrive in real-time. When it falls\n * back to HTTP polling (managed by SseTransport internally), the\n * service continues operating transparently.\n *\n * @task T218\n */\n\nimport type { ConduitMessage, Transport } from '@cleocode/contracts';\n\n/** SSE connection service configuration. */\nexport interface SseConnectionConfig {\n /** Agent ID to connect as. */\n agentId: string;\n /** API key for authentication. */\n apiKey: string;\n /** API base URL. */\n apiBaseUrl: string;\n /** SSE endpoint URL. If omitted, uses apiBaseUrl + /sse. */\n sseEndpoint?: string;\n /** Transport instance to use. Injected by createRuntime. */\n transport: Transport;\n}\n\n/** Message handler callback. */\nexport type SseMessageHandler = (message: ConduitMessage) => void;\n\n/** SseConnectionService manages a persistent transport with subscribe() support. */\nexport class SseConnectionService {\n private config: SseConnectionConfig;\n private handler: SseMessageHandler | null = null;\n private unsubscribe: (() => void) | null = null;\n private running = false;\n\n constructor(config: SseConnectionConfig) {\n this.config = config;\n }\n\n /** Register a message handler for incoming messages. */\n onMessage(handler: SseMessageHandler): void {\n this.handler = handler;\n }\n\n /** Start the SSE connection. */\n async start(): Promise<void> {\n if (this.running) return;\n this.running = true;\n\n await this.config.transport.connect({\n agentId: this.config.agentId,\n apiKey: this.config.apiKey,\n apiBaseUrl: this.config.apiBaseUrl,\n sseEndpoint: this.config.sseEndpoint,\n });\n\n // Subscribe to incoming messages if transport supports it\n if (this.handler && this.config.transport.subscribe) {\n this.unsubscribe = this.config.transport.subscribe(this.handler);\n }\n }\n\n /** Stop the connection and clean up. */\n async stop(): Promise<void> {\n this.running = false;\n\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = null;\n }\n\n await this.config.transport.disconnect();\n }\n\n /** Get connection service status. */\n status(): { running: boolean; transportName: string } {\n return {\n running: this.running,\n transportName: this.config.transport.name,\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/services/agent-poller.ts","../src/services/heartbeat.ts","../src/services/key-rotation.ts","../src/services/sse-connection.ts"],"sourcesContent":["/**\n * @cleocode/runtime — Long-running process layer for CLEO.\n *\n * Provides background services: agent polling, SSE connections,\n * heartbeat intervals, and credential rotation.\n *\n * @module runtime\n */\n\nimport type { AgentRegistryAPI, Transport } from '@cleocode/contracts';\nimport { conduit } from '@cleocode/core';\n\nconst { resolveTransport } = conduit;\n\nimport type { AgentPollerConfig } from './services/agent-poller.js';\nimport { AgentPoller } from './services/agent-poller.js';\nimport { HeartbeatService } from './services/heartbeat.js';\nimport { KeyRotationService } from './services/key-rotation.js';\nimport { SseConnectionService } from './services/sse-connection.js';\n\nexport type { AgentPollerConfig, MessageHandler } from './services/agent-poller.js';\nexport { AgentPoller } from './services/agent-poller.js';\nexport type { HeartbeatConfig } from './services/heartbeat.js';\nexport { HeartbeatService } from './services/heartbeat.js';\nexport type { KeyRotationConfig } from './services/key-rotation.js';\nexport { KeyRotationService } from './services/key-rotation.js';\nexport type { SseConnectionConfig, SseMessageHandler } from './services/sse-connection.js';\nexport { SseConnectionService } from './services/sse-connection.js';\n\n/** Configuration for createRuntime(). */\nexport interface RuntimeConfig {\n /** Agent ID to run as. If omitted, uses the most recently used active agent. */\n agentId?: string;\n /** Poll interval in milliseconds. Default: 5000. */\n pollIntervalMs?: number;\n /** Known group conversation IDs to monitor for @mentions. */\n groupConversationIds?: string[];\n /** Max messages per group conversation poll. Default: 15. */\n groupPollLimit?: number;\n /** Heartbeat interval in milliseconds. Default: 30000. Set to 0 to disable. */\n heartbeatIntervalMs?: number;\n /** Max key age in milliseconds before rotation. Default: 30 days. Set to 0 to disable. */\n maxKeyAgeMs?: number;\n /** SSE endpoint URL. If set, enables persistent SSE connection. */\n sseEndpoint?: string;\n /** Transport factory for SSE connection. Caller provides to avoid circular deps. */\n createSseTransport?: () => import('@cleocode/contracts').Transport;\n /**\n * Pre-created transport instance. When provided, bypasses auto-resolution.\n * The transport must NOT be connected yet — createRuntime handles connection.\n */\n transport?: Transport;\n}\n\n/** Handle returned by createRuntime(). */\nexport interface RuntimeHandle {\n /** The AgentPoller instance. */\n poller: AgentPoller;\n /** The HeartbeatService instance (null if disabled). */\n heartbeat: HeartbeatService | null;\n /** The KeyRotationService instance (null if disabled). */\n keyRotation: KeyRotationService | null;\n /** The SseConnectionService instance (null if no SSE endpoint). */\n sseConnection: SseConnectionService | null;\n /** The resolved transport (local, sse, or http). */\n transport: Transport;\n /** The agent ID the runtime is running as. */\n agentId: string;\n /** Stop all runtime services and clean up. */\n stop: () => void;\n}\n\n/**\n * Create and start a runtime from the agent registry.\n *\n * Resolves the agent credential, configures the poller, and starts polling.\n * Returns a handle to register message handlers and stop the runtime.\n *\n * @param registry - AgentRegistryAPI instance for credential lookup.\n * @param config - Optional runtime configuration overrides.\n * @returns A RuntimeHandle with the poller, agentId, and stop function.\n */\nexport async function createRuntime(\n registry: AgentRegistryAPI,\n config?: RuntimeConfig,\n): Promise<RuntimeHandle> {\n const credential = config?.agentId\n ? await registry.get(config.agentId)\n : await registry.getActive();\n\n if (!credential) {\n throw new Error(\n 'No agent credential found. Run: cleo agent register --id <id> --api-key <key>',\n );\n }\n\n // Resolve transport: caller-provided > auto-detected (Local > SSE > HTTP)\n const transport = config?.transport ?? resolveTransport(credential);\n await transport.connect({\n agentId: credential.agentId,\n apiKey: credential.apiKey,\n apiBaseUrl: credential.apiBaseUrl,\n ...credential.transportConfig,\n });\n\n const pollerConfig: AgentPollerConfig = {\n agentId: credential.agentId,\n apiKey: credential.apiKey,\n apiBaseUrl: credential.apiBaseUrl,\n pollIntervalMs: config?.pollIntervalMs ?? credential.transportConfig.pollIntervalMs ?? 5000,\n groupConversationIds: config?.groupConversationIds,\n groupPollLimit: config?.groupPollLimit,\n transport,\n };\n\n const poller = new AgentPoller(pollerConfig);\n\n // Heartbeat service (disabled when intervalMs is 0)\n let heartbeat: HeartbeatService | null = null;\n if (config?.heartbeatIntervalMs !== 0) {\n heartbeat = new HeartbeatService({\n agentId: credential.agentId,\n apiKey: credential.apiKey,\n apiBaseUrl: credential.apiBaseUrl,\n intervalMs: config?.heartbeatIntervalMs,\n });\n heartbeat.start();\n }\n\n // Key rotation service (disabled when maxKeyAgeMs is 0)\n let keyRotation: KeyRotationService | null = null;\n if (config?.maxKeyAgeMs !== 0) {\n keyRotation = new KeyRotationService({\n agentId: credential.agentId,\n registry,\n maxKeyAgeMs: config?.maxKeyAgeMs,\n });\n keyRotation.start();\n }\n\n // SSE connection service (enabled when sseEndpoint + transport factory provided)\n let sseConnection: SseConnectionService | null = null;\n const sseEndpoint = config?.sseEndpoint ?? credential.transportConfig.sseEndpoint;\n if (sseEndpoint && config?.createSseTransport) {\n sseConnection = new SseConnectionService({\n agentId: credential.agentId,\n apiKey: credential.apiKey,\n apiBaseUrl: credential.apiBaseUrl,\n sseEndpoint,\n transport: config.createSseTransport(),\n });\n // Start is async but we don't block createRuntime on it\n void sseConnection.start();\n }\n\n return {\n poller,\n heartbeat,\n keyRotation,\n sseConnection,\n transport,\n agentId: credential.agentId,\n stop: () => {\n poller.stop();\n heartbeat?.stop();\n keyRotation?.stop();\n void sseConnection?.stop();\n void transport.disconnect();\n },\n };\n}\n","/**\n * AgentPoller — Polls for messages via HttpTransport AND group conversations.\n *\n * Fixes the group @mention blind spot: the peek endpoint only matches\n * to_agent_id (DMs). This poller ALSO checks known group conversation\n * messages for @agentId content matches.\n *\n * @task T183\n */\n\nimport type { ConduitMessage, Transport } from '@cleocode/contracts';\n\n/** Message handler callback. */\nexport type MessageHandler = (message: ConduitMessage) => void;\n\n/** Poller configuration. */\nexport interface AgentPollerConfig {\n /** Agent ID to poll as. */\n agentId: string;\n /** API key for authentication. */\n apiKey: string;\n /** API base URL. */\n apiBaseUrl: string;\n /** Poll interval in milliseconds. Default: 5000. */\n pollIntervalMs?: number;\n /** Known group conversation IDs to monitor for @mentions. */\n groupConversationIds?: string[];\n /** Max messages to fetch per group conversation poll. Default: 15. */\n groupPollLimit?: number;\n /**\n * Transport instance for polling messages.\n * When provided, poll() delegates to transport.poll() instead of raw HTTP.\n * The transport must already be connected before passing to AgentPoller.\n */\n transport?: Transport;\n}\n\n/** Tracks seen message IDs for dedup. */\nconst DEFAULT_POLL_INTERVAL = 5000;\nconst DEFAULT_GROUP_POLL_LIMIT = 15;\n\n/**\n * AgentPoller service — polls peek endpoint AND group conversations.\n * Deduplicates messages by ID across both sources.\n */\nexport class AgentPoller {\n private config: AgentPollerConfig;\n private handler: MessageHandler | null = null;\n private interval: ReturnType<typeof setInterval> | null = null;\n private seenMessageIds = new Set<string>();\n private running = false;\n\n constructor(config: AgentPollerConfig) {\n this.config = config;\n }\n\n /** Register a message handler. */\n onMessage(handler: MessageHandler): void {\n this.handler = handler;\n }\n\n /** Start the polling loop. */\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const intervalMs = this.config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL;\n\n // Initial poll immediately\n void this.pollCycle();\n\n this.interval = setInterval(() => {\n void this.pollCycle();\n }, intervalMs);\n }\n\n /** Stop the polling loop. */\n stop(): void {\n this.running = false;\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n }\n\n /** Get poller status. */\n status(): { running: boolean; seenCount: number } {\n return {\n running: this.running,\n seenCount: this.seenMessageIds.size,\n };\n }\n\n /** Single poll cycle — peek + group conversations. */\n private async pollCycle(): Promise<void> {\n if (!this.handler) return;\n\n const newMessages: ConduitMessage[] = [];\n\n // Track 1: Standard peek endpoint (catches DMs)\n try {\n const peekMessages = await this.peekMessages();\n for (const msg of peekMessages) {\n if (!this.seenMessageIds.has(msg.id)) {\n this.seenMessageIds.add(msg.id);\n newMessages.push(msg);\n }\n }\n } catch {\n // Best-effort — don't crash the loop\n }\n\n // Track 2: Group conversation polling (catches @mentions in group messages)\n const groupIds = this.config.groupConversationIds ?? [];\n for (const convId of groupIds) {\n try {\n const groupMessages = await this.pollGroupConversation(convId);\n for (const msg of groupMessages) {\n if (!this.seenMessageIds.has(msg.id)) {\n this.seenMessageIds.add(msg.id);\n newMessages.push(msg);\n }\n }\n } catch {\n // Best-effort per conversation\n }\n }\n\n // Deliver new messages to handler\n for (const msg of newMessages) {\n this.handler(msg);\n }\n\n // Prevent unbounded growth of seen set (keep last 5000)\n if (this.seenMessageIds.size > 5000) {\n const entries = [...this.seenMessageIds];\n this.seenMessageIds = new Set(entries.slice(-3000));\n }\n }\n\n /** Peek for messages mentioning this agent. Delegates to transport when available. */\n private async peekMessages(): Promise<ConduitMessage[]> {\n if (this.config.transport) {\n return this.config.transport.poll({ limit: 50 });\n }\n\n // Fallback: raw HTTP when no transport injected\n const params = new URLSearchParams();\n params.set('mentioned', this.config.agentId);\n params.set('limit', '50');\n\n const url = `${this.config.apiBaseUrl}/messages/peek?${params}`;\n const response = await fetch(url, {\n method: 'GET',\n headers: this.headers(),\n });\n\n if (!response.ok) return [];\n\n const data = (await response.json()) as {\n data?: {\n messages?: Array<{\n id: string;\n fromAgentId?: string;\n content?: string;\n conversationId?: string;\n createdAt?: string;\n }>;\n };\n };\n\n return (data.data?.messages ?? []).map((m) => ({\n id: m.id,\n from: m.fromAgentId ?? 'unknown',\n content: m.content ?? '',\n threadId: m.conversationId,\n timestamp: m.createdAt ?? new Date().toISOString(),\n }));\n }\n\n /**\n * Poll a group conversation for recent messages that @mention this agent.\n * This is the fix for the group @mention blind spot.\n */\n private async pollGroupConversation(conversationId: string): Promise<ConduitMessage[]> {\n const limit = this.config.groupPollLimit ?? DEFAULT_GROUP_POLL_LIMIT;\n const url = `${this.config.apiBaseUrl}/conversations/${conversationId}/messages?sort=desc&limit=${limit}`;\n\n const response = await fetch(url, {\n method: 'GET',\n headers: this.headers(),\n });\n\n if (!response.ok) return [];\n\n const data = (await response.json()) as {\n data?: {\n messages?: Array<{\n id: string;\n fromAgentId?: string;\n content?: string;\n conversationId?: string;\n createdAt?: string;\n }>;\n };\n };\n\n const mentionPattern = new RegExp(`@${this.config.agentId}\\\\b|@all\\\\b`, 'i');\n\n return (data.data?.messages ?? [])\n .filter((m) => {\n // Only deliver messages that mention us or @all\n const content = m.content ?? '';\n return mentionPattern.test(content) && m.fromAgentId !== this.config.agentId;\n })\n .map((m) => ({\n id: m.id,\n from: m.fromAgentId ?? 'unknown',\n content: m.content ?? '',\n threadId: m.conversationId ?? conversationId,\n timestamp: m.createdAt ?? new Date().toISOString(),\n }));\n }\n\n /** Build auth headers. */\n private headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n 'X-Agent-Id': this.config.agentId,\n };\n }\n}\n","/**\n * HeartbeatService — Periodic online status heartbeat.\n *\n * Sends a heartbeat to the SignalDock API at a configurable interval\n * to maintain the agent's online status. If the heartbeat fails,\n * it retries silently — the agent continues operating regardless.\n *\n * @task T218\n */\n\n/** Heartbeat service configuration. */\nexport interface HeartbeatConfig {\n /** Agent ID to send heartbeats for. */\n agentId: string;\n /** API key for authentication. */\n apiKey: string;\n /** API base URL. */\n apiBaseUrl: string;\n /** Heartbeat interval in milliseconds. Default: 30000 (30s). */\n intervalMs?: number;\n}\n\n/** Default heartbeat interval: 30 seconds. */\nconst DEFAULT_INTERVAL_MS = 30_000;\n\n/** HeartbeatService sends periodic online status to the cloud API. */\nexport class HeartbeatService {\n private config: HeartbeatConfig;\n private timer: ReturnType<typeof setInterval> | null = null;\n private running = false;\n private consecutiveFailures = 0;\n\n constructor(config: HeartbeatConfig) {\n this.config = config;\n }\n\n /** Start sending heartbeats at the configured interval. */\n start(): void {\n if (this.running) return;\n this.running = true;\n this.consecutiveFailures = 0;\n\n const intervalMs = this.config.intervalMs ?? DEFAULT_INTERVAL_MS;\n\n // Send initial heartbeat immediately\n void this.sendHeartbeat();\n\n this.timer = setInterval(() => {\n void this.sendHeartbeat();\n }, intervalMs);\n }\n\n /** Stop sending heartbeats. */\n stop(): void {\n this.running = false;\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Get heartbeat service status. */\n status(): { running: boolean; consecutiveFailures: number } {\n return {\n running: this.running,\n consecutiveFailures: this.consecutiveFailures,\n };\n }\n\n /** Send a single heartbeat to the cloud API. */\n private async sendHeartbeat(): Promise<void> {\n try {\n const response = await fetch(\n `${this.config.apiBaseUrl}/agents/${this.config.agentId}/heartbeat`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n 'X-Agent-Id': this.config.agentId,\n },\n body: JSON.stringify({ status: 'online' }),\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (response.ok) {\n this.consecutiveFailures = 0;\n } else {\n this.consecutiveFailures++;\n }\n } catch {\n this.consecutiveFailures++;\n }\n }\n}\n","/**\n * KeyRotationService — Automatic API key rotation based on credential age.\n *\n * Monitors the age of the agent's API key and triggers rotation when\n * the key exceeds the configured threshold. Uses the AgentRegistryAPI\n * to perform the actual rotation (which calls the cloud API and\n * re-encrypts the new key locally).\n *\n * @task T218\n */\n\nimport type { AgentRegistryAPI } from '@cleocode/contracts';\n\n/** Key rotation service configuration. */\nexport interface KeyRotationConfig {\n /** Agent ID to monitor. */\n agentId: string;\n /** AgentRegistryAPI instance for credential lookup and rotation. */\n registry: AgentRegistryAPI;\n /** Check interval in milliseconds. Default: 3600000 (1 hour). */\n checkIntervalMs?: number;\n /** Max key age in milliseconds before rotation. Default: 2592000000 (30 days). */\n maxKeyAgeMs?: number;\n}\n\n/** Default check interval: 1 hour. */\nconst DEFAULT_CHECK_INTERVAL_MS = 3_600_000;\n\n/** Default max key age: 30 days. */\nconst DEFAULT_MAX_KEY_AGE_MS = 30 * 24 * 60 * 60 * 1000;\n\n/** KeyRotationService monitors credential age and auto-rotates when threshold is exceeded. */\nexport class KeyRotationService {\n private config: KeyRotationConfig;\n private timer: ReturnType<typeof setInterval> | null = null;\n private running = false;\n private lastRotationAt: string | null = null;\n\n constructor(config: KeyRotationConfig) {\n this.config = config;\n }\n\n /** Start monitoring key age at the configured interval. */\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const intervalMs = this.config.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;\n\n // Initial check after a short delay (don't rotate immediately on startup)\n setTimeout(() => {\n void this.checkAndRotate();\n }, 5000);\n\n this.timer = setInterval(() => {\n void this.checkAndRotate();\n }, intervalMs);\n }\n\n /** Stop monitoring. */\n stop(): void {\n this.running = false;\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Get rotation service status. */\n status(): { running: boolean; lastRotationAt: string | null } {\n return {\n running: this.running,\n lastRotationAt: this.lastRotationAt,\n };\n }\n\n /** Check credential age and rotate if needed. */\n private async checkAndRotate(): Promise<void> {\n try {\n const credential = await this.config.registry.get(this.config.agentId);\n if (!credential) return;\n\n const maxAge = this.config.maxKeyAgeMs ?? DEFAULT_MAX_KEY_AGE_MS;\n const credentialAge = Date.now() - new Date(credential.updatedAt).getTime();\n\n if (credentialAge > maxAge) {\n await this.config.registry.rotateKey(this.config.agentId);\n this.lastRotationAt = new Date().toISOString();\n }\n } catch {\n // Rotation failure is non-fatal — will retry next interval\n }\n }\n}\n","/**\n * SseConnectionService — Persistent SSE connection manager.\n *\n * Maintains a persistent SSE connection to the SignalDock API for\n * real-time message delivery. Wraps SseTransport with lifecycle\n * management: start, stop, reconnect, and message forwarding.\n *\n * When SSE is available, messages arrive in real-time. When it falls\n * back to HTTP polling (managed by SseTransport internally), the\n * service continues operating transparently.\n *\n * @task T218\n */\n\nimport type { ConduitMessage, Transport } from '@cleocode/contracts';\n\n/** SSE connection service configuration. */\nexport interface SseConnectionConfig {\n /** Agent ID to connect as. */\n agentId: string;\n /** API key for authentication. */\n apiKey: string;\n /** API base URL. */\n apiBaseUrl: string;\n /** SSE endpoint URL. If omitted, uses apiBaseUrl + /sse. */\n sseEndpoint?: string;\n /** Transport instance to use. Injected by createRuntime. */\n transport: Transport;\n}\n\n/** Message handler callback. */\nexport type SseMessageHandler = (message: ConduitMessage) => void;\n\n/** SseConnectionService manages a persistent transport with subscribe() support. */\nexport class SseConnectionService {\n private config: SseConnectionConfig;\n private handler: SseMessageHandler | null = null;\n private unsubscribe: (() => void) | null = null;\n private running = false;\n\n constructor(config: SseConnectionConfig) {\n this.config = config;\n }\n\n /** Register a message handler for incoming messages. */\n onMessage(handler: SseMessageHandler): void {\n this.handler = handler;\n }\n\n /** Start the SSE connection. */\n async start(): Promise<void> {\n if (this.running) return;\n this.running = true;\n\n await this.config.transport.connect({\n agentId: this.config.agentId,\n apiKey: this.config.apiKey,\n apiBaseUrl: this.config.apiBaseUrl,\n sseEndpoint: this.config.sseEndpoint,\n });\n\n // Subscribe to incoming messages if transport supports it\n if (this.handler && this.config.transport.subscribe) {\n this.unsubscribe = this.config.transport.subscribe(this.handler);\n }\n }\n\n /** Stop the connection and clean up. */\n async stop(): Promise<void> {\n this.running = false;\n\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = null;\n }\n\n await this.config.transport.disconnect();\n }\n\n /** Get connection service status. */\n status(): { running: boolean; transportName: string } {\n return {\n running: this.running,\n transportName: this.config.transport.name,\n };\n }\n}\n"],"mappings":";AAUA,SAAS,eAAe;;;AC4BxB,IAAM,wBAAwB;AAC9B,IAAM,2BAA2B;AAM3B,IAAO,cAAP,MAAkB;EACd;EACA,UAAiC;EACjC,WAAkD;EAClD,iBAAiB,oBAAI,IAAG;EACxB,UAAU;EAElB,YAAY,QAAyB;AACnC,SAAK,SAAS;EAChB;;EAGA,UAAU,SAAuB;AAC/B,SAAK,UAAU;EACjB;;EAGA,QAAK;AACH,QAAI,KAAK;AAAS;AAClB,SAAK,UAAU;AAEf,UAAM,aAAa,KAAK,OAAO,kBAAkB;AAGjD,SAAK,KAAK,UAAS;AAEnB,SAAK,WAAW,YAAY,MAAK;AAC/B,WAAK,KAAK,UAAS;IACrB,GAAG,UAAU;EACf;;EAGA,OAAI;AACF,SAAK,UAAU;AACf,QAAI,KAAK,UAAU;AACjB,oBAAc,KAAK,QAAQ;AAC3B,WAAK,WAAW;IAClB;EACF;;EAGA,SAAM;AACJ,WAAO;MACL,SAAS,KAAK;MACd,WAAW,KAAK,eAAe;;EAEnC;;EAGQ,MAAM,YAAS;AACrB,QAAI,CAAC,KAAK;AAAS;AAEnB,UAAM,cAAgC,CAAA;AAGtC,QAAI;AACF,YAAM,eAAe,MAAM,KAAK,aAAY;AAC5C,iBAAW,OAAO,cAAc;AAC9B,YAAI,CAAC,KAAK,eAAe,IAAI,IAAI,EAAE,GAAG;AACpC,eAAK,eAAe,IAAI,IAAI,EAAE;AAC9B,sBAAY,KAAK,GAAG;QACtB;MACF;IACF,QAAQ;IAER;AAGA,UAAM,WAAW,KAAK,OAAO,wBAAwB,CAAA;AACrD,eAAW,UAAU,UAAU;AAC7B,UAAI;AACF,cAAM,gBAAgB,MAAM,KAAK,sBAAsB,MAAM;AAC7D,mBAAW,OAAO,eAAe;AAC/B,cAAI,CAAC,KAAK,eAAe,IAAI,IAAI,EAAE,GAAG;AACpC,iBAAK,eAAe,IAAI,IAAI,EAAE;AAC9B,wBAAY,KAAK,GAAG;UACtB;QACF;MACF,QAAQ;MAER;IACF;AAGA,eAAW,OAAO,aAAa;AAC7B,WAAK,QAAQ,GAAG;IAClB;AAGA,QAAI,KAAK,eAAe,OAAO,KAAM;AACnC,YAAM,UAAU,CAAC,GAAG,KAAK,cAAc;AACvC,WAAK,iBAAiB,IAAI,IAAI,QAAQ,MAAM,IAAK,CAAC;IACpD;EACF;;EAGQ,MAAM,eAAY;AACxB,QAAI,KAAK,OAAO,WAAW;AACzB,aAAO,KAAK,OAAO,UAAU,KAAK,EAAE,OAAO,GAAE,CAAE;IACjD;AAGA,UAAM,SAAS,IAAI,gBAAe;AAClC,WAAO,IAAI,aAAa,KAAK,OAAO,OAAO;AAC3C,WAAO,IAAI,SAAS,IAAI;AAExB,UAAM,MAAM,GAAG,KAAK,OAAO,UAAU,kBAAkB,MAAM;AAC7D,UAAM,WAAW,MAAM,MAAM,KAAK;MAChC,QAAQ;MACR,SAAS,KAAK,QAAO;KACtB;AAED,QAAI,CAAC,SAAS;AAAI,aAAO,CAAA;AAEzB,UAAM,OAAQ,MAAM,SAAS,KAAI;AAYjC,YAAQ,KAAK,MAAM,YAAY,CAAA,GAAI,IAAI,CAAC,OAAO;MAC7C,IAAI,EAAE;MACN,MAAM,EAAE,eAAe;MACvB,SAAS,EAAE,WAAW;MACtB,UAAU,EAAE;MACZ,WAAW,EAAE,cAAa,oBAAI,KAAI,GAAG,YAAW;MAChD;EACJ;;;;;EAMQ,MAAM,sBAAsB,gBAAsB;AACxD,UAAM,QAAQ,KAAK,OAAO,kBAAkB;AAC5C,UAAM,MAAM,GAAG,KAAK,OAAO,UAAU,kBAAkB,cAAc,6BAA6B,KAAK;AAEvG,UAAM,WAAW,MAAM,MAAM,KAAK;MAChC,QAAQ;MACR,SAAS,KAAK,QAAO;KACtB;AAED,QAAI,CAAC,SAAS;AAAI,aAAO,CAAA;AAEzB,UAAM,OAAQ,MAAM,SAAS,KAAI;AAYjC,UAAM,iBAAiB,IAAI,OAAO,IAAI,KAAK,OAAO,OAAO,eAAe,GAAG;AAE3E,YAAQ,KAAK,MAAM,YAAY,CAAA,GAC5B,OAAO,CAAC,MAAK;AAEZ,YAAM,UAAU,EAAE,WAAW;AAC7B,aAAO,eAAe,KAAK,OAAO,KAAK,EAAE,gBAAgB,KAAK,OAAO;IACvE,CAAC,EACA,IAAI,CAAC,OAAO;MACX,IAAI,EAAE;MACN,MAAM,EAAE,eAAe;MACvB,SAAS,EAAE,WAAW;MACtB,UAAU,EAAE,kBAAkB;MAC9B,WAAW,EAAE,cAAa,oBAAI,KAAI,GAAG,YAAW;MAChD;EACN;;EAGQ,UAAO;AACb,WAAO;MACL,gBAAgB;MAChB,eAAe,UAAU,KAAK,OAAO,MAAM;MAC3C,cAAc,KAAK,OAAO;;EAE9B;;;;AChNF,IAAM,sBAAsB;AAGtB,IAAO,mBAAP,MAAuB;EACnB;EACA,QAA+C;EAC/C,UAAU;EACV,sBAAsB;EAE9B,YAAY,QAAuB;AACjC,SAAK,SAAS;EAChB;;EAGA,QAAK;AACH,QAAI,KAAK;AAAS;AAClB,SAAK,UAAU;AACf,SAAK,sBAAsB;AAE3B,UAAM,aAAa,KAAK,OAAO,cAAc;AAG7C,SAAK,KAAK,cAAa;AAEvB,SAAK,QAAQ,YAAY,MAAK;AAC5B,WAAK,KAAK,cAAa;IACzB,GAAG,UAAU;EACf;;EAGA,OAAI;AACF,SAAK,UAAU;AACf,QAAI,KAAK,OAAO;AACd,oBAAc,KAAK,KAAK;AACxB,WAAK,QAAQ;IACf;EACF;;EAGA,SAAM;AACJ,WAAO;MACL,SAAS,KAAK;MACd,qBAAqB,KAAK;;EAE9B;;EAGQ,MAAM,gBAAa;AACzB,QAAI;AACF,YAAM,WAAW,MAAM,MACrB,GAAG,KAAK,OAAO,UAAU,WAAW,KAAK,OAAO,OAAO,cACvD;QACE,QAAQ;QACR,SAAS;UACP,gBAAgB;UAChB,eAAe,UAAU,KAAK,OAAO,MAAM;UAC3C,cAAc,KAAK,OAAO;;QAE5B,MAAM,KAAK,UAAU,EAAE,QAAQ,SAAQ,CAAE;QACzC,QAAQ,YAAY,QAAQ,GAAM;OACnC;AAGH,UAAI,SAAS,IAAI;AACf,aAAK,sBAAsB;MAC7B,OAAO;AACL,aAAK;MACP;IACF,QAAQ;AACN,WAAK;IACP;EACF;;;;ACpEF,IAAM,4BAA4B;AAGlC,IAAM,yBAAyB,KAAK,KAAK,KAAK,KAAK;AAG7C,IAAO,qBAAP,MAAyB;EACrB;EACA,QAA+C;EAC/C,UAAU;EACV,iBAAgC;EAExC,YAAY,QAAyB;AACnC,SAAK,SAAS;EAChB;;EAGA,QAAK;AACH,QAAI,KAAK;AAAS;AAClB,SAAK,UAAU;AAEf,UAAM,aAAa,KAAK,OAAO,mBAAmB;AAGlD,eAAW,MAAK;AACd,WAAK,KAAK,eAAc;IAC1B,GAAG,GAAI;AAEP,SAAK,QAAQ,YAAY,MAAK;AAC5B,WAAK,KAAK,eAAc;IAC1B,GAAG,UAAU;EACf;;EAGA,OAAI;AACF,SAAK,UAAU;AACf,QAAI,KAAK,OAAO;AACd,oBAAc,KAAK,KAAK;AACxB,WAAK,QAAQ;IACf;EACF;;EAGA,SAAM;AACJ,WAAO;MACL,SAAS,KAAK;MACd,gBAAgB,KAAK;;EAEzB;;EAGQ,MAAM,iBAAc;AAC1B,QAAI;AACF,YAAM,aAAa,MAAM,KAAK,OAAO,SAAS,IAAI,KAAK,OAAO,OAAO;AACrE,UAAI,CAAC;AAAY;AAEjB,YAAM,SAAS,KAAK,OAAO,eAAe;AAC1C,YAAM,gBAAgB,KAAK,IAAG,IAAK,IAAI,KAAK,WAAW,SAAS,EAAE,QAAO;AAEzE,UAAI,gBAAgB,QAAQ;AAC1B,cAAM,KAAK,OAAO,SAAS,UAAU,KAAK,OAAO,OAAO;AACxD,aAAK,kBAAiB,oBAAI,KAAI,GAAG,YAAW;MAC9C;IACF,QAAQ;IAER;EACF;;;;AC1DI,IAAO,uBAAP,MAA2B;EACvB;EACA,UAAoC;EACpC,cAAmC;EACnC,UAAU;EAElB,YAAY,QAA2B;AACrC,SAAK,SAAS;EAChB;;EAGA,UAAU,SAA0B;AAClC,SAAK,UAAU;EACjB;;EAGA,MAAM,QAAK;AACT,QAAI,KAAK;AAAS;AAClB,SAAK,UAAU;AAEf,UAAM,KAAK,OAAO,UAAU,QAAQ;MAClC,SAAS,KAAK,OAAO;MACrB,QAAQ,KAAK,OAAO;MACpB,YAAY,KAAK,OAAO;MACxB,aAAa,KAAK,OAAO;KAC1B;AAGD,QAAI,KAAK,WAAW,KAAK,OAAO,UAAU,WAAW;AACnD,WAAK,cAAc,KAAK,OAAO,UAAU,UAAU,KAAK,OAAO;IACjE;EACF;;EAGA,MAAM,OAAI;AACR,SAAK,UAAU;AAEf,QAAI,KAAK,aAAa;AACpB,WAAK,YAAW;AAChB,WAAK,cAAc;IACrB;AAEA,UAAM,KAAK,OAAO,UAAU,WAAU;EACxC;;EAGA,SAAM;AACJ,WAAO;MACL,SAAS,KAAK;MACd,eAAe,KAAK,OAAO,UAAU;;EAEzC;;;;AJzEF,IAAM,EAAE,iBAAiB,IAAI;AAsE7B,eAAsB,cACpB,UACA,QACwB;AACxB,QAAM,aAAa,QAAQ,UACvB,MAAM,SAAS,IAAI,OAAO,OAAO,IACjC,MAAM,SAAS,UAAU;AAE7B,MAAI,CAAC,YAAY;AACf,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAGA,QAAM,YAAY,QAAQ,aAAa,iBAAiB,UAAU;AAClE,QAAM,UAAU,QAAQ;AAAA,IACtB,SAAS,WAAW;AAAA,IACpB,QAAQ,WAAW;AAAA,IACnB,YAAY,WAAW;AAAA,IACvB,GAAG,WAAW;AAAA,EAChB,CAAC;AAED,QAAM,eAAkC;AAAA,IACtC,SAAS,WAAW;AAAA,IACpB,QAAQ,WAAW;AAAA,IACnB,YAAY,WAAW;AAAA,IACvB,gBAAgB,QAAQ,kBAAkB,WAAW,gBAAgB,kBAAkB;AAAA,IACvF,sBAAsB,QAAQ;AAAA,IAC9B,gBAAgB,QAAQ;AAAA,IACxB;AAAA,EACF;AAEA,QAAM,SAAS,IAAI,YAAY,YAAY;AAG3C,MAAI,YAAqC;AACzC,MAAI,QAAQ,wBAAwB,GAAG;AACrC,gBAAY,IAAI,iBAAiB;AAAA,MAC/B,SAAS,WAAW;AAAA,MACpB,QAAQ,WAAW;AAAA,MACnB,YAAY,WAAW;AAAA,MACvB,YAAY,QAAQ;AAAA,IACtB,CAAC;AACD,cAAU,MAAM;AAAA,EAClB;AAGA,MAAI,cAAyC;AAC7C,MAAI,QAAQ,gBAAgB,GAAG;AAC7B,kBAAc,IAAI,mBAAmB;AAAA,MACnC,SAAS,WAAW;AAAA,MACpB;AAAA,MACA,aAAa,QAAQ;AAAA,IACvB,CAAC;AACD,gBAAY,MAAM;AAAA,EACpB;AAGA,MAAI,gBAA6C;AACjD,QAAM,cAAc,QAAQ,eAAe,WAAW,gBAAgB;AACtE,MAAI,eAAe,QAAQ,oBAAoB;AAC7C,oBAAgB,IAAI,qBAAqB;AAAA,MACvC,SAAS,WAAW;AAAA,MACpB,QAAQ,WAAW;AAAA,MACnB,YAAY,WAAW;AAAA,MACvB;AAAA,MACA,WAAW,OAAO,mBAAmB;AAAA,IACvC,CAAC;AAED,SAAK,cAAc,MAAM;AAAA,EAC3B;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,WAAW;AAAA,IACpB,MAAM,MAAM;AACV,aAAO,KAAK;AACZ,iBAAW,KAAK;AAChB,mBAAa,KAAK;AAClB,WAAK,eAAe,KAAK;AACzB,WAAK,UAAU,WAAW;AAAA,IAC5B;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/runtime",
|
|
3
|
-
"version": "2026.4.
|
|
3
|
+
"version": "2026.4.4",
|
|
4
4
|
"description": "Long-running process layer for CLEO — agent polling, SSE connections, heartbeat, key rotation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@cleocode/
|
|
16
|
-
"@cleocode/
|
|
15
|
+
"@cleocode/core": "2026.4.4",
|
|
16
|
+
"@cleocode/contracts": "2026.4.4"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"tsup": "^8.0.0",
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
"files": [
|
|
23
23
|
"dist"
|
|
24
24
|
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/kryptobaseddev/cleo.git",
|
|
28
|
+
"directory": "packages/runtime"
|
|
29
|
+
},
|
|
25
30
|
"publishConfig": {
|
|
26
31
|
"access": "public"
|
|
27
32
|
},
|