Procházet zdrojové kódy

fix(orchestrator-wake): handle null archive timestamps

Balogun Feranmi před 6 dny
rodič
revize
0c2f24e970

+ 49 - 0
src/hooks/orchestrator-wake/index.test.ts

@@ -360,6 +360,27 @@ describe('orchestrator wake scheduler', () => {
     expect(clock.pendingCount()).toBe(0);
   });
 
+  test('treats a null archive timestamp as unarchived for v1', async () => {
+    const promptAsync = mock(async () => ({}));
+    const { scheduler } = createScheduler({
+      sessionClient: makeClient({
+        promptAsync,
+        get: mock(async () => ({
+          data: {
+            time: { created: 1, updated: 1, archived: null },
+          },
+        })),
+      }),
+    });
+
+    await scheduler.event({
+      event: { type: 'session.idle', properties: { sessionID: 'p1' } },
+    });
+    await clock.advance(60_000);
+
+    expect(promptAsync).toHaveBeenCalledTimes(1);
+  });
+
   test('archive update cancels an armed timer', async () => {
     const promptAsync = mock(async () => ({}));
     const { scheduler } = createScheduler({
@@ -1288,6 +1309,34 @@ describe('children-driven degraded mode (v2)', () => {
     expect(clock.pendingCount()).toBe(0);
   });
 
+  test('treats a null archive timestamp as unarchived for v2', async () => {
+    const promptAsync = mock(async () => ({}));
+    const { scheduler } = createScheduler({
+      hostFlavor: 'v2',
+      intervalMs: 60_000,
+      sessionClient: makeV2Client({
+        promptAsync,
+        listChildren: [{ id: 'c1', time: { updated: Date.now() } }],
+        get: mock(async () => ({
+          data: {
+            time: { created: 1, updated: 1, archived: null },
+          },
+        })),
+      }),
+    });
+
+    await scheduler.event({
+      event: { type: 'session.idle', properties: { sessionID: 'p1' } },
+    });
+    await clock.advance(60_000);
+
+    expect(promptAsync).toHaveBeenCalledTimes(1);
+    const call = (
+      promptAsync.mock.calls as unknown as Array<[{ delivery?: string }]>
+    )[0]?.[0];
+    expect(call?.delivery).toBe('queue');
+  });
+
   test('v2 without get uses observed archive state and preserves queue delivery', async () => {
     const promptAsync = mock(async () => ({}));
     const { scheduler } = createScheduler({

+ 5 - 1
src/hooks/orchestrator-wake/index.ts

@@ -407,7 +407,11 @@ function readSessionArchiveState(session: unknown): boolean | undefined {
   ) {
     return undefined;
   }
-  return session.time.archived !== undefined;
+  const archived = session.time.archived;
+  if (archived === undefined || archived === null) return false;
+  return typeof archived === 'number' && Number.isFinite(archived)
+    ? true
+    : undefined;
 }
 
 function readEventArchiveState(event: {