@mycadet/assistant 0.12.10 → 0.13.0
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/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/node_modules/@mycadet/ces-client/src/http-credentials.ts +18 -1
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +0 -20
- package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
- package/openapi.yaml +2 -708
- package/package.json +2 -3
- package/scripts/sync-web-search-catalog.ts +1 -2
- package/src/__tests__/cloud-profile-guard.test.ts +41 -5
- package/src/__tests__/config-schema.test.ts +6 -38
- package/src/__tests__/conversation-queue.test.ts +45 -6
- package/src/__tests__/db-readiness-http-gate.test.ts +0 -6
- package/src/__tests__/persist-onboarding-artifacts.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +0 -51
- package/src/__tests__/provider-cloud-proxy-integration.test.ts +2 -110
- package/src/__tests__/run-due-schedules.test.ts +0 -4
- package/src/__tests__/schedule-store.test.ts +0 -12
- package/src/__tests__/stt-stream-session.test.ts +4 -4
- package/src/__tests__/telephony-tts-guard.test.ts +2 -50
- package/src/__tests__/voice-config-update.test.ts +12 -167
- package/src/__tests__/web-search-catalog-parity.test.ts +0 -2
- package/src/__tests__/workspace-migration-130-speech-mode-to-provider.test.ts +9 -7
- package/src/__tests__/workspace-migration-132-web-search-mode-to-provider.test.ts +6 -6
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -4
- package/src/calls/resolve-call-tts-provider.ts +6 -25
- package/src/calls/telephony-credential-preflight.ts +0 -10
- package/src/calls/telephony-tts-capability.ts +1 -17
- package/src/cli/commands/tts.ts +1 -2
- package/src/cli/index.help.ts +0 -2
- package/src/cli/program.ts +0 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +4 -42
- package/src/config/schemas/__tests__/stt.test.ts +10 -14
- package/src/config/schemas/llm.ts +3 -5
- package/src/config/schemas/services.ts +5 -6
- package/src/config/schemas/stt.ts +1 -3
- package/src/config/schemas/tts.ts +1 -30
- package/src/daemon/daemon-readiness.ts +4 -29
- package/src/daemon/lifecycle.ts +0 -31
- package/src/daemon/providers-setup.ts +0 -8
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -66
- package/src/ipc/__tests__/db-readiness-ipc-gate.test.ts +1 -9
- package/src/live-voice/__tests__/live-voice-credential-preflight.test.ts +0 -44
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +4 -6
- package/src/live-voice/__tests__/live-voice-stt.test.ts +1 -1
- package/src/live-voice/live-voice-credential-preflight.ts +9 -33
- package/src/live-voice/live-voice-session.ts +10 -6
- package/src/live-voice/live-voice-tts.ts +1 -8
- package/src/plugins/defaults/memory/worker.ts +3 -3
- package/src/prompts/persona-resolver.ts +0 -35
- package/src/providers/__tests__/search-provider-catalog.test.ts +5 -8
- package/src/providers/__tests__/voice-error-copy.test.ts +9 -10
- package/src/providers/search-provider-catalog.ts +7 -39
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +3 -47
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +6 -372
- package/src/providers/speech-to-text/provider-catalog.ts +2 -73
- package/src/providers/speech-to-text/resolve.ts +3 -120
- package/src/runtime/http-router-types.ts +1 -3
- package/src/runtime/http-server.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +13 -11
- package/src/runtime/routes/__tests__/tts-routes.test.ts +2 -6
- package/src/runtime/routes/conversation-query-routes.ts +0 -96
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +7 -7
- package/src/runtime/routes/index.ts +0 -4
- package/src/runtime/routes/live-voice-routes.test.ts +4 -11
- package/src/runtime/routes/live-voice-routes.ts +8 -11
- package/src/runtime/routes/secret-routes.ts +0 -6
- package/src/runtime/routes/tts-routes.ts +0 -38
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1 -7
- package/src/schedule/__tests__/worker-mcp-readiness.test.ts +0 -4
- package/src/schedule/schedule-store.ts +0 -7
- package/src/schedule/scheduler.ts +0 -5
- package/src/schedule/worker.ts +1 -1
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +2 -10
- package/src/stt/daemon-batch-transcriber.ts +4 -38
- package/src/stt/types.ts +1 -9
- package/src/tools/network/__tests__/web-search.test.ts +3 -250
- package/src/tools/network/web-search.ts +4 -165
- package/src/tts/__tests__/provider-resolution.test.ts +0 -5
- package/src/tts/provider-catalog.ts +0 -4
- package/src/tts/types.ts +0 -1
- package/src/__tests__/cloud-speech-defaults.test.ts +0 -330
- package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1708
- package/src/__tests__/migration-export-http.test.ts +0 -557
- package/src/__tests__/migration-export-streaming.test.ts +0 -350
- package/src/__tests__/migration-export-to-gcs.test.ts +0 -706
- package/src/__tests__/migration-import-commit-http.test.ts +0 -1176
- package/src/__tests__/migration-import-from-gcs.test.ts +0 -534
- package/src/__tests__/migration-import-from-path.test.ts +0 -324
- package/src/__tests__/migration-import-from-url.test.ts +0 -588
- package/src/__tests__/migration-import-preflight-http.test.ts +0 -739
- package/src/__tests__/migration-jobs-status.test.ts +0 -139
- package/src/__tests__/migration-parity-persistence.test.ts +0 -1891
- package/src/__tests__/migration-transport.test.ts +0 -1089
- package/src/__tests__/migration-validate-http.test.ts +0 -634
- package/src/__tests__/migration-wizard.test.ts +0 -1395
- package/src/__tests__/rebind-secrets-screen.test.ts +0 -876
- package/src/__tests__/sanitize-config-for-transfer.test.ts +0 -259
- package/src/__tests__/transfer-progress-screen.test.ts +0 -1217
- package/src/__tests__/validation-results-screen.test.ts +0 -1130
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +0 -147
- package/src/background-wake/background-wake-routes.test.ts +0 -906
- package/src/background-wake/cloud-client.test.ts +0 -328
- package/src/background-wake/cloud-client.ts +0 -196
- package/src/background-wake/next-wake.test.ts +0 -335
- package/src/background-wake/next-wake.ts +0 -188
- package/src/background-wake/publisher.ts +0 -93
- package/src/background-wake/wake-intent-hooks.test.ts +0 -234
- package/src/cli/commands/__tests__/backup.test.ts +0 -363
- package/src/cli/commands/backup.help.ts +0 -180
- package/src/cli/commands/backup.ts +0 -398
- package/src/cloud/cloud-speech.test.ts +0 -335
- package/src/cloud/cloud-speech.ts +0 -300
- package/src/config/cloud-speech-defaults.ts +0 -300
- package/src/config/sanitize-for-transfer.ts +0 -111
- package/src/live-voice/__tests__/live-voice-cloud-speech-fallback.test.ts +0 -250
- package/src/providers/speech-to-text/__tests__/cadet-cloud-realtime.test.ts +0 -457
- package/src/providers/speech-to-text/__tests__/cadet-cloud.test.ts +0 -271
- package/src/providers/speech-to-text/__tests__/cadet-speech-relay-connection.test.ts +0 -126
- package/src/providers/speech-to-text/cadet-cloud-flux-realtime.ts +0 -309
- package/src/providers/speech-to-text/cadet-cloud-realtime.ts +0 -315
- package/src/providers/speech-to-text/cadet-cloud.ts +0 -108
- package/src/providers/speech-to-text/cadet-speech-relay-connection.ts +0 -176
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +0 -254
- package/src/runtime/migrations/__tests__/job-registry.test.ts +0 -346
- package/src/runtime/migrations/__tests__/origin-mode.test.ts +0 -62
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +0 -184
- package/src/runtime/migrations/__tests__/staged-import-path.test.ts +0 -110
- package/src/runtime/migrations/__tests__/v1-test-helpers.ts +0 -112
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +0 -285
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +0 -126
- package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +0 -253
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +0 -208
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +0 -485
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +0 -349
- package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +0 -196
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +0 -551
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +0 -428
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +0 -220
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +0 -1823
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +0 -598
- package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +0 -463
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -538
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +0 -515
- package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +0 -453
- package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +0 -319
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +0 -240
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +0 -473
- package/src/runtime/migrations/gcs-signed-url.ts +0 -190
- package/src/runtime/migrations/job-registry.ts +0 -297
- package/src/runtime/migrations/migration-transport.ts +0 -612
- package/src/runtime/migrations/migration-wizard.ts +0 -747
- package/src/runtime/migrations/origin-mode.ts +0 -42
- package/src/runtime/migrations/rebind-secrets-screen.ts +0 -412
- package/src/runtime/migrations/staged-import-path.ts +0 -116
- package/src/runtime/migrations/transfer-progress-screen.ts +0 -323
- package/src/runtime/migrations/validation-results-screen.ts +0 -469
- package/src/runtime/migrations/vbundle-builder.ts +0 -1382
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -405
- package/src/runtime/migrations/vbundle-import-policy.ts +0 -241
- package/src/runtime/migrations/vbundle-importer.ts +0 -826
- package/src/runtime/migrations/vbundle-metadata-merge.ts +0 -113
- package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -2844
- package/src/runtime/migrations/vbundle-streaming-validator.ts +0 -426
- package/src/runtime/migrations/vbundle-tar-stream.ts +0 -244
- package/src/runtime/migrations/vbundle-validator.ts +0 -724
- package/src/runtime/routes/__tests__/migration-cadet-metadata-reconcile.test.ts +0 -227
- package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +0 -54
- package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +0 -70
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +0 -257
- package/src/runtime/routes/background-wake-routes.ts +0 -387
- package/src/runtime/routes/migration-routes.ts +0 -2584
- package/src/tools/network/__tests__/cloud-search-proxy.test.ts +0 -282
- package/src/tools/network/cloud-search-proxy.ts +0 -211
- package/src/tts/__tests__/cadet-provider.test.ts +0 -253
- package/src/tts/providers/cadet-provider.ts +0 -244
- package/src/types/tar-stream.d.ts +0 -66
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
type BackgroundWakeIntent,
|
|
5
|
-
computeNextBackgroundWakeIntent,
|
|
6
|
-
} from "../../background-wake/next-wake.js";
|
|
7
|
-
import {
|
|
8
|
-
getHeartbeatService,
|
|
9
|
-
type HeartbeatService,
|
|
10
|
-
} from "../../heartbeat/heartbeat-service.js";
|
|
11
|
-
import {
|
|
12
|
-
getScheduler,
|
|
13
|
-
type SchedulerHandle,
|
|
14
|
-
} from "../../schedule/scheduler.js";
|
|
15
|
-
import { getLogger } from "../../util/logger.js";
|
|
16
|
-
import { GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
|
|
17
|
-
import { BadRequestError, ServiceUnavailableError } from "./errors.js";
|
|
18
|
-
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
19
|
-
|
|
20
|
-
/** The scheduler and heartbeat surface the drain-due lease needs. */
|
|
21
|
-
type BackgroundWakeRuntime = {
|
|
22
|
-
scheduler: Pick<SchedulerHandle, "runOnce" | "runDueWorkOnce">;
|
|
23
|
-
heartbeat: Pick<HeartbeatService, "nextRunAt" | "runCloudWakeIfDue">;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const PREPARE_SLEEP_DEFER_WINDOW_MS = 60_000;
|
|
27
|
-
const HEARTBEAT_DUE_TOLERANCE_MS = 1_000;
|
|
28
|
-
const MIN_DRAIN_START_BUDGET_MS = 5_000;
|
|
29
|
-
const LEASE_RENEW_INTERVAL_MS = 120_000;
|
|
30
|
-
const log = getLogger("background-wake-routes");
|
|
31
|
-
|
|
32
|
-
class DrainDeadlineElapsedError extends Error {
|
|
33
|
-
constructor() {
|
|
34
|
-
super("background wake lease deadline elapsed before starting work");
|
|
35
|
-
this.name = "DrainDeadlineElapsedError";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
type RenewWakeLease = (leaseId: string) => Promise<unknown>;
|
|
40
|
-
type CompleteWakeLease = (args: {
|
|
41
|
-
leaseId: string;
|
|
42
|
-
status: "completed" | "failed" | "expired";
|
|
43
|
-
error?: string;
|
|
44
|
-
nextIntent?: BackgroundWakeIntent | null;
|
|
45
|
-
}) => Promise<unknown>;
|
|
46
|
-
|
|
47
|
-
let computeWakeIntent = computeNextBackgroundWakeIntent;
|
|
48
|
-
let renewWakeLease: RenewWakeLease = defaultRenewWakeLease;
|
|
49
|
-
let completeWakeLease: CompleteWakeLease = defaultCompleteWakeLease;
|
|
50
|
-
const activeDrainLeases = new Set<string>();
|
|
51
|
-
const activeDrainPromises = new Set<Promise<void>>();
|
|
52
|
-
const timestampInputSchema = z.union([z.number(), z.string()]);
|
|
53
|
-
|
|
54
|
-
const wakeIntentSchema = z
|
|
55
|
-
.object({
|
|
56
|
-
nextWakeAt: z.number(),
|
|
57
|
-
actualNextDueAt: z.number(),
|
|
58
|
-
reason: z.enum(["heartbeat", "schedule", "mixed"]),
|
|
59
|
-
sourceGeneration: z.string(),
|
|
60
|
-
computedAt: z.number(),
|
|
61
|
-
sourcePayload: z.record(z.string(), z.unknown()),
|
|
62
|
-
})
|
|
63
|
-
.nullable();
|
|
64
|
-
|
|
65
|
-
const normalizedDrainDueRequestSchema = z.object({
|
|
66
|
-
leaseId: z.string().min(1),
|
|
67
|
-
reason: z.string().min(1),
|
|
68
|
-
sourceGeneration: z.string().min(1),
|
|
69
|
-
startedAt: z.number(),
|
|
70
|
-
deadlineAt: z.number(),
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const drainDueRequestSchema = z.union([
|
|
74
|
-
z.object({
|
|
75
|
-
leaseId: z.string().min(1),
|
|
76
|
-
reason: z.string().min(1),
|
|
77
|
-
sourceGeneration: z.string().min(1),
|
|
78
|
-
startedAt: timestampInputSchema,
|
|
79
|
-
deadlineAt: timestampInputSchema,
|
|
80
|
-
}),
|
|
81
|
-
z.object({
|
|
82
|
-
lease_id: z.string().min(1),
|
|
83
|
-
reason: z.string().min(1),
|
|
84
|
-
source_generation: z.string().min(1),
|
|
85
|
-
started_at: timestampInputSchema,
|
|
86
|
-
deadline_at: timestampInputSchema,
|
|
87
|
-
}),
|
|
88
|
-
]);
|
|
89
|
-
|
|
90
|
-
function normalizeDrainDueBody(body: Record<string, unknown>) {
|
|
91
|
-
return {
|
|
92
|
-
leaseId: body.leaseId ?? body.lease_id,
|
|
93
|
-
reason: body.reason,
|
|
94
|
-
sourceGeneration: body.sourceGeneration ?? body.source_generation,
|
|
95
|
-
startedAt: normalizeTimestamp(body.startedAt ?? body.started_at),
|
|
96
|
-
deadlineAt: normalizeTimestamp(body.deadlineAt ?? body.deadline_at),
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function normalizeTimestamp(value: unknown): unknown {
|
|
101
|
-
if (typeof value === "number") {
|
|
102
|
-
return value;
|
|
103
|
-
}
|
|
104
|
-
if (typeof value !== "string") {
|
|
105
|
-
return value;
|
|
106
|
-
}
|
|
107
|
-
const numeric = Number(value);
|
|
108
|
-
if (Number.isFinite(numeric)) {
|
|
109
|
-
return numeric;
|
|
110
|
-
}
|
|
111
|
-
const parsed = Date.parse(value);
|
|
112
|
-
return Number.isFinite(parsed) ? parsed : value;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
type DrainDueRequest = z.infer<typeof normalizedDrainDueRequestSchema>;
|
|
116
|
-
function parseDrainDueBody(body: Record<string, unknown>): DrainDueRequest {
|
|
117
|
-
const parsed = normalizedDrainDueRequestSchema.safeParse(
|
|
118
|
-
normalizeDrainDueBody(body),
|
|
119
|
-
);
|
|
120
|
-
if (!parsed.success) {
|
|
121
|
-
throw new BadRequestError(
|
|
122
|
-
"leaseId, reason, sourceGeneration, startedAt, and deadlineAt are required",
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
return parsed.data;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async function defaultRenewWakeLease(leaseId: string): Promise<unknown> {
|
|
129
|
-
const { renewBackgroundWakeLease } =
|
|
130
|
-
await import("../../background-wake/cloud-client.js");
|
|
131
|
-
return renewBackgroundWakeLease(leaseId);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function defaultCompleteWakeLease(
|
|
135
|
-
args: Parameters<CompleteWakeLease>[0],
|
|
136
|
-
): Promise<unknown> {
|
|
137
|
-
const { completeBackgroundWakeLease } =
|
|
138
|
-
await import("../../background-wake/cloud-client.js");
|
|
139
|
-
return completeBackgroundWakeLease(args);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function handleGetIntent() {
|
|
143
|
-
return { intent: computeWakeIntent() };
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function handlePrepareSleep() {
|
|
147
|
-
const now = Date.now();
|
|
148
|
-
const intent = computeWakeIntent(now);
|
|
149
|
-
return {
|
|
150
|
-
intent,
|
|
151
|
-
deferSleep:
|
|
152
|
-
intent != null &&
|
|
153
|
-
intent.nextWakeAt <= now + PREPARE_SLEEP_DEFER_WINDOW_MS,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async function handleDrainDue(body: Record<string, unknown>) {
|
|
158
|
-
const request = parseDrainDueBody(body);
|
|
159
|
-
const scheduler = getScheduler();
|
|
160
|
-
const heartbeat = getHeartbeatService();
|
|
161
|
-
if (!scheduler || !heartbeat) {
|
|
162
|
-
throw new ServiceUnavailableError(
|
|
163
|
-
"Background wake runtime is not available",
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
const runtime: BackgroundWakeRuntime = { scheduler, heartbeat };
|
|
167
|
-
|
|
168
|
-
if (!activeDrainLeases.has(request.leaseId)) {
|
|
169
|
-
activeDrainLeases.add(request.leaseId);
|
|
170
|
-
const drainPromise = runDrainDueLease(request, runtime);
|
|
171
|
-
activeDrainPromises.add(drainPromise);
|
|
172
|
-
void drainPromise.finally(() => activeDrainPromises.delete(drainPromise));
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
accepted: true,
|
|
177
|
-
leaseId: request.leaseId,
|
|
178
|
-
reason: request.reason,
|
|
179
|
-
sourceGeneration: request.sourceGeneration,
|
|
180
|
-
startedAt: request.startedAt,
|
|
181
|
-
deadlineAt: request.deadlineAt,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async function runDrainDueLease(
|
|
186
|
-
request: DrainDueRequest,
|
|
187
|
-
runtime: BackgroundWakeRuntime,
|
|
188
|
-
): Promise<void> {
|
|
189
|
-
let renewTimer: ReturnType<typeof setInterval> | undefined;
|
|
190
|
-
try {
|
|
191
|
-
renewTimer = setInterval(() => {
|
|
192
|
-
void renewWakeLease(request.leaseId).catch(() => {});
|
|
193
|
-
}, LEASE_RENEW_INTERVAL_MS);
|
|
194
|
-
renewTimer.unref?.();
|
|
195
|
-
|
|
196
|
-
assertDrainCanStart(request.deadlineAt);
|
|
197
|
-
const result = await performDrainDue(request, runtime);
|
|
198
|
-
await reportLeaseCompletion({
|
|
199
|
-
leaseId: request.leaseId,
|
|
200
|
-
status: "completed",
|
|
201
|
-
nextIntent: result.nextIntent,
|
|
202
|
-
});
|
|
203
|
-
} catch (error) {
|
|
204
|
-
await reportLeaseCompletion({
|
|
205
|
-
leaseId: request.leaseId,
|
|
206
|
-
status: completionStatusForError(error),
|
|
207
|
-
error: error instanceof Error ? error.message : String(error),
|
|
208
|
-
nextIntent: computeWakeIntent(),
|
|
209
|
-
});
|
|
210
|
-
} finally {
|
|
211
|
-
if (renewTimer) {
|
|
212
|
-
clearInterval(renewTimer);
|
|
213
|
-
}
|
|
214
|
-
activeDrainLeases.delete(request.leaseId);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
async function reportLeaseCompletion(
|
|
219
|
-
args: Parameters<CompleteWakeLease>[0],
|
|
220
|
-
): Promise<void> {
|
|
221
|
-
try {
|
|
222
|
-
await completeWakeLease(args);
|
|
223
|
-
} catch (err) {
|
|
224
|
-
log.warn(
|
|
225
|
-
{ err, leaseId: args.leaseId, status: args.status },
|
|
226
|
-
"Failed to report background wake lease completion",
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
async function performDrainDue(
|
|
232
|
-
request: DrainDueRequest,
|
|
233
|
-
runtime: BackgroundWakeRuntime,
|
|
234
|
-
) {
|
|
235
|
-
const now = Date.now();
|
|
236
|
-
const currentIntent = computeWakeIntent(now);
|
|
237
|
-
const heartbeatDue = isHeartbeatTimerDue(runtime.heartbeat.nextRunAt, now);
|
|
238
|
-
const schedulerDue =
|
|
239
|
-
heartbeatDue ||
|
|
240
|
-
reasonIncludesSource(request.reason, "schedule") ||
|
|
241
|
-
intentHasDueSource(currentIntent, "schedule", now);
|
|
242
|
-
|
|
243
|
-
if (heartbeatDue) {
|
|
244
|
-
if (hasStartBudget(request.deadlineAt)) {
|
|
245
|
-
await runtime.heartbeat.runCloudWakeIfDue({
|
|
246
|
-
now,
|
|
247
|
-
toleranceMs: HEARTBEAT_DUE_TOLERANCE_MS,
|
|
248
|
-
scheduledFor: request.startedAt,
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (schedulerDue) {
|
|
254
|
-
await runtime.scheduler.runDueWorkOnce({
|
|
255
|
-
deadlineAt: request.deadlineAt,
|
|
256
|
-
minStartBudgetMs: MIN_DRAIN_START_BUDGET_MS,
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const nextIntent = computeWakeIntent();
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
nextIntent,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function assertDrainCanStart(deadlineAt: number): void {
|
|
268
|
-
if (deadlineAt <= Date.now()) {
|
|
269
|
-
throw new DrainDeadlineElapsedError();
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function completionStatusForError(error: unknown): "failed" | "expired" {
|
|
274
|
-
return error instanceof DrainDeadlineElapsedError ? "expired" : "failed";
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function reasonIncludesSource(
|
|
278
|
-
reason: string,
|
|
279
|
-
source: "heartbeat" | "schedule",
|
|
280
|
-
): boolean {
|
|
281
|
-
return reason === "mixed" || reason === source;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function isHeartbeatTimerDue(nextRunAt: number | null, now: number): boolean {
|
|
285
|
-
return nextRunAt != null && nextRunAt <= now + HEARTBEAT_DUE_TOLERANCE_MS;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function intentHasDueSource(
|
|
289
|
-
intent: BackgroundWakeIntent | null,
|
|
290
|
-
source: "heartbeat" | "schedule",
|
|
291
|
-
now: number,
|
|
292
|
-
): boolean {
|
|
293
|
-
return (
|
|
294
|
-
intent != null &&
|
|
295
|
-
intent.actualNextDueAt <= now + HEARTBEAT_DUE_TOLERANCE_MS &&
|
|
296
|
-
reasonIncludesSource(intent.reason, source)
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function hasStartBudget(deadlineAt: number): boolean {
|
|
301
|
-
return deadlineAt - Date.now() >= MIN_DRAIN_START_BUDGET_MS;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/** @internal Test helper for route-only tests. */
|
|
305
|
-
export function setBackgroundWakeIntentComputerForTest(
|
|
306
|
-
nextCompute: typeof computeNextBackgroundWakeIntent | null,
|
|
307
|
-
): void {
|
|
308
|
-
computeWakeIntent = nextCompute ?? computeNextBackgroundWakeIntent;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/** @internal Test helper for route-only tests. */
|
|
312
|
-
export function setBackgroundWakeLeaseClientForTest(
|
|
313
|
-
nextClient: {
|
|
314
|
-
renew: RenewWakeLease;
|
|
315
|
-
complete: CompleteWakeLease;
|
|
316
|
-
} | null,
|
|
317
|
-
): void {
|
|
318
|
-
renewWakeLease = nextClient?.renew ?? defaultRenewWakeLease;
|
|
319
|
-
completeWakeLease = nextClient?.complete ?? defaultCompleteWakeLease;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/** @internal Test helper for route-only tests. */
|
|
323
|
-
export async function flushBackgroundWakeDrainsForTest(): Promise<void> {
|
|
324
|
-
while (activeDrainPromises.size > 0) {
|
|
325
|
-
await Promise.allSettled([...activeDrainPromises]);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
export const ROUTES: RouteDefinition[] = [
|
|
330
|
-
{
|
|
331
|
-
operationId: "getBackgroundWakeIntent",
|
|
332
|
-
endpoint: "background-wake/intent",
|
|
333
|
-
method: "GET",
|
|
334
|
-
policy: {
|
|
335
|
-
requiredScopes: ["internal.write"],
|
|
336
|
-
allowedPrincipalTypes: GATEWAY_PRINCIPALS,
|
|
337
|
-
},
|
|
338
|
-
summary: "Get background wake intent",
|
|
339
|
-
description: "Return the current computed background wake intent.",
|
|
340
|
-
tags: ["background-wake"],
|
|
341
|
-
responseBody: z.object({
|
|
342
|
-
intent: wakeIntentSchema,
|
|
343
|
-
}),
|
|
344
|
-
handler: () => handleGetIntent(),
|
|
345
|
-
},
|
|
346
|
-
{
|
|
347
|
-
operationId: "prepareBackgroundWakeSleep",
|
|
348
|
-
endpoint: "background-wake/prepare-sleep",
|
|
349
|
-
method: "POST",
|
|
350
|
-
policy: {
|
|
351
|
-
requiredScopes: ["internal.write"],
|
|
352
|
-
allowedPrincipalTypes: GATEWAY_PRINCIPALS,
|
|
353
|
-
},
|
|
354
|
-
summary: "Prepare for assistant sleep",
|
|
355
|
-
description:
|
|
356
|
-
"Return the current background wake intent and whether sleep should be deferred.",
|
|
357
|
-
tags: ["background-wake"],
|
|
358
|
-
responseBody: z.object({
|
|
359
|
-
intent: wakeIntentSchema,
|
|
360
|
-
deferSleep: z.boolean(),
|
|
361
|
-
}),
|
|
362
|
-
handler: () => handlePrepareSleep(),
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
operationId: "drainDueBackgroundWake",
|
|
366
|
-
endpoint: "background-wake/drain-due",
|
|
367
|
-
method: "POST",
|
|
368
|
-
policy: {
|
|
369
|
-
requiredScopes: ["internal.write"],
|
|
370
|
-
allowedPrincipalTypes: GATEWAY_PRINCIPALS,
|
|
371
|
-
},
|
|
372
|
-
summary: "Drain due background wake work",
|
|
373
|
-
description:
|
|
374
|
-
"Run due heartbeat and scheduler work for a background wake lease.",
|
|
375
|
-
tags: ["background-wake"],
|
|
376
|
-
requestBody: drainDueRequestSchema,
|
|
377
|
-
responseBody: z.object({
|
|
378
|
-
accepted: z.boolean(),
|
|
379
|
-
leaseId: z.string(),
|
|
380
|
-
reason: z.string(),
|
|
381
|
-
sourceGeneration: z.string(),
|
|
382
|
-
startedAt: z.number(),
|
|
383
|
-
deadlineAt: z.number(),
|
|
384
|
-
}),
|
|
385
|
-
handler: ({ body }: RouteHandlerArgs) => handleDrainDue(body ?? {}),
|
|
386
|
-
},
|
|
387
|
-
];
|