@atolis-hq/wake 0.2.27 → 0.2.29
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.
|
@@ -234,6 +234,12 @@ async function applyEvent(current, event, ctx, config) {
|
|
|
234
234
|
...(payload.sentinel === 'AWAITING_APPROVAL' && payload.action !== undefined
|
|
235
235
|
? { pendingApprovalAction: payload.action }
|
|
236
236
|
: {}),
|
|
237
|
+
...(payload.executionOutcome !== undefined
|
|
238
|
+
? { lastExecutionOutcome: payload.executionOutcome }
|
|
239
|
+
: {}),
|
|
240
|
+
...(payload.workflowOutcome !== undefined
|
|
241
|
+
? { lastWorkflowOutcome: payload.workflowOutcome }
|
|
242
|
+
: {}),
|
|
237
243
|
};
|
|
238
244
|
if (payload.sentinel === 'BLOCKED' || payload.sentinel === 'FAILED') {
|
|
239
245
|
nextContext.blockedFromStage = current.wake.stage;
|
|
@@ -8,22 +8,111 @@ import { createEventEnvelope } from '../lib/event-log.js';
|
|
|
8
8
|
// `deliverOutboundEvent` is injected rather than imported so this module does
|
|
9
9
|
// not depend on the outbox module directly.
|
|
10
10
|
export function createStaleRunReconciler(deps) {
|
|
11
|
+
function isTerminalLifecycle(lifecycle) {
|
|
12
|
+
return lifecycle === 'TERMINAL';
|
|
13
|
+
}
|
|
14
|
+
function recoveryOutcomeForLifecycle(lifecycle) {
|
|
15
|
+
switch (lifecycle) {
|
|
16
|
+
case 'CREATED':
|
|
17
|
+
case 'CLAIMED':
|
|
18
|
+
case 'PREPARING':
|
|
19
|
+
return 'CANCELED_BY_RECONCILIATION';
|
|
20
|
+
case 'PROCESS_STARTING':
|
|
21
|
+
case 'RUNNING':
|
|
22
|
+
case 'FINALISING':
|
|
23
|
+
return 'STALLED';
|
|
24
|
+
case 'TERMINAL':
|
|
25
|
+
return 'SUPERSEDED';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
11
28
|
async function staleReason(record, now) {
|
|
12
29
|
if (record.status !== 'running') {
|
|
13
30
|
return null;
|
|
14
31
|
}
|
|
15
|
-
if (
|
|
32
|
+
if (isTerminalLifecycle(record.lifecycle)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (deps.isRunningRecordActive === undefined) {
|
|
36
|
+
if (record.lifecycle !== 'CREATED' &&
|
|
37
|
+
record.lifecycle !== 'CLAIMED' &&
|
|
38
|
+
record.lifecycle !== 'PREPARING') {
|
|
39
|
+
const startedAtMs = Date.parse(record.startedAt);
|
|
40
|
+
if (!Number.isFinite(startedAtMs)) {
|
|
41
|
+
return 'timeout';
|
|
42
|
+
}
|
|
43
|
+
return now.getTime() - startedAtMs >= deps.runnerTimeoutMs() ? 'timeout' : null;
|
|
44
|
+
}
|
|
45
|
+
return 'startup-recovery';
|
|
46
|
+
}
|
|
47
|
+
const active = await deps.isRunningRecordActive(record);
|
|
48
|
+
if (active) {
|
|
49
|
+
const startedAtMs = Date.parse(record.startedAt);
|
|
50
|
+
if (!Number.isFinite(startedAtMs)) {
|
|
51
|
+
return 'timeout';
|
|
52
|
+
}
|
|
53
|
+
return now.getTime() - startedAtMs >= deps.runnerTimeoutMs() ? 'timeout' : null;
|
|
54
|
+
}
|
|
55
|
+
if (record.lifecycle === 'RUNNING' || record.lifecycle === 'PROCESS_STARTING') {
|
|
16
56
|
return 'runner-lock-not-active';
|
|
17
57
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
58
|
+
return 'startup-recovery';
|
|
59
|
+
}
|
|
60
|
+
async function deliverRecoveredLabels(projection, runId, finishedAt) {
|
|
61
|
+
await deps.deliverOutboundEvent(createLabelsEvent({
|
|
62
|
+
projection,
|
|
63
|
+
runId,
|
|
64
|
+
statusLabel: 'wake:status.failed',
|
|
65
|
+
stageLabel: stageLabelForStage(projection.wake.stage),
|
|
66
|
+
workflowLabel: workflowLabelForWorkflowName(workflowNameForProjection(projection, deps.config)),
|
|
67
|
+
occurredAt: finishedAt,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
async function recoverMissingRunRecordClaims(runRecords, finishedAt) {
|
|
71
|
+
const knownRunIds = new Set(runRecords.map((record) => record.runId));
|
|
72
|
+
const projections = await deps.stateStore.listIssueStates();
|
|
73
|
+
for (const projection of projections) {
|
|
74
|
+
const runId = projection.wake.lastRunId;
|
|
75
|
+
if (runId === undefined ||
|
|
76
|
+
knownRunIds.has(runId) ||
|
|
77
|
+
runRecords.some((record) => record.workItemKey === projection.workItemKey) ||
|
|
78
|
+
projection.context.lastRunSentinel !== undefined) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const event = createEventEnvelope({
|
|
82
|
+
eventId: `${runId}-missing-run-record-recovered`,
|
|
83
|
+
workItemKey: projection.workItemKey,
|
|
84
|
+
streamScope: 'work-item',
|
|
85
|
+
direction: 'internal',
|
|
86
|
+
sourceSystem: 'wake',
|
|
87
|
+
sourceEventType: 'wake.run.completed',
|
|
88
|
+
sourceRefs: {
|
|
89
|
+
repo: projection.issue.repo,
|
|
90
|
+
issueNumber: projection.issue.number,
|
|
91
|
+
runId,
|
|
92
|
+
},
|
|
93
|
+
occurredAt: finishedAt,
|
|
94
|
+
ingestedAt: finishedAt,
|
|
95
|
+
trigger: 'immediate',
|
|
96
|
+
payload: {
|
|
97
|
+
action: projection.context.lastRunAction,
|
|
98
|
+
sentinel: 'FAILED',
|
|
99
|
+
executionOutcome: 'CANCELED_BY_RECONCILIATION',
|
|
100
|
+
runId,
|
|
101
|
+
reason: 'runner:missing-run-record',
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
await deps.stateStore.appendEventEnvelope(event);
|
|
105
|
+
await deps.projectionUpdater.rebuildFromEvents([event]);
|
|
106
|
+
const updatedProjection = await deps.stateStore.readIssueState(projection.workItemKey);
|
|
107
|
+
if (updatedProjection !== null) {
|
|
108
|
+
await deliverRecoveredLabels(updatedProjection, runId, finishedAt);
|
|
109
|
+
}
|
|
21
110
|
}
|
|
22
|
-
return now.getTime() - startedAtMs >= deps.runnerTimeoutMs() ? 'timeout' : null;
|
|
23
111
|
}
|
|
24
112
|
async function reconcileStaleRunningRecords(now) {
|
|
25
113
|
const finishedAt = now.toISOString();
|
|
26
114
|
const runRecords = await deps.stateStore.listRunRecords();
|
|
115
|
+
await recoverMissingRunRecordClaims(runRecords, finishedAt);
|
|
27
116
|
const staleRecords = [];
|
|
28
117
|
for (const record of runRecords) {
|
|
29
118
|
const reason = await staleReason(record, now);
|
|
@@ -46,8 +135,10 @@ export function createStaleRunReconciler(deps) {
|
|
|
46
135
|
if (projection === null || projection.wake.lastRunId !== record.runId || newerCompletedRun) {
|
|
47
136
|
await deps.stateStore.writeRunRecord({
|
|
48
137
|
...record,
|
|
138
|
+
lifecycle: 'TERMINAL',
|
|
49
139
|
status: 'superseded',
|
|
50
140
|
finishedAt,
|
|
141
|
+
executionOutcome: 'SUPERSEDED',
|
|
51
142
|
summary: 'Stale running record was superseded by a newer run.',
|
|
52
143
|
metadata: {
|
|
53
144
|
...record.metadata,
|
|
@@ -57,15 +148,19 @@ export function createStaleRunReconciler(deps) {
|
|
|
57
148
|
});
|
|
58
149
|
continue;
|
|
59
150
|
}
|
|
151
|
+
const staleExecutionOutcome = reason === 'timeout' ? 'TIMED_OUT' : recoveryOutcomeForLifecycle(record.lifecycle);
|
|
60
152
|
await deps.stateStore.writeRunRecord({
|
|
61
153
|
...record,
|
|
154
|
+
lifecycle: 'TERMINAL',
|
|
62
155
|
status: 'failed',
|
|
63
156
|
finishedAt,
|
|
64
157
|
sentinel: 'FAILED',
|
|
158
|
+
executionOutcome: staleExecutionOutcome,
|
|
65
159
|
summary: `Run exceeded timeout while marked running and was reconciled by a later tick.`,
|
|
66
160
|
metadata: {
|
|
67
161
|
...record.metadata,
|
|
68
162
|
reconciledBy: 'stale-running-record',
|
|
163
|
+
recoveryLifecycle: record.lifecycle,
|
|
69
164
|
staleReason: reason,
|
|
70
165
|
timeoutMs: deps.runnerTimeoutMs(),
|
|
71
166
|
},
|
|
@@ -88,8 +183,13 @@ export function createStaleRunReconciler(deps) {
|
|
|
88
183
|
payload: {
|
|
89
184
|
action: record.action,
|
|
90
185
|
sentinel: 'FAILED',
|
|
186
|
+
executionOutcome: staleExecutionOutcome,
|
|
91
187
|
runId: record.runId,
|
|
92
|
-
reason: reason === 'timeout'
|
|
188
|
+
reason: reason === 'timeout'
|
|
189
|
+
? 'runner:stale-timeout'
|
|
190
|
+
: reason === 'startup-recovery'
|
|
191
|
+
? `runner:recover-${record.lifecycle.toLowerCase()}`
|
|
192
|
+
: 'runner:orphaned-process',
|
|
93
193
|
...(record.routing === undefined ? {} : { routing: record.routing }),
|
|
94
194
|
},
|
|
95
195
|
});
|
|
@@ -97,14 +197,7 @@ export function createStaleRunReconciler(deps) {
|
|
|
97
197
|
await deps.projectionUpdater.rebuildFromEvents([runCompletedEvent]);
|
|
98
198
|
const updatedProjection = await deps.stateStore.readIssueState(projection.workItemKey);
|
|
99
199
|
if (updatedProjection !== null) {
|
|
100
|
-
await
|
|
101
|
-
projection: updatedProjection,
|
|
102
|
-
runId: record.runId,
|
|
103
|
-
statusLabel: 'wake:status.failed',
|
|
104
|
-
stageLabel: stageLabelForStage(updatedProjection.wake.stage),
|
|
105
|
-
workflowLabel: workflowLabelForWorkflowName(workflowNameForProjection(updatedProjection, deps.config)),
|
|
106
|
-
occurredAt: finishedAt,
|
|
107
|
-
}));
|
|
200
|
+
await deliverRecoveredLabels(updatedProjection, record.runId, finishedAt);
|
|
108
201
|
}
|
|
109
202
|
}
|
|
110
203
|
}
|
|
@@ -446,10 +446,18 @@ export function createTickRunner(deps) {
|
|
|
446
446
|
repo: candidate.issue.repo,
|
|
447
447
|
issueNumber: candidate.issue.number,
|
|
448
448
|
action,
|
|
449
|
+
lifecycle: 'CREATED',
|
|
449
450
|
status: 'running',
|
|
450
451
|
startedAt: nowIso,
|
|
452
|
+
routing,
|
|
451
453
|
};
|
|
452
454
|
await deps.stateStore.writeRunRecord(runningRecord);
|
|
455
|
+
async function transitionRunLifecycle(lifecycle) {
|
|
456
|
+
await deps.stateStore.writeRunRecord({
|
|
457
|
+
...(await deps.stateStore.readRunRecord(runId)),
|
|
458
|
+
lifecycle,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
453
461
|
const claimedAt = eventStampNow();
|
|
454
462
|
const claimedEvent = createEventEnvelope({
|
|
455
463
|
eventId: `${runId}-claimed`,
|
|
@@ -473,6 +481,7 @@ export function createTickRunner(deps) {
|
|
|
473
481
|
},
|
|
474
482
|
});
|
|
475
483
|
await deps.stateStore.appendEventEnvelope(claimedEvent);
|
|
484
|
+
await transitionRunLifecycle('CLAIMED');
|
|
476
485
|
await projectionUpdater.rebuildFromEvents([claimedEvent]);
|
|
477
486
|
await deliverOutboundEvent(createLabelsEvent({
|
|
478
487
|
projection: candidate,
|
|
@@ -483,6 +492,7 @@ export function createTickRunner(deps) {
|
|
|
483
492
|
occurredAt: eventStampNow(),
|
|
484
493
|
}));
|
|
485
494
|
try {
|
|
495
|
+
await transitionRunLifecycle('PREPARING');
|
|
486
496
|
const prepareResult = workspaceMode === 'branch'
|
|
487
497
|
? await deps.workspaceManager.prepareWorkspace({
|
|
488
498
|
workId: candidate.workItemKey,
|
|
@@ -499,7 +509,18 @@ export function createTickRunner(deps) {
|
|
|
499
509
|
const upstreamChanges = 'upstreamChanges' in prepareResult && typeof prepareResult.upstreamChanges === 'string'
|
|
500
510
|
? prepareResult.upstreamChanges
|
|
501
511
|
: undefined;
|
|
512
|
+
const preparedRecord = (await deps.stateStore.readRunRecord(runId));
|
|
513
|
+
await deps.stateStore.writeRunRecord({
|
|
514
|
+
...preparedRecord,
|
|
515
|
+
lifecycle: 'PROCESS_STARTING',
|
|
516
|
+
metadata: {
|
|
517
|
+
...preparedRecord.metadata,
|
|
518
|
+
...(workspacePath === undefined ? {} : { workspacePath }),
|
|
519
|
+
workspaceMode,
|
|
520
|
+
},
|
|
521
|
+
});
|
|
502
522
|
const recentEvents = await deps.stateStore.listEventEnvelopesForWorkItem(candidate.workItemKey, 6);
|
|
523
|
+
await transitionRunLifecycle('RUNNING');
|
|
503
524
|
const runnerResult = await deps.runner.run({
|
|
504
525
|
action,
|
|
505
526
|
projection: candidate,
|
|
@@ -571,8 +592,23 @@ export function createTickRunner(deps) {
|
|
|
571
592
|
: { failureClass: runnerResult.failureClass }),
|
|
572
593
|
...(runnerResult.routing === undefined ? {} : { routing: runnerResult.routing }),
|
|
573
594
|
};
|
|
595
|
+
const executionOutcome = runnerResult.failureClass === 'quota'
|
|
596
|
+
? 'QUOTA_EXHAUSTED'
|
|
597
|
+
: runnerResult.failureClass === 'infra'
|
|
598
|
+
? 'PROCESS_FAILED'
|
|
599
|
+
: 'COMPLETED';
|
|
600
|
+
const workflowOutcome = sentinel === 'DONE'
|
|
601
|
+
? 'DONE'
|
|
602
|
+
: sentinel === 'BLOCKED'
|
|
603
|
+
? 'BLOCKED'
|
|
604
|
+
: sentinel === 'AWAITING_APPROVAL'
|
|
605
|
+
? 'AWAITING_APPROVAL'
|
|
606
|
+
: undefined;
|
|
607
|
+
await transitionRunLifecycle('FINALISING');
|
|
608
|
+
const finalisingRecord = (await deps.stateStore.readRunRecord(runId));
|
|
574
609
|
await deps.stateStore.writeRunRecord({
|
|
575
|
-
...
|
|
610
|
+
...finalisingRecord,
|
|
611
|
+
lifecycle: 'TERMINAL',
|
|
576
612
|
status: sentinel === 'DONE'
|
|
577
613
|
? 'completed'
|
|
578
614
|
: sentinel === 'BLOCKED'
|
|
@@ -583,10 +619,15 @@ export function createTickRunner(deps) {
|
|
|
583
619
|
finishedAt,
|
|
584
620
|
sessionId: runnerResult.session_id,
|
|
585
621
|
sentinel,
|
|
622
|
+
executionOutcome,
|
|
623
|
+
...(workflowOutcome !== undefined ? { workflowOutcome } : {}),
|
|
586
624
|
summary: parsedRunnerResult.body,
|
|
587
625
|
...(runnerResult.routing === undefined ? {} : { routing: runnerResult.routing }),
|
|
588
626
|
...(runnerResult.tokenUsage === undefined ? {} : { tokenUsage: runnerResult.tokenUsage }),
|
|
589
|
-
metadata:
|
|
627
|
+
metadata: {
|
|
628
|
+
...finalisingRecord.metadata,
|
|
629
|
+
...resultMetadata,
|
|
630
|
+
},
|
|
590
631
|
});
|
|
591
632
|
const runCompletedEvent = createEventEnvelope({
|
|
592
633
|
eventId: `${runId}-completed`,
|
|
@@ -626,6 +667,8 @@ export function createTickRunner(deps) {
|
|
|
626
667
|
: { handledCommentId: latestHumanCommentId(candidate) }),
|
|
627
668
|
body: parsedRunnerResult.body,
|
|
628
669
|
envelope: parsedRunnerResult.envelope,
|
|
670
|
+
executionOutcome,
|
|
671
|
+
...(workflowOutcome !== undefined ? { workflowOutcome } : {}),
|
|
629
672
|
},
|
|
630
673
|
});
|
|
631
674
|
await deps.stateStore.appendEventEnvelope(runCompletedEvent);
|
|
@@ -668,11 +711,14 @@ export function createTickRunner(deps) {
|
|
|
668
711
|
catch (err) {
|
|
669
712
|
const finishedAt = deps.clock.now().toISOString();
|
|
670
713
|
const sentinel = 'FAILED';
|
|
714
|
+
const failedRecord = (await deps.stateStore.readRunRecord(runId)) ?? runningRecord;
|
|
671
715
|
await deps.stateStore.writeRunRecord({
|
|
672
|
-
...
|
|
716
|
+
...failedRecord,
|
|
717
|
+
lifecycle: 'TERMINAL',
|
|
673
718
|
status: 'failed',
|
|
674
719
|
finishedAt,
|
|
675
720
|
sentinel,
|
|
721
|
+
executionOutcome: 'PROCESS_FAILED',
|
|
676
722
|
summary: err instanceof Error ? err.message : String(err),
|
|
677
723
|
metadata: {
|
|
678
724
|
failureClass: 'infra',
|
|
@@ -699,6 +745,7 @@ export function createTickRunner(deps) {
|
|
|
699
745
|
runId,
|
|
700
746
|
reason: 'runner:infrastructure-error',
|
|
701
747
|
failureClass: 'infra',
|
|
748
|
+
executionOutcome: 'PROCESS_FAILED',
|
|
702
749
|
// Deliberately omit handledCommentId: an infra blip (CLI crash, timeout,
|
|
703
750
|
// network error) never reached the agent, so it isn't an answer to the
|
|
704
751
|
// triggering comment. Leaving it unset lets the next tick retry the same
|
|
@@ -15,6 +15,37 @@ export const wakeArtifactsEnvelopeSchema = z.object({
|
|
|
15
15
|
artifacts: z.array(reportedArtifactSchema).default([]),
|
|
16
16
|
});
|
|
17
17
|
export const runnerSentinelSchema = z.enum(runnerSentinelValues);
|
|
18
|
+
export const executionOutcomeValues = [
|
|
19
|
+
'COMPLETED',
|
|
20
|
+
'STARTUP_FAILED',
|
|
21
|
+
'PROCESS_FAILED',
|
|
22
|
+
'TIMED_OUT',
|
|
23
|
+
'STALLED',
|
|
24
|
+
'CANCELED_BY_RECONCILIATION',
|
|
25
|
+
'CANCELED_BY_OPERATOR',
|
|
26
|
+
'QUOTA_EXHAUSTED',
|
|
27
|
+
'MALFORMED_OUTPUT',
|
|
28
|
+
'SUPERSEDED',
|
|
29
|
+
];
|
|
30
|
+
export const workflowOutcomeValues = [
|
|
31
|
+
'DONE',
|
|
32
|
+
'BLOCKED',
|
|
33
|
+
'AWAITING_APPROVAL',
|
|
34
|
+
'AWAITING_INPUT',
|
|
35
|
+
'CHANGES_REQUESTED',
|
|
36
|
+
];
|
|
37
|
+
export const executionAttemptLifecycleValues = [
|
|
38
|
+
'CREATED',
|
|
39
|
+
'CLAIMED',
|
|
40
|
+
'PREPARING',
|
|
41
|
+
'PROCESS_STARTING',
|
|
42
|
+
'RUNNING',
|
|
43
|
+
'FINALISING',
|
|
44
|
+
'TERMINAL',
|
|
45
|
+
];
|
|
46
|
+
export const executionOutcomeSchema = z.enum(executionOutcomeValues);
|
|
47
|
+
export const workflowOutcomeSchema = z.enum(workflowOutcomeValues);
|
|
48
|
+
export const executionAttemptLifecycleSchema = z.enum(executionAttemptLifecycleValues);
|
|
18
49
|
export const defaultAgentIdentity = 'Wake';
|
|
19
50
|
export const defaultSmokePrompt = `This is ${defaultAgentIdentity}, reply with "hi ${defaultAgentIdentity} only"`;
|
|
20
51
|
const modelOverridesSchema = z
|
|
@@ -270,7 +301,20 @@ const runTokenUsageSchema = z.object({
|
|
|
270
301
|
costUsd: z.number().nonnegative().optional(),
|
|
271
302
|
turns: z.number().nonnegative().optional(),
|
|
272
303
|
});
|
|
273
|
-
|
|
304
|
+
function legacyRunLifecycle(input) {
|
|
305
|
+
if (input.lifecycle !== undefined) {
|
|
306
|
+
return input;
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
...input,
|
|
310
|
+
lifecycle: input.status === 'running' ? 'RUNNING' : 'TERMINAL',
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
export const runRecordSchema = z.preprocess((input) => {
|
|
314
|
+
return input !== null && typeof input === 'object'
|
|
315
|
+
? legacyRunLifecycle(input)
|
|
316
|
+
: input;
|
|
317
|
+
}, z.object({
|
|
274
318
|
schemaVersion: z.literal(1),
|
|
275
319
|
runId: z.string(),
|
|
276
320
|
// The work item this run belongs to. Required: run records are Wake-owned
|
|
@@ -285,16 +329,26 @@ export const runRecordSchema = z.object({
|
|
|
285
329
|
repo: z.string(),
|
|
286
330
|
issueNumber: z.number().int().positive(),
|
|
287
331
|
action: identifierSchema,
|
|
288
|
-
|
|
332
|
+
lifecycle: executionAttemptLifecycleSchema,
|
|
333
|
+
status: z.enum([
|
|
334
|
+
'running',
|
|
335
|
+
'completed',
|
|
336
|
+
'awaiting-approval',
|
|
337
|
+
'blocked',
|
|
338
|
+
'failed',
|
|
339
|
+
'superseded',
|
|
340
|
+
]),
|
|
289
341
|
startedAt: isoTimestampSchema,
|
|
290
342
|
finishedAt: isoTimestampSchema.optional(),
|
|
291
343
|
sessionId: z.string().optional(),
|
|
292
344
|
sentinel: runnerSentinelSchema.optional(),
|
|
345
|
+
executionOutcome: executionOutcomeSchema.optional(),
|
|
346
|
+
workflowOutcome: workflowOutcomeSchema.optional(),
|
|
293
347
|
summary: z.string().optional(),
|
|
294
348
|
routing: runnerRoutingSchema.optional(),
|
|
295
349
|
tokenUsage: runTokenUsageSchema.optional(),
|
|
296
350
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
297
|
-
});
|
|
351
|
+
}));
|
|
298
352
|
const runnerHealthEntrySchema = z.object({
|
|
299
353
|
pausedUntil: isoTimestampSchema.optional(),
|
|
300
354
|
// 'reported' = CLI told us the real reset time; 'estimated' = exponential
|
package/dist/src/version.js
CHANGED