@episoda/cli 0.2.182 → 0.2.184

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.
@@ -3046,7 +3046,7 @@ var require_package = __commonJS({
3046
3046
  "package.json"(exports2, module2) {
3047
3047
  module2.exports = {
3048
3048
  name: "@episoda/cli",
3049
- version: "0.2.182",
3049
+ version: "0.2.184",
3050
3050
  description: "CLI tool for Episoda local development workflow orchestration",
3051
3051
  main: "dist/index.js",
3052
3052
  types: "dist/index.d.ts",
@@ -15311,10 +15311,7 @@ var Daemon = class _Daemon {
15311
15311
  });
15312
15312
  }
15313
15313
  static {
15314
- this.AGENT_HEARTBEAT_INTERVAL_MS = 15e3;
15315
- }
15316
- static {
15317
- this.AGENT_HEARTBEAT_REQUEST_TIMEOUT_MS = 12e3;
15314
+ this.AGENT_HEARTBEAT_INTERVAL_MS = 6e4;
15318
15315
  }
15319
15316
  static {
15320
15317
  this.HEALTH_PORT = 9999;
@@ -15341,59 +15338,58 @@ var Daemon = class _Daemon {
15341
15338
  loop.stop();
15342
15339
  this.agentHeartbeatLoops.delete(sessionId);
15343
15340
  }
15344
- async handleServerRequestedAgentStop(sessionId) {
15341
+ async shouldContinueAgentSession(sessionId) {
15342
+ try {
15343
+ const config = await (0, import_core22.loadConfig)();
15344
+ const apiUrl = config?.api_url || "https://episoda.dev";
15345
+ const response = await fetchWithAuth(`${apiUrl}/api/agent-sessions/${encodeURIComponent(sessionId)}/heartbeat`, {
15346
+ method: "POST"
15347
+ });
15348
+ if (!response.ok) {
15349
+ console.warn(`[Daemon] EP1429: Heartbeat API returned ${response.status} for session ${sessionId}; continuing session`);
15350
+ return true;
15351
+ }
15352
+ const payload = await response.json().catch(() => null);
15353
+ return payload?.continue !== false;
15354
+ } catch (error) {
15355
+ console.warn(`[Daemon] EP1429: Heartbeat API check failed for session ${sessionId}; continuing session`, error);
15356
+ return true;
15357
+ }
15358
+ }
15359
+ async stopAgentSessionFromServerRequest(sessionId) {
15345
15360
  try {
15346
15361
  const agentManager = getAgentControlPlane();
15362
+ await agentManager.initialize();
15347
15363
  await agentManager.stopSession(sessionId);
15348
- console.log(`[Daemon] EP1430: Stopped session ${sessionId} due to server continue=false`);
15364
+ this.agentEventSeq.delete(sessionId);
15365
+ console.log(`[Daemon] EP1429: Stopped agent session ${sessionId} on server heartbeat continue=false`);
15349
15366
  } catch (error) {
15350
- console.warn(
15351
- `[Daemon] EP1430: Failed to stop session ${sessionId} after server continue=false:`,
15352
- error
15353
- );
15367
+ console.warn(`[Daemon] EP1429: Failed to stop session ${sessionId} after continue=false`, error);
15354
15368
  }
15355
15369
  }
15356
- startAgentHeartbeatLoop(sessionId) {
15370
+ startAgentHeartbeatLoop(sessionId, client) {
15357
15371
  if (this.agentHeartbeatLoops.has(sessionId)) return;
15358
15372
  let stopped = false;
15359
- let inFlight = false;
15360
15373
  let timer = null;
15361
15374
  const runHeartbeat = async () => {
15362
- if (stopped || inFlight) return;
15363
- inFlight = true;
15364
- const abortController = new AbortController();
15365
- const requestTimeout = setTimeout(() => {
15366
- abortController.abort();
15367
- }, _Daemon.AGENT_HEARTBEAT_REQUEST_TIMEOUT_MS);
15375
+ if (stopped) return;
15368
15376
  try {
15369
- const config = await (0, import_core22.loadConfig)();
15370
- if (!config?.access_token) return;
15371
- const apiUrl = config.api_url || "https://episoda.dev";
15372
- const response = await fetchWithAuth(
15373
- `${apiUrl}/api/agent-sessions/${sessionId}/heartbeat`,
15374
- {
15375
- method: "POST",
15376
- signal: abortController.signal
15377
- }
15378
- );
15379
- if (!response.ok && response.status !== 409) {
15380
- return;
15381
- }
15382
- const payload = await response.json().catch(() => null);
15383
- const shouldContinue = response.status === 409 ? false : Boolean(payload?.data?.continue ?? payload?.continue ?? true);
15377
+ await client.send({
15378
+ type: "agent_heartbeat",
15379
+ sessionId,
15380
+ pid: process.pid,
15381
+ status: "running"
15382
+ });
15383
+ const shouldContinue = await this.shouldContinueAgentSession(sessionId);
15384
15384
  if (!shouldContinue) {
15385
- await this.handleServerRequestedAgentStop(sessionId);
15386
15385
  this.stopAgentHeartbeatLoop(sessionId);
15386
+ await this.stopAgentSessionFromServerRequest(sessionId);
15387
15387
  }
15388
15388
  } catch (error) {
15389
- if (error instanceof Error && error.name === "AbortError") {
15390
- console.warn(
15391
- `[Daemon] EP1430: Heartbeat request timed out for session ${sessionId} after ${_Daemon.AGENT_HEARTBEAT_REQUEST_TIMEOUT_MS}ms`
15392
- );
15393
- }
15394
- } finally {
15395
- clearTimeout(requestTimeout);
15396
- inFlight = false;
15389
+ console.warn(
15390
+ `[Daemon] EP1429: Failed to send agent_heartbeat for session ${sessionId}:`,
15391
+ error
15392
+ );
15397
15393
  }
15398
15394
  };
15399
15395
  timer = setInterval(() => {
@@ -15848,7 +15844,7 @@ var Daemon = class _Daemon {
15848
15844
  await agentManager.initialize();
15849
15845
  let result;
15850
15846
  if (cmd.action === "start") {
15851
- this.startAgentHeartbeatLoop(cmd.sessionId);
15847
+ this.startAgentHeartbeatLoop(cmd.sessionId, client);
15852
15848
  const callbacks = createStreamingCallbacks(cmd.sessionId, message.id);
15853
15849
  const agentWorkingDir = await resolveAgentWorkingDirectory({
15854
15850
  command: cmd,
@@ -15889,7 +15885,7 @@ var Daemon = class _Daemon {
15889
15885
  error: startResult.error
15890
15886
  };
15891
15887
  } else if (cmd.action === "message") {
15892
- this.startAgentHeartbeatLoop(cmd.sessionId);
15888
+ this.startAgentHeartbeatLoop(cmd.sessionId, client);
15893
15889
  const callbacks = createStreamingCallbacks(cmd.sessionId, message.id);
15894
15890
  const sendResult = await agentManager.sendMessage({
15895
15891
  sessionId: cmd.sessionId,