@cydm/happy-elves 0.1.0-beta.283 → 0.1.0-beta.285

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.
@@ -29,7 +29,7 @@ export type TurnSummary = {
29
29
  checkpointId?: string;
30
30
  runtimeTurnId?: string;
31
31
  turnSeq?: number;
32
- status: "running" | "completed" | "failed" | "cancelled";
32
+ status: "running" | "completed" | "failed" | "cancelled" | "unknown";
33
33
  prompt: string;
34
34
  output: string;
35
35
  terminalText: string;
@@ -243,6 +243,13 @@ export function summarizeTurns(events) {
243
243
  current.checkpointId = payload.currentHead;
244
244
  current.runtimeTurnId = payload.lastTurnId;
245
245
  }
246
+ else if (payload?.type === "status" && isReconcileUnknownTag(payload.tag)) {
247
+ if (current.status === "running") {
248
+ current.status = "unknown";
249
+ current.completedAt = event.createdAt;
250
+ current.terminalText = payload.text.trim();
251
+ }
252
+ }
246
253
  else if (payload?.type === "memory_used") {
247
254
  current.usedMemories = dedupeUsedMemories([...current.usedMemories, ...payload.memories]);
248
255
  }
@@ -472,6 +479,9 @@ function turnInProgressError(sessionId, turn, latestCompleted) {
472
479
  latestCompletedTurnId: latestCompleted?.turnId,
473
480
  });
474
481
  }
482
+ function isReconcileUnknownTag(tag) {
483
+ return tag === "reconcile:unknown" || tag === "reconcile:not_found" || tag === "reconcile:uninspectable";
484
+ }
475
485
  export async function rebuildTurnOutput(client, sessionId, turnId) {
476
486
  const events = await client.history(sessionId, 1000);
477
487
  const folded = foldAliasedRuntimeEvents(events);
@@ -34,7 +34,7 @@ function orchestrationStateFromStatus(status) {
34
34
  return "idle";
35
35
  if (status === "completed")
36
36
  return "completed";
37
- if (status === "failed" || status === "offline")
37
+ if (status === "failed" || status === "offline" || status === "unknown")
38
38
  return "blocked";
39
39
  return "closed";
40
40
  }
@@ -70,6 +70,17 @@ export function deriveOrchestration(session, events = []) {
70
70
  completed: false,
71
71
  };
72
72
  }
73
+ if (latestTerminalTurn?.status === "unknown") {
74
+ return {
75
+ state: "blocked",
76
+ rawStatus: session.status,
77
+ needsUser: false,
78
+ blocked: true,
79
+ completed: false,
80
+ reason: "execution-unknown",
81
+ lastEventId: latestTerminalTurn.lastEventId,
82
+ };
83
+ }
73
84
  const state = orchestrationStateFromStatus(session.status);
74
85
  return {
75
86
  state,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/happy-elves-cli",
3
- "version": "0.1.0-beta.283",
3
+ "version": "0.1.0-beta.285",
4
4
  "private": true,
5
5
  "type": "module"
6
6
  }
@@ -4,6 +4,7 @@ import { getRuntime } from "../runtime.js";
4
4
  import { sessions, sessionTurns } from "../state.js";
5
5
  import { sendCommandResponse } from "../relay/send.js";
6
6
  import { settleScheduleTurnTerminal } from "../schedule/runner.js";
7
+ import { emitEvent } from "./events.js";
7
8
  import { commandSessionMetadata } from "./metadata.js";
8
9
  export async function handleReconcileTurn(ws, config, command) {
9
10
  if (command.type !== "machine:reconcileTurn")
@@ -89,11 +90,31 @@ export async function handleReconcileTurn(ws, config, command) {
89
90
  result,
90
91
  },
91
92
  });
93
+ if (isUnconfirmedReconcileResult(result) && result.turnId) {
94
+ await emitEvent(ws, config, command.sessionId, result.turnId, {
95
+ type: "status",
96
+ tag: `reconcile:${result.status}`,
97
+ text: reconcileStatusText(result),
98
+ });
99
+ }
92
100
  await sendReconcileResult(ws, config, command, result);
93
101
  if (result.status === "terminal" && result.terminalStatus && result.turnId) {
94
102
  await settleTerminalSideEffects(config, result);
95
103
  }
