@cydm/happy-elves 0.1.0-beta.47 → 0.1.0-beta.49

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.
Files changed (55) hide show
  1. package/apps/cli/dist/commands/app.js +5 -1
  2. package/apps/cli/dist/commands/config.js +2 -2
  3. package/apps/cli/dist/commands/lib/args.js +7 -0
  4. package/apps/cli/dist/commands/lib/config.d.ts +1 -0
  5. package/apps/cli/dist/commands/lib/config.js +15 -2
  6. package/apps/cli/dist/commands/lib/doctor.js +3 -3
  7. package/apps/cli/dist/commands/lib/exit.js +2 -1
  8. package/apps/cli/dist/commands/lib/index.d.ts +1 -0
  9. package/apps/cli/dist/commands/lib/index.js +1 -0
  10. package/apps/cli/dist/commands/lib/json.d.ts +1 -1
  11. package/apps/cli/dist/commands/lib/json.js +2 -2
  12. package/apps/cli/dist/commands/lib/orchestrator.d.ts +30 -0
  13. package/apps/cli/dist/commands/lib/orchestrator.js +146 -0
  14. package/apps/cli/dist/commands/lib/relay-http.js +15 -4
  15. package/apps/cli/dist/commands/lib/session-output.d.ts +1 -0
  16. package/apps/cli/dist/commands/lib/session-output.js +42 -10
  17. package/apps/cli/dist/commands/lib/status.js +3 -3
  18. package/apps/cli/dist/commands/lib/types.d.ts +1 -0
  19. package/apps/cli/dist/commands/lib/usage.js +46 -16
  20. package/apps/cli/dist/commands/orchestrator.d.ts +2 -0
  21. package/apps/cli/dist/commands/orchestrator.js +411 -0
  22. package/apps/cli/dist/commands/relay.js +5 -5
  23. package/apps/cli/dist/commands/remote.js +2 -2
  24. package/apps/cli/dist/commands/skill.d.ts +2 -0
  25. package/apps/cli/dist/commands/skill.js +214 -0
  26. package/apps/cli/dist/commands/turn.js +5 -2
  27. package/apps/daemon/dist/session/directory.d.ts +4 -0
  28. package/apps/daemon/dist/session/directory.js +30 -3
  29. package/apps/daemon/package.json +1 -1
  30. package/apps/relay/dist/connections.d.ts +1 -0
  31. package/apps/relay/dist/controller-handlers.js +4 -1
  32. package/apps/relay/dist/http-routes.js +32 -1
  33. package/apps/relay/dist/http-schemas.d.ts +16 -0
  34. package/apps/relay/dist/http-schemas.js +5 -0
  35. package/apps/relay/dist/relay-context.js +2 -0
  36. package/apps/relay/dist/session-projection-reducer.js +1 -1
  37. package/npm-shrinkwrap.json +5 -5
  38. package/package.json +1 -1
  39. package/packages/client/dist/client.d.ts +5 -0
  40. package/packages/client/dist/client.js +35 -7
  41. package/packages/client/dist/http.d.ts +1 -0
  42. package/packages/client/dist/http.js +15 -3
  43. package/packages/client/dist/live-session-state.d.ts +15 -0
  44. package/packages/client/dist/live-session-state.js +60 -0
  45. package/packages/client/dist/live-state.d.ts +8 -0
  46. package/packages/client/dist/live-state.js +33 -0
  47. package/packages/client/dist/live-types.d.ts +113 -0
  48. package/packages/client/dist/live-types.js +1 -0
  49. package/packages/client/dist/live.d.ts +2 -99
  50. package/packages/client/dist/live.js +16 -77
  51. package/packages/client/dist/types.d.ts +4 -1
  52. package/packages/client/dist/validation.d.ts +1 -0
  53. package/packages/client/dist/validation.js +11 -0
  54. package/packages/shared/dist/protocol.d.ts +3 -0
  55. package/packages/shared/dist/protocol.js +1 -0
