@cydm/happy-elves 0.1.0-beta.495 → 0.1.0-beta.496
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/apps/cli/package.json +1 -1
- package/apps/daemon/dist/direct/server.js +1 -0
- package/apps/daemon/dist/orchestration/watcher.d.ts +1 -0
- package/apps/daemon/dist/orchestration/watcher.js +67 -7
- package/apps/daemon/dist/relay/register.js +1 -0
- package/apps/daemon/dist/session/he-official/common.d.ts +3 -0
- package/apps/daemon/package.json +1 -1
- package/package.json +1 -1
- package/packages/shared/dist/protocol-schemas.d.ts +6 -0
- package/packages/shared/dist/protocol-schemas.js +3 -0
- package/packages/shared/dist/protocol-types.d.ts +3 -0
package/apps/cli/package.json
CHANGED
|
@@ -742,6 +742,7 @@ class DirectState {
|
|
|
742
742
|
acpxVersion: runtimeCapabilities.version,
|
|
743
743
|
daemonVersion: daemonPackage.version,
|
|
744
744
|
...(runtimeCapabilities.turnPrimitives ? { turnPrimitives: runtimeCapabilities.turnPrimitives } : {}),
|
|
745
|
+
runtimeEvents: { forwardCursor: true },
|
|
745
746
|
sessionPrimitives: runtimePrimitiveSupportForCapabilities(runtimeCapabilities.sessionPrimitives, "native"),
|
|
746
747
|
...(runtimeCapabilities.sidechat ? { sidechat: runtimeCapabilities.sidechat } : {}),
|
|
747
748
|
directoryBrowser: true,
|
|
@@ -22,6 +22,7 @@ export declare function catchUpSubscriptionEvents(input: {
|
|
|
22
22
|
config: DaemonConfig;
|
|
23
23
|
subscription: OrchestrationSubscriptionRecord;
|
|
24
24
|
}): Promise<SubscriptionCatchUpResult>;
|
|
25
|
+
export declare function remoteEventCursorForSubscription(crewMachineId: string, subscription: Pick<OrchestrationSubscriptionRecord, "remoteEventCursor" | "remoteEventSeq">): string | undefined;
|
|
25
26
|
export declare function attentionFromReconcile(result: TurnReconcileResult): {
|
|
26
27
|
eventType: TaskAttentionEventType;
|
|
27
28
|
reason: string;
|
|
@@ -4,10 +4,13 @@ import { RemoteCommandError, requestRemoteMachineResult } from "../relay/remote-
|
|
|
4
4
|
import { runtimeRead } from "../session/runtime-truth.js";
|
|
5
5
|
import { activeSubscriptionsForTurn, appendAttention, markSubscriptionLiveness, markSubscriptionRemoteScan, recordTurnRuntimeBinding, } from "./store.js";
|
|
6
6
|
import { scheduleRemoteOrchestrationWork } from "./remote-work-budget.js";
|
|
7
|
+
import { relayMachineRegistry } from "../state.js";
|
|
7
8
|
const remoteCatchupsInFlight = new Map();
|
|
8
9
|
const legacyRemoteCursorFallbackUntil = new Map();
|
|
10
|
+
const remoteForwardCursorConfirmed = new Set();
|
|
9
11
|
const legacyRemoteCursorFallbackMs = 5 * 60 * 1000;
|
|
10
12
|
const initialRemoteEventCursor = "event_000000000000";
|
|
13
|
+
const forwardEventCursorSinceBeta = 495;
|
|
11
14
|
export async function observeSessionEvent(input) {
|
|
12
15
|
const attention = attentionFromEvent(input.payload);
|
|
13
16
|
if (!attention) {
|
|
@@ -116,11 +119,10 @@ export async function catchUpSubscriptionEvents(input) {
|
|
|
116
119
|
}
|
|
117
120
|
async function catchUpSubscriptionEventsInternal(input) {
|
|
118
121
|
const crewMachineId = input.subscription.crewMachineId ?? input.config.machineId;
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
? input.subscription.remoteEventCursor
|
|
122
|
-
?? (input.subscription.remoteEventSeq ? `event_${String(input.subscription.remoteEventSeq).padStart(12, "0")}` : initialRemoteEventCursor)
|
|
122
|
+
const remoteCursor = crewMachineId !== input.config.machineId
|
|
123
|
+
? remoteEventCursorForSubscription(crewMachineId, input.subscription)
|
|
123
124
|
: undefined;
|
|
125
|
+
const remoteCursorEnabled = remoteCursor !== undefined;
|
|
124
126
|
try {
|
|
125
127
|
const result = crewMachineId === input.config.machineId
|
|
126
128
|
? await runtimeRead(input.config, {
|
|
@@ -171,7 +173,14 @@ async function catchUpSubscriptionEventsInternal(input) {
|
|
|
171
173
|
// the forward cursor in its response. Treat that as the legacy path for
|
|
172
174
|
// this machine; in particular, never advance a durable cursor from a
|
|
173
175
|
// page whose peer cannot prove append-only progress.
|
|
174
|
-
if (crewMachineId !== input.config.machineId &&
|
|
176
|
+
if (crewMachineId !== input.config.machineId && result.cursorSupported === true) {
|
|
177
|
+
remoteForwardCursorConfirmed.add(crewMachineId);
|
|
178
|
+
}
|
|
179
|
+
else if (crewMachineId !== input.config.machineId && remoteCursorEnabled && !result.nextAfter) {
|
|
180
|
+
// A peer that accepted the forward request but cannot prove the cursor
|
|
181
|
+
// contract is no longer trusted. Remove the positive handshake so the
|
|
182
|
+
// next safe, legacy-compatible request can renegotiate it.
|
|
183
|
+
remoteForwardCursorConfirmed.delete(crewMachineId);
|
|
175
184
|
legacyRemoteCursorFallbackUntil.set(crewMachineId, Date.now() + legacyRemoteCursorFallbackMs);
|
|
176
185
|
}
|
|
177
186
|
const observed = await observeSubscriptionRuntimeEvents({
|
|
@@ -201,6 +210,7 @@ async function catchUpSubscriptionEventsInternal(input) {
|
|
|
201
210
|
}
|
|
202
211
|
catch (error) {
|
|
203
212
|
if (crewMachineId !== input.config.machineId && remoteCursorEnabled && isRemoteEventCursorCompatibilityFailure(error)) {
|
|
213
|
+
remoteForwardCursorConfirmed.delete(crewMachineId);
|
|
204
214
|
legacyRemoteCursorFallbackUntil.set(crewMachineId, Date.now() + legacyRemoteCursorFallbackMs);
|
|
205
215
|
}
|
|
206
216
|
await appendOrchestrationAudit(input.config, "orchestration.subscription.catchup_failed", `Subscription catch-up failed for ${input.subscription.taskId}`, {
|
|
@@ -216,13 +226,63 @@ async function catchUpSubscriptionEventsInternal(input) {
|
|
|
216
226
|
function canUseRemoteEventCursor(crewMachineId) {
|
|
217
227
|
const until = legacyRemoteCursorFallbackUntil.get(crewMachineId);
|
|
218
228
|
if (until === undefined)
|
|
219
|
-
return
|
|
229
|
+
return remotePeerAdvertisesForwardCursor(crewMachineId);
|
|
220
230
|
if (until <= Date.now()) {
|
|
221
231
|
legacyRemoteCursorFallbackUntil.delete(crewMachineId);
|
|
222
|
-
return
|
|
232
|
+
return remotePeerAdvertisesForwardCursor(crewMachineId);
|
|
223
233
|
}
|
|
224
234
|
return false;
|
|
225
235
|
}
|
|
236
|
+
export function remoteEventCursorForSubscription(crewMachineId, subscription) {
|
|
237
|
+
const fallbackUntil = legacyRemoteCursorFallbackUntil.get(crewMachineId);
|
|
238
|
+
if (fallbackUntil !== undefined && fallbackUntil > Date.now())
|
|
239
|
+
return undefined;
|
|
240
|
+
if (fallbackUntil !== undefined)
|
|
241
|
+
legacyRemoteCursorFallbackUntil.delete(crewMachineId);
|
|
242
|
+
const advertised = remotePeerForwardCursorSupport(crewMachineId);
|
|
243
|
+
// An explicit old version wins over a stale persisted cursor (for example,
|
|
244
|
+
// after a peer downgrade). Unknown manifests are different: a persisted
|
|
245
|
+
// cursor is still positive evidence until the peer identity is refreshed.
|
|
246
|
+
if (advertised === false)
|
|
247
|
+
return undefined;
|
|
248
|
+
// A persisted cursor is positive evidence from a previous successful
|
|
249
|
+
// forward-page response. It remains usable even when the peer's manifest is
|
|
250
|
+
// temporarily unavailable during reconnect.
|
|
251
|
+
if (subscription.remoteEventCursor)
|
|
252
|
+
return subscription.remoteEventCursor;
|
|
253
|
+
if (advertised !== true || !canUseRemoteEventCursor(crewMachineId))
|
|
254
|
+
return undefined;
|
|
255
|
+
return subscription.remoteEventSeq
|
|
256
|
+
? `event_${String(subscription.remoteEventSeq).padStart(12, "0")}`
|
|
257
|
+
: initialRemoteEventCursor;
|
|
258
|
+
}
|
|
259
|
+
function remotePeerAdvertisesForwardCursor(crewMachineId) {
|
|
260
|
+
return remotePeerForwardCursorSupport(crewMachineId) === true;
|
|
261
|
+
}
|
|
262
|
+
function remotePeerForwardCursorSupport(crewMachineId) {
|
|
263
|
+
const machine = relayMachineRegistry.get(crewMachineId);
|
|
264
|
+
const daemonVersion = machine?.capabilities.daemonVersion?.trim();
|
|
265
|
+
const advertisedFeature = machine?.capabilities.runtimeEvents?.forwardCursor;
|
|
266
|
+
if (advertisedFeature === false)
|
|
267
|
+
return false;
|
|
268
|
+
if (!daemonVersion) {
|
|
269
|
+
if (advertisedFeature === true || remoteForwardCursorConfirmed.has(crewMachineId))
|
|
270
|
+
return true;
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
// Published beta495 introduced the append-only eventsPage `after` field.
|
|
274
|
+
// Workspace daemons also publish an explicit feature bit. The historical
|
|
275
|
+
// package version 0.1.0 alone is not evidence: older development daemons
|
|
276
|
+
// used that same version while rejecting `after`.
|
|
277
|
+
const match = /^0\.1\.0-beta\.(\d+)$/.exec(daemonVersion);
|
|
278
|
+
if (match && Number(match[1]) < forwardEventCursorSinceBeta)
|
|
279
|
+
return false;
|
|
280
|
+
if (advertisedFeature === true || remoteForwardCursorConfirmed.has(crewMachineId))
|
|
281
|
+
return true;
|
|
282
|
+
if (match)
|
|
283
|
+
return Number(match[1]) >= forwardEventCursorSinceBeta;
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
226
286
|
function isRemoteEventCursorCompatibilityFailure(error) {
|
|
227
287
|
if (!(error instanceof RemoteCommandError))
|
|
228
288
|
return false;
|
|
@@ -49,6 +49,7 @@ export async function registerMachine(ws, config) {
|
|
|
49
49
|
acpxVersion: capabilities.version,
|
|
50
50
|
daemonVersion: daemonPackage.version,
|
|
51
51
|
...(capabilities.turnPrimitives ? { turnPrimitives: capabilities.turnPrimitives } : {}),
|
|
52
|
+
runtimeEvents: { forwardCursor: true },
|
|
52
53
|
sessionPrimitives: runtimePrimitiveSupportForCapabilities(capabilities.sessionPrimitives, "native"),
|
|
53
54
|
...(capabilities.sidechat ? { sidechat: capabilities.sidechat } : {}),
|
|
54
55
|
directoryBrowser: true,
|
|
@@ -45,6 +45,9 @@ export declare function machineList(config: DaemonConfig): {
|
|
|
45
45
|
turnPrimitives?: {
|
|
46
46
|
steer: "native" | "unsupported";
|
|
47
47
|
};
|
|
48
|
+
runtimeEvents?: {
|
|
49
|
+
forwardCursor: boolean;
|
|
50
|
+
};
|
|
48
51
|
sidechat?: import("../../../../../packages/shared/dist/index.js").SidechatCapability;
|
|
49
52
|
directoryBrowser?: boolean;
|
|
50
53
|
filePreview?: boolean;
|
package/apps/daemon/package.json
CHANGED
package/package.json
CHANGED
|
@@ -136,6 +136,9 @@ export declare const capabilitiesSchema: z.ZodObject<{
|
|
|
136
136
|
unsupported: "unsupported";
|
|
137
137
|
}>;
|
|
138
138
|
}, z.core.$strip>>;
|
|
139
|
+
runtimeEvents: z.ZodOptional<z.ZodObject<{
|
|
140
|
+
forwardCursor: z.ZodBoolean;
|
|
141
|
+
}, z.core.$strip>>;
|
|
139
142
|
sidechat: z.ZodOptional<z.ZodObject<{
|
|
140
143
|
strategy: z.ZodEnum<{
|
|
141
144
|
native: "native";
|
|
@@ -289,6 +292,9 @@ export declare const machineSnapshotSchema: z.ZodObject<{
|
|
|
289
292
|
unsupported: "unsupported";
|
|
290
293
|
}>;
|
|
291
294
|
}, z.core.$strip>>;
|
|
295
|
+
runtimeEvents: z.ZodOptional<z.ZodObject<{
|
|
296
|
+
forwardCursor: z.ZodBoolean;
|
|
297
|
+
}, z.core.$strip>>;
|
|
292
298
|
sidechat: z.ZodOptional<z.ZodObject<{
|
|
293
299
|
strategy: z.ZodEnum<{
|
|
294
300
|
native: "native";
|
|
@@ -72,6 +72,9 @@ export const capabilitiesSchema = z.object({
|
|
|
72
72
|
daemonVersion: z.string().optional(),
|
|
73
73
|
sessionPrimitives: sessionPrimitivesSchema.optional(),
|
|
74
74
|
turnPrimitives: turnPrimitivesSchema.optional(),
|
|
75
|
+
runtimeEvents: z.object({
|
|
76
|
+
forwardCursor: z.boolean(),
|
|
77
|
+
}).optional(),
|
|
75
78
|
sidechat: sidechatCapabilitySchema.optional(),
|
|
76
79
|
directoryBrowser: z.boolean().optional(),
|
|
77
80
|
filePreview: z.boolean().optional(),
|