96
104
  }
105
+ function isUnconfirmedReconcileResult(result) {
106
+ return result.status === "unknown" || result.status === "not_found" || result.status === "uninspectable";
107
+ }
108
+ function reconcileStatusText(result) {
109
+ const reason = result.reason ? ` Reason: ${result.reason}` : "";
110
+ if (result.status === "not_found") {
111
+ return `Unable to confirm whether the previous execution finished; provider reported authoritative not found.${reason}`;
112
+ }
113
+ if (result.status === "uninspectable") {
114
+ return `Unable to confirm whether the previous execution finished; this runtime cannot inspect the turn.${reason}`;
115
+ }
116
+ return `Unable to confirm whether the previous execution finished.${reason}`;
117
+ }
97
118
  async function sendReconcileResult(ws, config, command, result) {
98
119
  await sendCommandResponse(ws, {
99
120
  type: "machine:turnReconciled",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/happy-elves-daemon",
3
- "version": "0.1.0-beta.283",
3
+ "version": "0.1.0-beta.285",
4
4
  "private": true,
5
5
  "type": "module"
6
6
  }
@@ -246,9 +246,9 @@ export function handleMachineMessage(context, connection, message) {
246
246
  (!message.result.turnId || message.result.turnId === claim?.turn_id);
247
247
  if (claimMatches && turnId) {
248
248
  db.prepare(`UPDATE sessions
249
- SET status = 'failed',
249
+ SET status = 'unknown',
250
250
  current_head = COALESCE(current_head, ?),
251
- last_turn_id = ?,
251
+ last_turn_id = COALESCE(last_turn_id, ?),
252
252
  updated_at = ?
253
253
  WHERE id = ? AND account_id = ? AND machine_id = ? AND status != 'closed'`).run(turnId, turnId, ts, message.result.sessionId, connection.accountId, connection.machineId);
254
254
  if (requestId) {
@@ -230,21 +230,6 @@ export function createRelayContext(options) {
230
230
  .get(claim.account_id, claim.request_id);
231
231
  if (admission && (admission.finished_at !== null || admission.terminal_status !== null))
232
232
  return {};
233
- const session = options.db
234
- .prepare(`SELECT status, current_head, last_turn_id, updated_at
235
- FROM sessions
236
- WHERE account_id = ? AND id = ?`)
237
- .get(claim.account_id, claim.session_id);
238
- if (admission?.dispatch === "delivered" &&
239
- session &&
240
- isSettledSessionProjectionStatus(session.status) &&
241
- session.updated_at > claim.created_at &&
242
- (session.last_turn_id === claim.turn_id || (session.current_head !== claim.turn_id && session.last_turn_id !== claim.turn_id))) {
243
- return {
244
- terminalStatus: terminalStatusForSettledSessionStatus(session.status),
245
- finishedAt: session.updated_at,
246
- };
247
- }
248
233
  const newerTerminal = options.db
249
234
  .prepare(`SELECT 1
250
235
  FROM relay_admissions
@@ -259,16 +244,6 @@ export function createRelayContext(options) {
259
244
  .get(claim.account_id, claim.session_id, claim.request_id, claim.created_at);
260
245
  return newerTerminal ? {} : undefined;
261
246
  }
262
- function terminalStatusForSettledSessionStatus(status) {
263
- if (status === "failed")
264
- return "failed";
265
- if (status === "cancelled" || status === "closed")
266
- return "cancelled";
267
- return "completed";
268
- }
269
- function isSettledSessionProjectionStatus(status) {
270
- return status === "ready" || status === "completed" || status === "cancelled" || status === "closed";
271
- }
272
247
  function claimSessionTurn(params) {
273
248
  const existing = activeSessionClaim(params.accountId, params.sessionId);
274
249
  if (existing && existing.request_id !== params.requestId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/happy-elves",
3
- "version": "0.1.0-beta.283",
3
+ "version": "0.1.0-beta.285",
4
4
  "description": "Remote controller for local coding agents with hosted or self-hosted relay support.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -211,6 +211,7 @@ export declare const sessionSnapshotSchema: z.ZodObject<{
211
211
  new: "new";
212
212
  ready: "ready";
213
213
  running: "running";
214
+ unknown: "unknown";
214
215
  completed: "completed";
215
216
  failed: "failed";
216
217
  cancelled: "cancelled";
@@ -457,9 +458,9 @@ export declare const daemonLifecycleResultSchema: z.ZodObject<{
457
458
  platform: z.ZodOptional<z.ZodString>;
458
459
  arch: z.ZodOptional<z.ZodString>;
459
460
  installMode: z.ZodOptional<z.ZodEnum<{
461
+ unknown: "unknown";
460
462
  "npm-global": "npm-global";
461
463
  "repo-dev": "repo-dev";
462
- unknown: "unknown";
463
464
  }>>;
464
465
  updateSupported: z.ZodOptional<z.ZodBoolean>;
465
466
  updateUnsupportedReason: z.ZodOptional<z.ZodString>;
@@ -79,7 +79,7 @@ export const sessionSnapshotSchema = z.object({
79
79
  agent: z.string().min(1),
80
80
  cwd: z.string(),
81
81
  name: z.string().min(1),
82
- status: z.enum(["new", "ready", "running", "completed", "failed", "cancelled", "offline", "closed"]),
82
+ status: z.enum(["new", "ready", "running", "unknown", "completed", "failed", "cancelled", "offline", "closed"]),
83
83
  createdAt: z.number(),
84
84
  updatedAt: z.number(),
85
85
  encryptedMetadata: encryptedEnvelopeSchema.optional(),
@@ -57,7 +57,7 @@ export type SessionSnapshot = {
57
57
  agent: string;
58
58
  cwd: string;
59
59
  name: string;
60
- status: "new" | "ready" | "running" | "completed" | "failed" | "cancelled" | "offline" | "closed";
60
+ status: "new" | "ready" | "running" | "unknown" | "completed" | "failed" | "cancelled" | "offline" | "closed";
61
61
  createdAt: number;
62
62
  updatedAt: number;
63
63
  encryptedMetadata?: EncryptedEnvelope;
@@ -1,4 +1,4 @@
1
- export declare const sessionStatuses: readonly ["new", "ready", "running", "completed", "failed", "cancelled", "offline", "closed"];
1
+ export declare const sessionStatuses: readonly ["new", "ready", "running", "unknown", "completed", "failed", "cancelled", "offline", "closed"];
2
2
  export type SessionStatus = (typeof sessionStatuses)[number];
3
3
  export type ControllerSessionCommand = "prompt" | "cancel" | "resume" | "rewind" | "fork" | "rename" | "close";
4
4
  export type SessionCommandDecision = {
@@ -2,6 +2,7 @@ export const sessionStatuses = [
2
2
  "new",
3
3
  "ready",
4
4
  "running",
5
+ "unknown",
5
6
  "completed",
6
7
  "failed",
7
8
  "cancelled",
@@ -15,7 +16,7 @@ export function isSessionClosed(status) {
15
16
  return status === "closed";
16
17
  }
17
18
  export function isSessionBusy(status) {
18
- return status === "running";
19
+ return status === "running" || status === "unknown";
19
20
  }
20
21
  export function controllerSessionCommandDecision(status, command) {
21
22
  if (command === "close" && status === "closed")
@@ -27,11 +28,11 @@ export function controllerSessionCommandDecision(status, command) {
27
28
  message: "Session is closed",
28
29
  };
29
30
  }
30
- if (command === "prompt" && status === "running") {
31
+ if (command === "prompt" && isSessionBusy(status)) {
31
32
  return {
32
33
  allowed: false,
33
34
  code: "SESSION_BUSY",
34
- message: "Session already has a running turn",
35
+ message: status === "unknown" ? "Session has a turn whose execution status is unknown" : "Session already has a running turn",
35
36
  };
36
37
  }
37
38
  return { allowed: true };
@@ -43,6 +44,8 @@ export function statusAfterMachineDisconnect(status) {
43
44
  return "failed";
44
45
  if (status === "running")
45
46
  return "offline";
47
+ if (status === "unknown")
48
+ return "unknown";
46
49
  return status;
47
50
  }
48
51
  //# sourceMappingURL=session-state.js.map