@@ -0,0 +1,113 @@
1
+ import type { MachineSnapshot, SessionSnapshot } from "../../shared/dist/index.js";
2
+ import type { ControllerSnapshot, DecodedSessionEvent, MachineMetadata, PermissionMode, SessionMetadata } from "./types.js";
3
+ export type ControllerLiveSessionPageState = {
4
+ error?: string;
5
+ hasOlderRelayEvents?: boolean;
6
+ hasOlderEvents: boolean;
7
+ initialized: boolean;
8
+ latestCursor?: string;
9
+ loadingOlderEvents: boolean;
10
+ olderCursor?: string;
11
+ olderRelayVersion?: number;
12
+ oldestEventId?: number;
13
+ projectionHeadKey?: string;
14
+ runtimeCanSync?: boolean;
15
+ runtimeHistoryChecked?: boolean;
16
+ };
17
+ export type ControllerLiveState = {
18
+ connectionState: "offline" | "connecting" | "online";
19
+ events: DecodedSessionEvent[];
20
+ lastError?: string;
21
+ lastSnapshotAt: number;
22
+ lastSnapshotMachineCount: number;
23
+ lastSnapshotSessionCount: number;
24
+ lastSnapshotSource: string;
25
+ machineMetadata: Record<string, MachineMetadata>;
26
+ machines: MachineSnapshot[];
27
+ selectedSessionId?: string;
28
+ sessionMetadata: Record<string, SessionMetadata>;
29
+ sessionPages: Record<string, ControllerLiveSessionPageState>;
30
+ sessions: SessionSnapshot[];
31
+ snapshotHydrating: boolean;
32
+ snapshotReady: boolean;
33
+ };
34
+ export type ControllerLiveEvent = {
35
+ type: "change";
36
+ state: ControllerLiveState;
37
+ } | {
38
+ type: "connection";
39
+ state: ControllerLiveState["connectionState"];
40
+ } | {
41
+ type: "diagnostic";
42
+ kind: "snapshot" | "websocket";
43
+ detail: Record<string, string | number | boolean>;
44
+ } | {
45
+ type: "snapshot";
46
+ source: string;
47
+ snapshot: ControllerSnapshot;
48
+ } | {
49
+ type: "machine";
50
+ machine: MachineSnapshot;
51
+ } | {
52
+ type: "session";
53
+ session: SessionSnapshot;
54
+ } | {
55
+ type: "event";
56
+ event: DecodedSessionEvent;
57
+ } | {
58
+ type: "ack";
59
+ requestId: string;
60
+ sessionId?: string;
61
+ } | {
62
+ type: "error";
63
+ message: string;
64
+ code?: string;
65
+ requestId?: string;
66
+ };
67
+ export type ControllerLiveOptions = {
68
+ reconnectMs?: number;
69
+ sessionEventPageSize?: number;
70
+ olderSessionEventPageSize?: number;
71
+ promptAckTimeoutMs?: number;
72
+ };
73
+ export type SendPromptInput = {
74
+ sessionId: string;
75
+ text: string;
76
+ permissionMode?: PermissionMode;
77
+ timeoutMs?: number;
78
+ };
79
+ export type SendPromptResult = {
80
+ requestId: string;
81
+ turnId: string;
82
+ };
83
+ export type RefreshSessionEventHistoryInput = {
84
+ mode?: "preserve" | "latest";
85
+ };
86
+ export interface ControllerLiveProjection {
87
+ start(): void;
88
+ stop(): void;
89
+ state(): ControllerLiveState;
90
+ subscribe(listener: (event: ControllerLiveEvent) => void): () => void;
91
+ refreshSnapshot(reason: string): Promise<void>;
92
+ selectSession(sessionId: string): Promise<void>;
93
+ refreshSessionEventHistory(sessionId: string, input?: RefreshSessionEventHistoryInput): Promise<void>;
94
+ sendPrompt(input: SendPromptInput): Promise<SendPromptResult>;
95
+ loadOlderSessionEvents(sessionId: string, input?: {
96
+ reveal?: "older" | "preserve";
97
+ }): Promise<void>;
98
+ syncRuntimeHistoryWindow(sessionId: string): Promise<boolean>;
99
+ }
100
+ export type PendingPrompt = {
101
+ reject(error: Error): void;
102
+ resolve(result: SendPromptResult): void;
103
+ sessionId: string;
104
+ timer: ReturnType<typeof setTimeout>;
105
+ turnId: string;
106
+ };
107
+ export type SessionHistoryFence = {
108
+ generation: number;
109
+ liveEventEpoch?: number;
110
+ projectionHead: string | undefined;
111
+ sessionExisted: boolean;
112
+ sessionId: string;
113
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,103 +1,6 @@
1
- import { type MachineSnapshot, type SessionSnapshot } from "../../shared/dist/index.js";
2
1
  import type { ControllerClient } from "./client.js";
3
- import type { ControllerSnapshot, DecodedSessionEvent, MachineMetadata, PermissionMode, SessionMetadata } from "./types.js";
4
- export type ControllerLiveSessionPageState = {
5
- error?: string;
6
- hasOlderRelayEvents?: boolean;
7
- hasOlderEvents: boolean;
8
- initialized: boolean;
9
- latestCursor?: string;
10
- loadingOlderEvents: boolean;
11
- olderCursor?: string;
12
- olderRelayVersion?: number;
13
- oldestEventId?: number;
14
- projectionHeadKey?: string;
15
- runtimeCanSync?: boolean;
16
- runtimeHistoryChecked?: boolean;
17
- };
18
- export type ControllerLiveState = {
19
- connectionState: "offline" | "connecting" | "online";
20
- events: DecodedSessionEvent[];
21
- lastError?: string;
22
- lastSnapshotAt: number;
23
- lastSnapshotMachineCount: number;
24
- lastSnapshotSessionCount: number;
25
- lastSnapshotSource: string;
26
- machineMetadata: Record<string, MachineMetadata>;
27
- machines: MachineSnapshot[];
28
- selectedSessionId?: string;
29
- sessionMetadata: Record<string, SessionMetadata>;
30
- sessionPages: Record<string, ControllerLiveSessionPageState>;
31
- sessions: SessionSnapshot[];
32
- snapshotHydrating: boolean;
33
- snapshotReady: boolean;
34
- };
35
- export type ControllerLiveEvent = {
36
- type: "change";
37
- state: ControllerLiveState;
38
- } | {
39
- type: "connection";
40
- state: ControllerLiveState["connectionState"];
41
- } | {
42
- type: "diagnostic";
43
- kind: "snapshot" | "websocket";
44
- detail: Record<string, string | number | boolean>;
45
- } | {
46
- type: "snapshot";
47
- source: string;
48
- snapshot: ControllerSnapshot;
49
- } | {
50
- type: "machine";
51
- machine: MachineSnapshot;
52
- } | {
53
- type: "session";
54
- session: SessionSnapshot;
55
- } | {
56
- type: "event";
57
- event: DecodedSessionEvent;
58
- } | {
59
- type: "ack";
60
- requestId: string;
61
- sessionId?: string;
62
- } | {
63
- type: "error";
64
- message: string;
65
- code?: string;
66
- requestId?: string;
67
- };
68
- export type ControllerLiveOptions = {
69
- reconnectMs?: number;
70
- sessionEventPageSize?: number;
71
- olderSessionEventPageSize?: number;
72
- promptAckTimeoutMs?: number;
73
- };
74
- export type SendPromptInput = {
75
- sessionId: string;
76
- text: string;
77
- permissionMode?: PermissionMode;
78
- timeoutMs?: number;
79
- };
80
- export type SendPromptResult = {
81
- requestId: string;
82
- turnId: string;
83
- };
84
- export type RefreshSessionEventHistoryInput = {
85
- mode?: "preserve" | "latest";
86
- };
87
- export interface ControllerLiveProjection {
88
- start(): void;
89
- stop(): void;
90
- state(): ControllerLiveState;
91
- subscribe(listener: (event: ControllerLiveEvent) => void): () => void;
92
- refreshSnapshot(reason: string): Promise<void>;
93
- selectSession(sessionId: string): Promise<void>;
94
- refreshSessionEventHistory(sessionId: string, input?: RefreshSessionEventHistoryInput): Promise<void>;
95
- sendPrompt(input: SendPromptInput): Promise<SendPromptResult>;
96
- loadOlderSessionEvents(sessionId: string, input?: {
97
- reveal?: "older" | "preserve";
98
- }): Promise<void>;
99
- syncRuntimeHistoryWindow(sessionId: string): Promise<boolean>;
100
- }
2
+ import type { ControllerLiveEvent, ControllerLiveOptions, ControllerLiveProjection, ControllerLiveState, RefreshSessionEventHistoryInput, SendPromptInput, SendPromptResult } from "./live-types.js";
3
+ export type { ControllerLiveEvent, ControllerLiveOptions, ControllerLiveProjection, ControllerLiveSessionPageState, ControllerLiveState, RefreshSessionEventHistoryInput, SendPromptInput, SendPromptResult, } from "./live-types.js";
101
4
  export declare class ControllerLiveProjectionImpl implements ControllerLiveProjection {
102
5
  private readonly client;
103
6
  private readonly listeners;
@@ -1,6 +1,8 @@
1
1
  import { encryptJson, randomId, } from "../../shared/dist/index.js";
2
2
  import { ControllerClientError, ControllerCommandError } from "./errors.js";
3
- import { oldestSessionEventId, shouldHoldHistoricalEventForOlderPage } from "./session-event-order.js";
3
+ import { cloneLiveState, errorMessage, nonEmptyLiveString, sleep, upsertById, } from "./live-state.js";
4
+ import { hasVisibleTranscriptPayload, isAuthoritativeSessionModel, isSelectedSessionHeadMaterialized, newestEventIdForSession, oldestEventIdForSession, runtimeHistoricalBackfillCanContinue, runtimeHistoricalBackfillCompleted, runtimeHistoricalBackfillKnown, selectedSessionHistoryHead, selectedSessionProjectionHead, sessionEventCount, sessionHasHistoricalBackfillEvents, sessionHasMissingHistoricalPrefix, } from "./live-session-state.js";
5
+ import { shouldHoldHistoricalEventForOlderPage } from "./session-event-order.js";
4
6
  import { exactReplaceSessionEvents, mergeAuthoritativeSessionEventsByIdentity, mergeLatestPagePreservingLoadedOlderEvents, mergeSessionEventsById, } from "./session-event-window.js";
5
7
  import { subscribe as subscribeToRelay } from "./transport.js";
6
8
  import { wsUrl } from "./http.js";
@@ -67,7 +69,7 @@ export class ControllerLiveProjectionImpl {
67
69
  this.setState({ connectionState: "offline" });
68
70
  }
69
71
  state() {
70
- return cloneState(this.stateValue);
72
+ return cloneLiveState(this.stateValue);
71
73
  }
72
74
  subscribe(listener) {
73
75
  this.listeners.add(listener);
@@ -136,8 +138,8 @@ export class ControllerLiveProjectionImpl {
136
138
  await this.refreshSelectedSessionEventHistory(sessionId, input);
137
139
  }
138
140
  async sendPrompt(input) {
139
- const sessionId = nonEmpty(input.sessionId, "sessionId");
140
- const text = nonEmpty(input.text, "text");
141
+ const sessionId = nonEmptyLiveString(input.sessionId, "sessionId");
142
+ const text = nonEmptyLiveString(input.text, "text");
141
143
  await this.ensureSocketOpen();
142
144
  const socket = this.socket;
143
145
  if (!socket || socket.readyState !== WebSocket.OPEN) {
@@ -804,15 +806,7 @@ export class ControllerLiveProjectionImpl {
804
806
  void this.loadSelectedSessionEventHistory(session, { force: true });
805
807
  }
806
808
  selectedSessionHeadMaterialized(session) {
807
- const currentHead = session.currentHead || session.lastTurnId;
808
- if (!currentHead)
809
- return true;
810
- const localEvents = this.stateValue.events.filter((event) => event.sessionId === session.id);
811
- const eventIdMatch = /^event:(\d+)$/.exec(currentHead);
812
- if (eventIdMatch)
813
- return localEvents.some((event) => event.id === Number(eventIdMatch[1]));
814
- return localEvents.some((event) => event.turnId === currentHead ||
815
- (event.decoded?.type === "done" && (event.decoded.currentHead === currentHead || event.decoded.lastTurnId === currentHead)));
809
+ return isSelectedSessionHeadMaterialized(session, this.stateValue.events);
816
810
  }
817
811
  clearUninitializedAuthoritativeSnapshotSessions(sessions, sessionFences) {
818
812
  for (const session of sessions) {
@@ -905,7 +899,7 @@ export class ControllerLiveProjectionImpl {
905
899
  });
906
900
  }
907
901
  isAuthoritativeSession(session) {
908
- return session?.historyModel === "authoritative-linear";
902
+ return isAuthoritativeSessionModel(session);
909
903
  }
910
904
  isAuthoritativeSnapshotSession(session) {
911
905
  return this.isAuthoritativeSession(session) ||
@@ -941,42 +935,29 @@ export class ControllerLiveProjectionImpl {
941
935
  }
942
936
  runtimeHistoricalBackfillCanContinue(sessionId) {
943
937
  const metadata = this.stateValue.sessionMetadata[sessionId];
944
- const status = metadata?.historicalBackfillStatus ?? metadata?.historicalBackfill;
945
- return status === "partial" || metadata?.truncatedHistoricalBackfill === true || this.sessionHasMissingHistoricalPrefix(sessionId);
938
+ return runtimeHistoricalBackfillCanContinue(metadata, this.sessionHasMissingHistoricalPrefix(sessionId));
946
939
  }
947
940
  runtimeHistoricalBackfillKnown(sessionId) {
948
- const metadata = this.stateValue.sessionMetadata[sessionId];
949
- return Boolean(metadata &&
950
- (metadata.historicalBackfillStatus !== undefined ||
951
- metadata.historicalBackfill !== undefined ||
952
- metadata.historicalBackfillCursor !== undefined ||
953
- metadata.truncatedHistoricalBackfill !== undefined));
941
+ return runtimeHistoricalBackfillKnown(this.stateValue.sessionMetadata[sessionId]);
954
942
  }
955
943
  runtimeHistoricalBackfillCompleted(sessionId) {
956
- const metadata = this.stateValue.sessionMetadata[sessionId];
957
- const status = metadata?.historicalBackfillStatus ?? metadata?.historicalBackfill;
958
- return status === "completed" || metadata?.historicalBackfillCursor === 0;
944
+ return runtimeHistoricalBackfillCompleted(this.stateValue.sessionMetadata[sessionId]);
959
945
  }
960
946
  oldestEventIdForSession(sessionId) {
961
- return oldestSessionEventId(this.stateValue.events, sessionId);
947
+ return oldestEventIdForSession(this.stateValue.events, sessionId);
962
948
  }
963
949
  newestEventIdForSession(sessionId) {
964
- return Math.max(0, ...this.stateValue.events.filter((event) => event.sessionId === sessionId).map((event) => event.id));
950
+ return newestEventIdForSession(this.stateValue.events, sessionId);
965
951
  }
966
952
  sessionEventCount(sessionId) {
967
- return this.stateValue.events.filter((event) => event.sessionId === sessionId).length;
953
+ return sessionEventCount(this.stateValue.events, sessionId);
968
954
  }
969
955
  sessionHasHistoricalBackfillEvents(sessionId) {
970
- return this.stateValue.events.some((event) => event.sessionId === sessionId && event.messageId?.startsWith("hist_"));
956
+ return sessionHasHistoricalBackfillEvents(this.stateValue.events, sessionId);
971
957
  }
972
958
  sessionHasMissingHistoricalPrefix(sessionId) {
973
959
  const session = this.stateValue.sessions.find((item) => item.id === sessionId);
974
- if (!session?.currentHead && !session?.lastTurnId)
975
- return false;
976
- const turnSeqs = this.stateValue.events
977
- .filter((event) => event.sessionId === sessionId && event.messageId?.startsWith("hist_") && typeof event.turnSeq === "number")
978
- .map((event) => event.turnSeq);
979
- return turnSeqs.length > 0 && Math.min(...turnSeqs) > 0;
960
+ return sessionHasMissingHistoricalPrefix(session, this.stateValue.events);
980
961
  }
981
962
  captureSessionHistoryFence(sessionId, session = this.stateValue.sessions.find((item) => item.id === sessionId), options = {}) {
982
963
  return {
@@ -1073,45 +1054,3 @@ export class ControllerLiveProjectionImpl {
1073
1054
  this.emit({ type: "error", message: errorMessage(error), requestId });
1074
1055
  }
1075
1056
  }
1076
- function upsertById(items, next) {
1077
- const copy = [...items];
1078
- const index = copy.findIndex((item) => item.id === next.id);
1079
- if (index >= 0)
1080
- copy[index] = next;
1081
- else
1082
- copy.unshift(next);
1083
- return copy;
1084
- }
1085
- function selectedSessionHistoryHead(session) {
1086
- return [session.currentHead ?? "", session.lastTurnId ?? "", session.status, String(session.updatedAt)].join(":");
1087
- }
1088
- function selectedSessionProjectionHead(session) {
1089
- return [session.currentHead ?? "", session.lastTurnId ?? ""].join(":");
1090
- }
1091
- function hasVisibleTranscriptPayload(event) {
1092
- const type = event.decoded?.type;
1093
- return type === "user_prompt" || type === "text_delta" || type === "tool_call";
1094
- }
1095
- function cloneState(state) {
1096
- return {
1097
- ...state,
1098
- events: [...state.events],
1099
- machineMetadata: { ...state.machineMetadata },
1100
- machines: [...state.machines],
1101
- sessionMetadata: { ...state.sessionMetadata },
1102
- sessionPages: Object.fromEntries(Object.entries(state.sessionPages).map(([key, value]) => [key, { ...value }])),
1103
- sessions: [...state.sessions],
1104
- };
1105
- }
1106
- function sleep(ms) {
1107
- return new Promise((resolve) => setTimeout(resolve, ms));
1108
- }
1109
- function nonEmpty(value, label) {
1110
- const trimmed = value.trim();
1111
- if (!trimmed)
1112
- throw new ControllerClientError(`${label} must be a non-empty string`, "INVALID_ARGUMENT");
1113
- return trimmed;
1114
- }
1115
- function errorMessage(error) {
1116
- return error instanceof Error ? error.message : String(error);
1117
- }
@@ -143,6 +143,9 @@ export type CreateSessionInput = {
143
143
  export type RunOptions = {
144
144
  wait?: boolean;
145
145
  permissionMode?: PermissionMode;
146
+ requestId?: string;
147
+ turnId?: string;
148
+ promptHash?: string;
146
149
  waitTimeoutMs?: number;
147
150
  ackTimeoutMs?: number;
148
151
  runtimeTimeoutMs?: number;
@@ -204,7 +207,7 @@ export type WaitInput = {
204
207
  export type CollectInput = {
205
208
  before?: EventCursor;
206
209
  beforeCursor?: string;
207
- since?: EventCursor;
210
+ since?: EventCursor | string;
208
211
  limit?: number;
209
212
  turnId?: string;
210
213
  };
@@ -9,6 +9,7 @@ export declare function requireNonEmpty(value: string, field: string): string;
9
9
  export declare function matchesWaitCondition(session: SessionSnapshot, until: WaitCondition): boolean;
10
10
  export declare function isWaitCondition(value: unknown): value is WaitCondition;
11
11
  export declare function parseEventCursor(value: EventCursor | undefined): number | undefined;
12
+ export declare function parseSinceEventCursor(value: EventCursor | string | undefined): number | string | undefined;
12
13
  export declare function parseEventLimit(value: number | undefined): number | undefined;
13
14
  export declare function parsePositiveDuration(value: number, field: string): number;
14
15
  export declare function sleep(ms: number): Promise<void>;
@@ -69,6 +69,17 @@ export function parseEventCursor(value) {
69
69
  }
70
70
  return Number(value.slice("cursor_".length));
71
71
  }
72
+ export function parseSinceEventCursor(value) {
73
+ if (value === undefined)
74
+ return undefined;
75
+ if (typeof value === "number")
76
+ return parseEventCursor(value);
77
+ if (/^cursor_\d+$/.test(value))
78
+ return parseEventCursor(value);
79
+ if (value.trim())
80
+ return value;
81
+ throw new ControllerClientError(`Invalid event cursor: ${value}`, "INVALID_ARGUMENT");
82
+ }
72
83
  export function parseEventLimit(value) {
73
84
  if (value === undefined)
74
85
  return undefined;
@@ -17,6 +17,7 @@ export type ControllerClientMessage = {
17
17
  turnId: string;
18
18
  encryptedPrompt: EncryptedEnvelope;
19
19
  encryptedMetadata?: EncryptedEnvelope;
20
+ promptHash?: string;
20
21
  permissionMode?: PermissionMode;
21
22
  timeoutMs?: number;
22
23
  } | {
@@ -441,6 +442,7 @@ export type JsonEnvelope<T = unknown> = {
441
442
  code: string;
442
443
  message: string;
443
444
  capability?: string;
445
+ retryable?: boolean;
444
446
  };
445
447
  meta: {
446
448
  requestId?: string;
@@ -450,6 +452,7 @@ export type JsonEnvelope<T = unknown> = {
450
452
  activeTurnId?: string;
451
453
  latestCompletedTurnId?: string;
452
454
  commandAcked?: boolean;
455
+ rawStatus?: string;
453
456
  };
454
457
  };
455
458
  export declare function parseClientMessage(value: unknown): ClientMessage;
@@ -19,6 +19,7 @@ const controllerMessageSchema = z.discriminatedUnion("type", [
19
19
  turnId: z.string().min(1),
20
20
  encryptedPrompt: encryptedEnvelopeSchema,
21
21
  encryptedMetadata: encryptedEnvelopeSchema.optional(),
22
+ promptHash: z.string().min(1).optional(),
22
23
  permissionMode: z.enum(["approve-all", "approve-reads", "deny-all"]).optional(),
23
24
  timeoutMs: z.number().positive().optional(),
24
25
  }),