@lightcone-ai/daemon 0.15.59 → 0.15.60

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.15.59",
3
+ "version": "0.15.60",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -179,6 +179,48 @@ export class AgentManager {
179
179
  return `${workspaceId ?? ''}:${agentId}`;
180
180
  }
181
181
 
182
+ /**
183
+ * Snapshot of agents this daemon process is currently managing. Sent to the
184
+ * server alongside 'ready' so the server can reconcile lifecycle state
185
+ * after a reconnect — agents previously on this machine but absent from
186
+ * the inventory get reset from stale `crashed unreachable` back to a clean
187
+ * `standby reachable available` baseline (machine is healthy, agent will
188
+ * be spawned on demand).
189
+ *
190
+ * Each entry mirrors the daemon-internal agent state machine:
191
+ * - 'starting': spawn in progress (entry exists in this.starting)
192
+ * - 'running': child process is alive (this.agents has it)
193
+ * The server treats whatever is NOT in this list as 'standby' (spawnable
194
+ * but not currently running on this machine).
195
+ */
196
+ getAgentInventory() {
197
+ const inventory = [];
198
+ for (const key of this.starting) {
199
+ const colonIdx = key.indexOf(':');
200
+ const workspaceId = colonIdx === -1 ? null : (key.slice(0, colonIdx) || null);
201
+ const agentId = colonIdx === -1 ? key : key.slice(colonIdx + 1);
202
+ inventory.push({
203
+ agentId,
204
+ workspaceId,
205
+ runtimeState: 'starting',
206
+ });
207
+ }
208
+ for (const [key, agent] of this.agents.entries()) {
209
+ const colonIdx = key.indexOf(':');
210
+ const workspaceId = agent.workspaceId
211
+ ?? (colonIdx === -1 ? null : (key.slice(0, colonIdx) || null));
212
+ const agentId = agent.agentId
213
+ ?? (colonIdx === -1 ? key : key.slice(colonIdx + 1));
214
+ inventory.push({
215
+ agentId,
216
+ workspaceId,
217
+ runtimeState: 'running',
218
+ sessionId: agent.sessionId ?? null,
219
+ });
220
+ }
221
+ return inventory;
222
+ }
223
+
182
224
  handle(msg, connection) {
183
225
  switch (msg.type) {
184
226
  case 'agent:start': return this._startAgent(msg, connection);
package/src/connection.js CHANGED
@@ -45,10 +45,16 @@ function parseOptionalDeviceHints() {
45
45
  }
46
46
 
47
47
  export class DaemonConnection {
48
- constructor({ serverUrl, machineApiKey, onMessage }) {
48
+ constructor({ serverUrl, machineApiKey, onMessage, getAgentInventory = null }) {
49
49
  this.serverUrl = serverUrl.replace(/^http/, 'ws');
50
50
  this.machineApiKey = machineApiKey;
51
51
  this.onMessage = onMessage;
52
+ // Optional callback invoked at 'ready' time to snapshot which agents
53
+ // this daemon is currently managing. Server uses this to reconcile
54
+ // stale lifecycle state after reconnects (see src/daemon/index.js
55
+ // 'ready' handler — agents previously on this machine but missing
56
+ // from inventory get reset from stale crashed→standby).
57
+ this.getAgentInventory = typeof getAgentInventory === 'function' ? getAgentInventory : null;
52
58
  this.ws = null;
53
59
  this.reconnectDelay = RECONNECT_INITIAL;
54
60
  this.stopped = false;
@@ -111,7 +117,20 @@ export class DaemonConnection {
111
117
  ...parseOptionalDeviceHints(),
112
118
  };
113
119
 
114
- console.log(`[Connection] Ready host=${hostname} runtimes=[${runtimes.join(',')}] v${DAEMON_VERSION}`);
120
+ let agentInventory = [];
121
+ if (this.getAgentInventory) {
122
+ try {
123
+ const snapshot = this.getAgentInventory();
124
+ if (Array.isArray(snapshot)) agentInventory = snapshot;
125
+ } catch (err) {
126
+ console.warn(`[Connection] getAgentInventory failed: ${err?.message ?? err}`);
127
+ }
128
+ }
129
+
130
+ console.log(
131
+ `[Connection] Ready — host=${hostname} runtimes=[${runtimes.join(',')}] v${DAEMON_VERSION}`
132
+ + ` inventory=${agentInventory.length}`
133
+ );
115
134
  this.send({
116
135
  type: 'ready',
117
136
  hostname,
@@ -119,6 +138,7 @@ export class DaemonConnection {
119
138
  runtimes,
120
139
  daemonVersion: DAEMON_VERSION,
121
140
  deviceHints,
141
+ agentInventory,
122
142
  });
123
143
  }
124
144
 
package/src/index.js CHANGED
@@ -58,6 +58,7 @@ const connection = new DaemonConnection({
58
58
  serverUrl: SERVER_URL,
59
59
  machineApiKey: MACHINE_API_KEY,
60
60
  onMessage: (msg) => agentManager.handle(msg, connection),
61
+ getAgentInventory: () => agentManager.getAgentInventory(),
61
62
  });
62
63
 
63
64
  connection.connect();