@beeos-ai/device-agent 0.15.0 → 0.16.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/README.md +35 -0
- package/dist/adapters/agent-gateway-client.d.ts +6 -68
- package/dist/adapters/agent-gateway-client.js +7 -67
- package/dist/adapters/agent-gateway-client.js.map +1 -1
- package/dist/adapters/device-compat.d.ts +1 -2
- package/dist/adapters/device-compat.js +2 -52
- package/dist/adapters/device-compat.js.map +1 -1
- package/dist/agent-runtime/action-guard.js +92 -17
- package/dist/agent-runtime/action-guard.js.map +1 -1
- package/dist/agent-runtime/adapters/base.d.ts +13 -9
- package/dist/agent-runtime/adapters/base.js.map +1 -1
- package/dist/agent-runtime/adapters/general-flash.d.ts +3 -3
- package/dist/agent-runtime/adapters/general-flash.js +2 -2
- package/dist/agent-runtime/adapters/general-flash.js.map +1 -1
- package/dist/agent-runtime/adapters/native-tools.d.ts +56 -0
- package/dist/agent-runtime/adapters/native-tools.js +416 -0
- package/dist/agent-runtime/adapters/native-tools.js.map +1 -0
- package/dist/agent-runtime/adapters/openai.d.ts +1 -2
- package/dist/agent-runtime/adapters/openai.js +5 -5
- package/dist/agent-runtime/adapters/openai.js.map +1 -1
- package/dist/agent-runtime/adapters/pro.d.ts +7 -37
- package/dist/agent-runtime/adapters/pro.js +7 -249
- package/dist/agent-runtime/adapters/pro.js.map +1 -1
- package/dist/agent-runtime/advanced/app-card.js +9 -2
- package/dist/agent-runtime/advanced/app-card.js.map +1 -1
- package/dist/agent-runtime/advanced/subgoal-verifier.d.ts +1 -0
- package/dist/agent-runtime/advanced/subgoal-verifier.js +30 -36
- package/dist/agent-runtime/advanced/subgoal-verifier.js.map +1 -1
- package/dist/agent-runtime/advanced/task-state.js +4 -9
- package/dist/agent-runtime/advanced/task-state.js.map +1 -1
- package/dist/agent-runtime/agent-runner.d.ts +2 -11
- package/dist/agent-runtime/agent-runner.js +69 -34
- package/dist/agent-runtime/agent-runner.js.map +1 -1
- package/dist/agent-runtime/core.d.ts +24 -46
- package/dist/agent-runtime/core.js +161 -228
- package/dist/agent-runtime/core.js.map +1 -1
- package/dist/agent-runtime/execution-checks.d.ts +40 -0
- package/dist/agent-runtime/execution-checks.js +133 -0
- package/dist/agent-runtime/execution-checks.js.map +1 -0
- package/dist/agent-runtime/execution-mode.d.ts +1 -8
- package/dist/agent-runtime/execution-mode.js +10 -30
- package/dist/agent-runtime/execution-mode.js.map +1 -1
- package/dist/agent-runtime/feedback.d.ts +4 -28
- package/dist/agent-runtime/feedback.js +4 -26
- package/dist/agent-runtime/feedback.js.map +1 -1
- package/dist/agent-runtime/form-prerequisites.d.ts +3 -0
- package/dist/agent-runtime/form-prerequisites.js +52 -0
- package/dist/agent-runtime/form-prerequisites.js.map +1 -0
- package/dist/agent-runtime/index.d.ts +2 -8
- package/dist/agent-runtime/index.js +2 -8
- package/dist/agent-runtime/index.js.map +1 -1
- package/dist/agent-runtime/{modes/orchestrator → knowledge}/app-guide.d.ts +1 -1
- package/dist/agent-runtime/{modes/orchestrator → knowledge}/app-guide.js +1 -1
- package/dist/agent-runtime/knowledge/app-guide.js.map +1 -0
- package/dist/agent-runtime/models/base.d.ts +3 -1
- package/dist/agent-runtime/models/client.js +30 -9
- package/dist/agent-runtime/models/client.js.map +1 -1
- package/dist/agent-runtime/models/reasoning.d.ts +10 -0
- package/dist/agent-runtime/models/reasoning.js +29 -0
- package/dist/agent-runtime/models/reasoning.js.map +1 -0
- package/dist/agent-runtime/models/runtime-registry.d.ts +2 -1
- package/dist/agent-runtime/models/runtime-registry.js +4 -2
- package/dist/agent-runtime/models/runtime-registry.js.map +1 -1
- package/dist/agent-runtime/models/vlm/autoglm.js +6 -2
- package/dist/agent-runtime/models/vlm/autoglm.js.map +1 -1
- package/dist/agent-runtime/models/vlm/base.js +1 -12
- package/dist/agent-runtime/models/vlm/base.js.map +1 -1
- package/dist/agent-runtime/models/vlm/index.d.ts +0 -4
- package/dist/agent-runtime/models/vlm/index.js +0 -4
- package/dist/agent-runtime/models/vlm/index.js.map +1 -1
- package/dist/agent-runtime/modes/hybrid/index.d.ts +2 -12
- package/dist/agent-runtime/modes/hybrid/index.js +10 -27
- package/dist/agent-runtime/modes/hybrid/index.js.map +1 -1
- package/dist/agent-runtime/modes/hybrid/planner/base.d.ts +4 -44
- package/dist/agent-runtime/modes/hybrid/planner/base.js +3 -21
- package/dist/agent-runtime/modes/hybrid/planner/base.js.map +1 -1
- package/dist/agent-runtime/modes/hybrid/planner/llm-planner.d.ts +1 -1
- package/dist/agent-runtime/modes/hybrid/planner/llm-planner.js +9 -6
- package/dist/agent-runtime/modes/hybrid/planner/llm-planner.js.map +1 -1
- package/dist/agent-runtime/modes/hybrid/planner/prompts.d.ts +1 -17
- package/dist/agent-runtime/modes/hybrid/planner/prompts.js +65 -160
- package/dist/agent-runtime/modes/hybrid/planner/prompts.js.map +1 -1
- package/dist/agent-runtime/plan-executor.d.ts +13 -14
- package/dist/agent-runtime/plan-executor.js +236 -122
- package/dist/agent-runtime/plan-executor.js.map +1 -1
- package/dist/agent-runtime/pro-memory.js +1 -1
- package/dist/agent-runtime/pro-memory.js.map +1 -1
- package/dist/agent-runtime/pro-operator.js +8 -2
- package/dist/agent-runtime/pro-operator.js.map +1 -1
- package/dist/agent-runtime/pro-perception.d.ts +1 -0
- package/dist/agent-runtime/pro-perception.js +4 -2
- package/dist/agent-runtime/pro-perception.js.map +1 -1
- package/dist/agent-runtime/pro-target-check.d.ts +2 -2
- package/dist/agent-runtime/pro-target-check.js +102 -17
- package/dist/agent-runtime/pro-target-check.js.map +1 -1
- package/dist/agent-runtime/prompts/operator.d.ts +2 -0
- package/dist/agent-runtime/prompts/operator.js +69 -0
- package/dist/agent-runtime/prompts/operator.js.map +1 -0
- package/dist/agent-runtime/prompts/verification.d.ts +2 -0
- package/dist/agent-runtime/prompts/verification.js +52 -0
- package/dist/agent-runtime/prompts/verification.js.map +1 -0
- package/dist/agent-runtime/skills/replay.d.ts +4 -14
- package/dist/agent-runtime/skills/replay.js +46 -27
- package/dist/agent-runtime/skills/replay.js.map +1 -1
- package/dist/agent-runtime/tool-batch.d.ts +10 -0
- package/dist/agent-runtime/tool-batch.js +51 -0
- package/dist/agent-runtime/tool-batch.js.map +1 -0
- package/dist/agent-runtime/visual-state.d.ts +2 -0
- package/dist/agent-runtime/visual-state.js +24 -0
- package/dist/agent-runtime/visual-state.js.map +1 -0
- package/dist/agent-runtime/vlm.d.ts +1 -1
- package/dist/agent-runtime/vlm.js +1 -1
- package/dist/api.js +3 -1
- package/dist/api.js.map +1 -1
- package/dist/app/agent-host.js +6 -2
- package/dist/app/agent-host.js.map +1 -1
- package/dist/app/multi-device-host.js +4 -5
- package/dist/app/multi-device-host.js.map +1 -1
- package/dist/app/session-manager.d.ts +3 -45
- package/dist/app/session-manager.js +8 -9
- package/dist/app/session-manager.js.map +1 -1
- package/dist/bootstrap.js +25 -84
- package/dist/bootstrap.js.map +1 -1
- package/dist/config/loader.js +12 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +7 -2
- package/dist/config/schema.js +8 -5
- package/dist/config/schema.js.map +1 -1
- package/dist/core/abortable-read.d.ts +1 -0
- package/dist/core/abortable-read.js +24 -0
- package/dist/core/abortable-read.js.map +1 -0
- package/dist/device/ui-xml.d.ts +4 -0
- package/dist/device/ui-xml.js +35 -13
- package/dist/device/ui-xml.js.map +1 -1
- package/dist/fleet/adapters/process/node-process-manager.d.ts +2 -1
- package/dist/fleet/adapters/process/node-process-manager.js +11 -19
- package/dist/fleet/adapters/process/node-process-manager.js.map +1 -1
- package/dist/inbound/acp/handlers/models.js +11 -8
- package/dist/inbound/acp/handlers/models.js.map +1 -1
- package/dist/inbound/acp/handlers/session.d.ts +1 -4
- package/dist/inbound/acp/handlers/session.js +3 -6
- package/dist/inbound/acp/handlers/session.js.map +1 -1
- package/dist/inbound/bma/agent.d.ts +47 -0
- package/dist/inbound/bma/agent.js +130 -0
- package/dist/inbound/bma/agent.js.map +1 -0
- package/dist/inbound/bma/command.d.ts +15 -0
- package/dist/inbound/bma/command.js +98 -0
- package/dist/inbound/bma/command.js.map +1 -0
- package/dist/inbound/bma/model-proxy.d.ts +22 -0
- package/dist/inbound/bma/model-proxy.js +130 -0
- package/dist/inbound/bma/model-proxy.js.map +1 -0
- package/dist/inbound/cli/main.js +12 -2
- package/dist/inbound/cli/main.js.map +1 -1
- package/dist/inbound/cli/run-artifacts.js +16 -7
- package/dist/inbound/cli/run-artifacts.js.map +1 -1
- package/dist/inbound/cli/run-command.d.ts +7 -0
- package/dist/inbound/cli/run-command.js +37 -29
- package/dist/inbound/cli/run-command.js.map +1 -1
- package/dist/index.d.ts +1 -10
- package/dist/index.js +1 -6
- package/dist/index.js.map +1 -1
- package/dist/mcp-client/autospawn.d.ts +7 -9
- package/dist/mcp-client/autospawn.js +16 -12
- package/dist/mcp-client/autospawn.js.map +1 -1
- package/dist/mcp-client/client.d.ts +6 -3
- package/dist/mcp-client/client.js +31 -13
- package/dist/mcp-client/client.js.map +1 -1
- package/dist/mcp-client/runtime.d.ts +5 -2
- package/dist/plugins/agent-sync/index.d.ts +32 -17
- package/dist/plugins/agent-sync/index.js +223 -232
- package/dist/plugins/agent-sync/index.js.map +1 -1
- package/dist/plugins/message-bus/inbound.d.ts +0 -32
- package/dist/plugins/message-bus/inbound.js +3 -0
- package/dist/plugins/message-bus/inbound.js.map +1 -1
- package/dist/plugins/message-bus/ms-channel-notifier.d.ts +1 -0
- package/dist/plugins/message-bus/ms-channel-notifier.js +16 -8
- package/dist/plugins/message-bus/ms-channel-notifier.js.map +1 -1
- package/dist/plugins/native-chat/deadline.d.ts +2 -0
- package/dist/plugins/native-chat/deadline.js +16 -0
- package/dist/plugins/native-chat/deadline.js.map +1 -0
- package/dist/plugins/native-chat/index.d.ts +46 -0
- package/dist/plugins/native-chat/index.js +331 -0
- package/dist/plugins/native-chat/index.js.map +1 -0
- package/dist/plugins/native-chat/reply-stream.d.ts +50 -0
- package/dist/plugins/native-chat/reply-stream.js +81 -0
- package/dist/plugins/native-chat/reply-stream.js.map +1 -0
- package/dist/plugins/native-chat/session-notifier.d.ts +16 -0
- package/dist/plugins/native-chat/session-notifier.js +130 -0
- package/dist/plugins/native-chat/session-notifier.js.map +1 -0
- package/dist/ports/agent-core.d.ts +2 -0
- package/dist/ports/agent-host.d.ts +2 -0
- package/dist/ports/agent-host.js +0 -21
- package/dist/ports/agent-host.js.map +1 -1
- package/dist/ports/device.d.ts +0 -1
- package/dist/ports/device.js +0 -1
- package/dist/ports/device.js.map +1 -1
- package/dist/runtime-authority/device-prompt.gen.d.ts +54 -0
- package/dist/runtime-authority/device-prompt.gen.js +19 -0
- package/dist/runtime-authority/device-prompt.gen.js.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +5 -3
- package/dist/adapters/device-desktop.d.ts +0 -49
- package/dist/adapters/device-desktop.js +0 -132
- package/dist/adapters/device-desktop.js.map +0 -1
- package/dist/agent-runtime/factory.d.ts +0 -24
- package/dist/agent-runtime/factory.js +0 -25
- package/dist/agent-runtime/factory.js.map +0 -1
- package/dist/agent-runtime/models/vlm/guiowl.d.ts +0 -1
- package/dist/agent-runtime/models/vlm/guiowl.js +0 -249
- package/dist/agent-runtime/models/vlm/guiowl.js.map +0 -1
- package/dist/agent-runtime/models/vlm/maiui.d.ts +0 -1
- package/dist/agent-runtime/models/vlm/maiui.js +0 -142
- package/dist/agent-runtime/models/vlm/maiui.js.map +0 -1
- package/dist/agent-runtime/models/vlm/qwen38.d.ts +0 -1
- package/dist/agent-runtime/models/vlm/qwen38.js +0 -241
- package/dist/agent-runtime/models/vlm/qwen38.js.map +0 -1
- package/dist/agent-runtime/models/vlm/qwenvl.d.ts +0 -1
- package/dist/agent-runtime/models/vlm/qwenvl.js +0 -202
- package/dist/agent-runtime/models/vlm/qwenvl.js.map +0 -1
- package/dist/agent-runtime/modes/orchestrator/app-guide.js.map +0 -1
- package/dist/agent-runtime/modes/orchestrator/index.d.ts +0 -41
- package/dist/agent-runtime/modes/orchestrator/index.js +0 -218
- package/dist/agent-runtime/modes/orchestrator/index.js.map +0 -1
- package/dist/agent-runtime/modes/orchestrator/prompt.d.ts +0 -7
- package/dist/agent-runtime/modes/orchestrator/prompt.js +0 -53
- package/dist/agent-runtime/modes/orchestrator/prompt.js.map +0 -1
- package/dist/agent-runtime/reflection.d.ts +0 -100
- package/dist/agent-runtime/reflection.js +0 -241
- package/dist/agent-runtime/reflection.js.map +0 -1
- package/dist/agent-runtime/reflectors/action-reflector.d.ts +0 -97
- package/dist/agent-runtime/reflectors/action-reflector.js +0 -234
- package/dist/agent-runtime/reflectors/action-reflector.js.map +0 -1
- package/dist/agent-runtime/reflectors/policy.d.ts +0 -56
- package/dist/agent-runtime/reflectors/policy.js +0 -111
- package/dist/agent-runtime/reflectors/policy.js.map +0 -1
- package/dist/agent-runtime/reflectors/trajectory-reflector.d.ts +0 -93
- package/dist/agent-runtime/reflectors/trajectory-reflector.js +0 -224
- package/dist/agent-runtime/reflectors/trajectory-reflector.js.map +0 -1
|
@@ -4,19 +4,36 @@
|
|
|
4
4
|
* Each attached device owns an independent logical runtime lease even when
|
|
5
5
|
* multiple device runtimes share one OS process. Lease credentials remain
|
|
6
6
|
* memory-only and are never included in logs.
|
|
7
|
+
*
|
|
8
|
+
* Registration, heartbeat and lease fencing are owned by the harness SDK's
|
|
9
|
+
* `RuntimeRegistrationCoordinator`; this plugin keeps only the durable
|
|
10
|
+
* registration lineage and the Types4 agent projection it publishes.
|
|
7
11
|
*/
|
|
8
12
|
import { randomUUID } from "node:crypto";
|
|
9
13
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
10
14
|
import { dirname, join } from "node:path";
|
|
11
|
-
import {
|
|
15
|
+
import { AgentGatewayClient, InstanceAuthorityRuntimeRegistrationTransport, RuntimeRegistrationCoordinator, RuntimeRegistrationJournalAdvanceRequiredError, RuntimeRegistrationOwnerResetRequiredError, signAgentMessage, } from "@beeos-ai/cloud-harness-sdk";
|
|
16
|
+
import { syncAgents, } from "../../adapters/agent-gateway-client.js";
|
|
12
17
|
import { AGENT_PROJECTION_HASH_DOMAIN, DEVICE_RUNTIME_CAPABILITIES, DEVICE_RUNTIME_CONTRACT_REVISION, DEVICE_RUNTIME_METHODS, DEVICE_RUNTIME_RPC_PROTOCOL_VERSION, verifiedAdmittedManifestDigest, } from "../../runtime-authority/contract.js";
|
|
13
18
|
import { runtimeDomainHash, sha256Hex, } from "../../runtime-authority/canonical.js";
|
|
19
|
+
import { DEVICE_NATIVE_MANIFEST_DIGEST, DEVICE_NATIVE_RUNTIME_MANIFEST } from "../../runtime-authority/device-prompt.gen.js";
|
|
14
20
|
const REGISTER_RETRY_MS = 5_000;
|
|
15
21
|
const HANDOFF_RETRY_MS = 65_000;
|
|
16
22
|
const SYNC_RETRY_MS = 5_000;
|
|
17
23
|
const REGISTRATION_INTENT_HASH_DOMAIN = "beeos.device-agent.registration-intent.v1";
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
/**
|
|
25
|
+
* A `fenced` registration is recoverable once the predecessor's control-plane
|
|
26
|
+
* lease settles. Keep the pre-SDK 65 s handoff wait instead of the SDK's
|
|
27
|
+
* shorter default so we do not thrash the authority while that lease is live.
|
|
28
|
+
*/
|
|
29
|
+
const HANDOFF_BACKOFF = Object.freeze({
|
|
30
|
+
maxAttempts: Number.POSITIVE_INFINITY,
|
|
31
|
+
initialDelayMs: HANDOFF_RETRY_MS,
|
|
32
|
+
maxDelayMs: HANDOFF_RETRY_MS,
|
|
33
|
+
factor: 1,
|
|
34
|
+
});
|
|
35
|
+
export function resolveRegistrationManifestDigest(cloudTarget = false) {
|
|
36
|
+
return cloudTarget ? DEVICE_NATIVE_MANIFEST_DIGEST : verifiedAdmittedManifestDigest();
|
|
20
37
|
}
|
|
21
38
|
/**
|
|
22
39
|
* Cluster treats registrationId as an idempotency key for one immutable
|
|
@@ -29,37 +46,52 @@ export function buildRuntimeRegistrationId(identityFingerprint, manifestDigest,
|
|
|
29
46
|
return `device-agent-${identityFingerprint.slice(0, 16)}-${generationHash.slice(0, 24)}`;
|
|
30
47
|
}
|
|
31
48
|
function registrationIntentHash(manifestDigest) {
|
|
49
|
+
const native = manifestDigest === DEVICE_NATIVE_MANIFEST_DIGEST;
|
|
32
50
|
return runtimeDomainHash(REGISTRATION_INTENT_HASH_DOMAIN, {
|
|
33
51
|
contractRevision: DEVICE_RUNTIME_CONTRACT_REVISION,
|
|
34
52
|
manifestDigest,
|
|
35
53
|
runtimeRpcProtocolVersion: DEVICE_RUNTIME_RPC_PROTOCOL_VERSION,
|
|
36
|
-
runtimeMethods: DEVICE_RUNTIME_METHODS,
|
|
37
|
-
capabilities: DEVICE_RUNTIME_CAPABILITIES,
|
|
54
|
+
runtimeMethods: native ? DEVICE_NATIVE_RUNTIME_MANIFEST.runtimeMethods : DEVICE_RUNTIME_METHODS,
|
|
55
|
+
capabilities: native ? DEVICE_NATIVE_RUNTIME_MANIFEST.capabilities : DEVICE_RUNTIME_CAPABILITIES,
|
|
38
56
|
});
|
|
39
57
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
58
|
+
/** Adapt the bridge-client keypair into the harness SDK request identity. */
|
|
59
|
+
function requestIdentity(identity) {
|
|
60
|
+
return {
|
|
61
|
+
publicKey: identity.publicKey,
|
|
62
|
+
sign: (message) => signAgentMessage(message, identity.privateKey),
|
|
63
|
+
};
|
|
43
64
|
}
|
|
44
65
|
export class AgentSyncPlugin {
|
|
45
66
|
opts;
|
|
46
67
|
name = "agent-sync";
|
|
47
68
|
ctx;
|
|
48
69
|
stopped = true;
|
|
49
|
-
generation = 0;
|
|
50
|
-
controlTimer = null;
|
|
51
|
-
expiryTimer = null;
|
|
52
|
-
syncTimer = null;
|
|
53
70
|
periodicSync = null;
|
|
54
|
-
|
|
71
|
+
restartTimer = null;
|
|
72
|
+
syncTimer = null;
|
|
73
|
+
coordinator = null;
|
|
55
74
|
identity = null;
|
|
56
75
|
registration = null;
|
|
57
76
|
acceptedRevision = 0;
|
|
58
77
|
acceptedHash = "";
|
|
78
|
+
syncedEpoch = null;
|
|
79
|
+
initializing = false;
|
|
59
80
|
queue = Promise.resolve();
|
|
81
|
+
authorityListeners = new Set();
|
|
60
82
|
constructor(opts) {
|
|
61
83
|
this.opts = opts;
|
|
62
84
|
}
|
|
85
|
+
currentLease() {
|
|
86
|
+
const lease = this.coordinator?.authorityLease;
|
|
87
|
+
if (!lease || !this.registration)
|
|
88
|
+
return null;
|
|
89
|
+
return { ...lease, journalStoreId: this.registration.journalStoreId, journalGeneration: this.registration.journalGeneration };
|
|
90
|
+
}
|
|
91
|
+
subscribeAuthority(listener) {
|
|
92
|
+
this.authorityListeners.add(listener);
|
|
93
|
+
return () => this.authorityListeners.delete(listener);
|
|
94
|
+
}
|
|
63
95
|
init(ctx) {
|
|
64
96
|
this.ctx = ctx;
|
|
65
97
|
}
|
|
@@ -67,27 +99,34 @@ export class AgentSyncPlugin {
|
|
|
67
99
|
if (!this.ctx || this.ctx.config.standalone)
|
|
68
100
|
return;
|
|
69
101
|
this.stopped = false;
|
|
70
|
-
|
|
71
|
-
await this.
|
|
102
|
+
await this.enqueue(() => this.startRegistration());
|
|
103
|
+
await this.drain();
|
|
72
104
|
const interval = this.opts.intervalMs ??
|
|
73
|
-
Number(process.env.BEEOS_AGENT_SYNC_INTERVAL_MS ?? "
|
|
105
|
+
Number(process.env.BEEOS_AGENT_SYNC_INTERVAL_MS ?? "30000");
|
|
74
106
|
if (interval > 0) {
|
|
75
107
|
this.periodicSync = setInterval(() => {
|
|
76
|
-
void this.enqueue(() => this.syncProjection(
|
|
108
|
+
void this.enqueue(() => this.syncProjection(false));
|
|
77
109
|
}, interval);
|
|
78
110
|
this.periodicSync.unref?.();
|
|
79
111
|
}
|
|
80
112
|
}
|
|
81
113
|
async stop() {
|
|
82
114
|
this.stopped = true;
|
|
83
|
-
this.generation++;
|
|
84
115
|
this.clearTimers();
|
|
85
|
-
this.
|
|
116
|
+
this.coordinator?.stop();
|
|
117
|
+
this.coordinator = null;
|
|
118
|
+
for (const listener of this.authorityListeners)
|
|
119
|
+
listener();
|
|
86
120
|
this.identity = null;
|
|
121
|
+
this.syncedEpoch = null;
|
|
87
122
|
await this.queue.catch(() => undefined);
|
|
88
123
|
}
|
|
89
|
-
|
|
90
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Build the harness registration coordinator for the current durable
|
|
126
|
+
* lineage and let it own registration, heartbeat and lease fencing.
|
|
127
|
+
*/
|
|
128
|
+
async startRegistration() {
|
|
129
|
+
if (!this.isCurrent() || !this.ctx)
|
|
91
130
|
return;
|
|
92
131
|
const { config, log } = this.ctx;
|
|
93
132
|
if (!config.agentGatewayUrl)
|
|
@@ -95,13 +134,13 @@ export class AgentSyncPlugin {
|
|
|
95
134
|
const identity = await this.opts.loadIdentity().catch(() => null);
|
|
96
135
|
if (!identity) {
|
|
97
136
|
log.warn("runtime-authority: identity unavailable; retrying registration");
|
|
98
|
-
this.
|
|
137
|
+
this.scheduleRestart(REGISTER_RETRY_MS);
|
|
99
138
|
return;
|
|
100
139
|
}
|
|
101
140
|
this.identity = identity;
|
|
102
141
|
let manifestDigest;
|
|
103
142
|
try {
|
|
104
|
-
manifestDigest = resolveRegistrationManifestDigest();
|
|
143
|
+
manifestDigest = resolveRegistrationManifestDigest(Boolean(config.cloudTargetInstanceId));
|
|
105
144
|
}
|
|
106
145
|
catch (error) {
|
|
107
146
|
// A defective contract cannot heal by retrying, and the gateway rejects it
|
|
@@ -111,138 +150,123 @@ export class AgentSyncPlugin {
|
|
|
111
150
|
}
|
|
112
151
|
const fingerprint = sha256Hex(identity.publicKey);
|
|
113
152
|
const registration = await this.ensureRegistrationState(fingerprint, manifestDigest);
|
|
114
|
-
const
|
|
153
|
+
const gateway = new AgentGatewayClient({
|
|
154
|
+
baseUrl: config.agentGatewayUrl,
|
|
155
|
+
identity: requestIdentity(identity),
|
|
156
|
+
});
|
|
157
|
+
const transport = new InstanceAuthorityRuntimeRegistrationTransport({
|
|
158
|
+
gateway,
|
|
159
|
+
registrationId: registration.registrationId,
|
|
160
|
+
});
|
|
161
|
+
const coordinator = new RuntimeRegistrationCoordinator(transport, {
|
|
115
162
|
registrationId: registration.registrationId,
|
|
116
163
|
handlerIdentity: `device-agent:${fingerprint.slice(0, 32)}`,
|
|
117
164
|
contractRevision: DEVICE_RUNTIME_CONTRACT_REVISION,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
capabilities: [...DEVICE_RUNTIME_CAPABILITIES],
|
|
165
|
+
runtimeMethods: (config.cloudTargetInstanceId ? DEVICE_NATIVE_RUNTIME_MANIFEST.runtimeMethods : DEVICE_RUNTIME_METHODS),
|
|
166
|
+
capabilities: (config.cloudTargetInstanceId ? DEVICE_NATIVE_RUNTIME_MANIFEST.capabilities : DEVICE_RUNTIME_CAPABILITIES),
|
|
167
|
+
manifestDigest: manifestDigest,
|
|
122
168
|
journalStoreId: registration.journalStoreId,
|
|
123
169
|
journalGeneration: registration.journalGeneration,
|
|
124
|
-
|
|
170
|
+
instanceIdentityKeyId: `device:${fingerprint}`,
|
|
171
|
+
targetInstanceId: config.cloudTargetInstanceId,
|
|
172
|
+
signIdentityProof: (message) => signAgentMessage(message, identity.privateKey),
|
|
173
|
+
fencedRetry: HANDOFF_BACKOFF,
|
|
174
|
+
}, (reason, cause) => this.onLeaseLost(reason, cause), () => this.now());
|
|
175
|
+
const previous = this.coordinator;
|
|
176
|
+
this.coordinator = coordinator;
|
|
177
|
+
previous?.stop();
|
|
178
|
+
coordinator.subscribeAuthority((lease) => this.onAuthorityChanged(lease));
|
|
179
|
+
this.initializing = true;
|
|
180
|
+
let lease;
|
|
125
181
|
try {
|
|
126
|
-
|
|
127
|
-
agentGatewayUrl: config.agentGatewayUrl,
|
|
128
|
-
identity,
|
|
129
|
-
payload,
|
|
130
|
-
fetchImpl: this.opts.fetchImpl,
|
|
131
|
-
now: this.opts.now,
|
|
132
|
-
randomUUID: this.opts.randomUUID,
|
|
133
|
-
});
|
|
134
|
-
if (!this.isCurrent(generation))
|
|
135
|
-
return;
|
|
136
|
-
if (result.status !== "active" ||
|
|
137
|
-
!result.instanceId ||
|
|
138
|
-
!result.leaseId ||
|
|
139
|
-
!result.runtimeLeaseCredential ||
|
|
140
|
-
!result.leaseExpiresAt ||
|
|
141
|
-
!result.heartbeatIntervalMs ||
|
|
142
|
-
!result.runtimeEpoch) {
|
|
143
|
-
log.warn({ status: result.status, activeRuntimeEpoch: result.activeRuntimeEpoch }, "runtime-authority: registration fenced; waiting for handoff");
|
|
144
|
-
this.dropLease();
|
|
145
|
-
this.scheduleRegister(HANDOFF_RETRY_MS, generation);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
if (Date.parse(result.leaseExpiresAt) <= this.now()) {
|
|
149
|
-
throw new Error("runtime authority returned an expired lease");
|
|
150
|
-
}
|
|
151
|
-
const sameEpoch = this.lease?.runtimeEpoch === result.runtimeEpoch;
|
|
152
|
-
this.lease = {
|
|
153
|
-
instanceId: result.instanceId,
|
|
154
|
-
leaseId: result.leaseId,
|
|
155
|
-
runtimeEpoch: result.runtimeEpoch,
|
|
156
|
-
runtimeLeaseCredential: result.runtimeLeaseCredential,
|
|
157
|
-
leaseExpiresAt: result.leaseExpiresAt,
|
|
158
|
-
heartbeatIntervalMs: result.heartbeatIntervalMs,
|
|
159
|
-
};
|
|
160
|
-
if (!sameEpoch) {
|
|
161
|
-
this.acceptedRevision = 0;
|
|
162
|
-
this.acceptedHash = "";
|
|
163
|
-
}
|
|
164
|
-
this.scheduleExpiry(generation);
|
|
165
|
-
log.info({
|
|
166
|
-
runtimeEpoch: this.lease.runtimeEpoch,
|
|
167
|
-
leaseExpiresAt: this.lease.leaseExpiresAt,
|
|
168
|
-
}, "runtime-authority: active lease acquired");
|
|
169
|
-
await this.syncProjection(generation, true);
|
|
170
|
-
this.scheduleHeartbeat(this.lease.heartbeatIntervalMs, generation);
|
|
182
|
+
lease = await coordinator.start();
|
|
171
183
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
log.warn({
|
|
186
|
-
err: gatewayError.message,
|
|
187
|
-
status: gatewayError.status,
|
|
188
|
-
retryMs,
|
|
189
|
-
...(rolled ? { journalGeneration: expectedGeneration } : {}),
|
|
190
|
-
}, "runtime-authority: registration failed");
|
|
191
|
-
this.dropLease();
|
|
192
|
-
this.scheduleRegister(retryMs, generation);
|
|
184
|
+
finally {
|
|
185
|
+
this.initializing = false;
|
|
186
|
+
}
|
|
187
|
+
if (!this.isCurrent()) {
|
|
188
|
+
coordinator.stop();
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (lease) {
|
|
192
|
+
// The listener is muted while the coordinator starts; publish the first
|
|
193
|
+
// projection here so callers can rely on start() having registered.
|
|
194
|
+
this.syncedEpoch = lease.runtimeEpoch;
|
|
195
|
+
log.info({ runtimeEpoch: lease.runtimeEpoch, leaseExpiresAt: lease.leaseExpiresAt }, "runtime-authority: active lease acquired");
|
|
196
|
+
await this.syncProjection(true);
|
|
193
197
|
}
|
|
194
198
|
}
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
/** Only a new runtime epoch needs a fresh projection; renewals do not. */
|
|
200
|
+
onAuthorityChanged(lease) {
|
|
201
|
+
for (const listener of this.authorityListeners)
|
|
202
|
+
listener();
|
|
203
|
+
if (!lease) {
|
|
204
|
+
this.syncedEpoch = null;
|
|
205
|
+
this.acceptedRevision = 0;
|
|
206
|
+
this.acceptedHash = "";
|
|
197
207
|
return;
|
|
198
|
-
const active = this.lease;
|
|
199
|
-
try {
|
|
200
|
-
const result = await heartbeatRuntime({
|
|
201
|
-
agentGatewayUrl: this.ctx.config.agentGatewayUrl,
|
|
202
|
-
identity: this.identity,
|
|
203
|
-
registrationId: this.registrationId(),
|
|
204
|
-
payload: {
|
|
205
|
-
leaseId: active.leaseId,
|
|
206
|
-
handlerIdentity: this.handlerIdentity(this.identity),
|
|
207
|
-
runtimeEpoch: active.runtimeEpoch,
|
|
208
|
-
},
|
|
209
|
-
fetchImpl: this.opts.fetchImpl,
|
|
210
|
-
now: this.opts.now,
|
|
211
|
-
randomUUID: this.opts.randomUUID,
|
|
212
|
-
});
|
|
213
|
-
if (!this.isCurrent(generation) || this.lease !== active)
|
|
214
|
-
return;
|
|
215
|
-
if (result.status !== "renewed" ||
|
|
216
|
-
!result.runtimeLeaseCredential ||
|
|
217
|
-
!result.leaseExpiresAt) {
|
|
218
|
-
this.ctx.log.warn({ status: result.status, activeRuntimeEpoch: result.activeRuntimeEpoch }, "runtime-authority: heartbeat fenced");
|
|
219
|
-
this.dropLease();
|
|
220
|
-
this.scheduleRegister(REGISTER_RETRY_MS, generation);
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
this.lease = {
|
|
224
|
-
...active,
|
|
225
|
-
runtimeLeaseCredential: result.runtimeLeaseCredential,
|
|
226
|
-
leaseExpiresAt: result.leaseExpiresAt,
|
|
227
|
-
};
|
|
228
|
-
this.scheduleExpiry(generation);
|
|
229
|
-
this.scheduleHeartbeat(active.heartbeatIntervalMs, generation);
|
|
230
208
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
209
|
+
if (this.initializing)
|
|
210
|
+
return;
|
|
211
|
+
if (lease.runtimeEpoch === this.syncedEpoch)
|
|
212
|
+
return;
|
|
213
|
+
this.syncedEpoch = lease.runtimeEpoch;
|
|
214
|
+
this.acceptedRevision = 0;
|
|
215
|
+
this.acceptedHash = "";
|
|
216
|
+
void this.enqueue(() => this.syncProjection(true));
|
|
217
|
+
}
|
|
218
|
+
onLeaseLost(reason, cause) {
|
|
219
|
+
this.syncedEpoch = null;
|
|
220
|
+
this.acceptedRevision = 0;
|
|
221
|
+
this.acceptedHash = "";
|
|
222
|
+
const log = this.ctx?.log;
|
|
223
|
+
if (cause instanceof RuntimeRegistrationJournalAdvanceRequiredError) {
|
|
224
|
+
log?.warn({ expectedJournalGeneration: cause.expectedJournalGeneration }, "runtime-authority: journal generation must advance");
|
|
225
|
+
void this.enqueue(() => this.rollAndRestart(cause.expectedJournalGeneration));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (cause instanceof RuntimeRegistrationOwnerResetRequiredError) {
|
|
229
|
+
log?.error("runtime-authority: journal store owner reset required; registration is fenced until an operator resets the journal identity");
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (reason === "expired") {
|
|
233
|
+
log?.warn("runtime-authority: lease expired; advancing journal generation");
|
|
234
|
+
void this.enqueue(() => this.rollAndRestart());
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
// Fenced and transient failures are already retried by the coordinator.
|
|
238
|
+
log?.warn({ reason, err: cause?.message }, "runtime-authority: lease lost; coordinator will retry registration");
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* The authority fenced the old journal lineage. Roll the durable generation
|
|
242
|
+
* exactly once, then rebuild the coordinator so it re-registers the new id.
|
|
243
|
+
*/
|
|
244
|
+
async rollAndRestart(expectedGeneration) {
|
|
245
|
+
if (!this.isCurrent() || !this.ctx || !this.identity || !this.registration) {
|
|
246
|
+
return;
|
|
242
247
|
}
|
|
248
|
+
const manifestDigest = resolveRegistrationManifestDigest();
|
|
249
|
+
const fingerprint = sha256Hex(this.identity.publicKey);
|
|
250
|
+
const current = this.registration;
|
|
251
|
+
const target = expectedGeneration ?? (BigInt(current.journalGeneration) + 1n).toString();
|
|
252
|
+
if (BigInt(target) <= BigInt(current.journalGeneration)) {
|
|
253
|
+
// Already at the requested generation; wait out the predecessor lease.
|
|
254
|
+
this.scheduleRestart(HANDOFF_RETRY_MS);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
await this.publishRegistrationState({
|
|
258
|
+
...current,
|
|
259
|
+
registrationId: buildRuntimeRegistrationId(fingerprint, manifestDigest, target, this.nextUUID()),
|
|
260
|
+
journalGeneration: target,
|
|
261
|
+
});
|
|
262
|
+
this.scheduleRestart(0);
|
|
243
263
|
}
|
|
244
|
-
async syncProjection(
|
|
245
|
-
if (!this.isCurrent(
|
|
264
|
+
async syncProjection(force, allowConflictRetry = true) {
|
|
265
|
+
if (!this.isCurrent() || !this.ctx || !this.identity || !this.coordinator) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const lease = this.coordinator.lease;
|
|
269
|
+
if (!lease)
|
|
246
270
|
return;
|
|
247
271
|
const declarations = await this.opts.buildDeclarations().catch((error) => {
|
|
248
272
|
this.ctx.log.warn({ err: error.message }, "agent-sync: declaration build failed");
|
|
@@ -258,37 +282,35 @@ export class AgentSyncPlugin {
|
|
|
258
282
|
if (!force && canonicalHash === this.acceptedHash)
|
|
259
283
|
return;
|
|
260
284
|
const revision = this.acceptedRevision + 1;
|
|
261
|
-
const
|
|
262
|
-
const snapshotId = `device-${active.runtimeEpoch}-${revision}-${canonicalHash.slice(0, 16)}`;
|
|
285
|
+
const snapshotId = `device-${lease.runtimeEpoch}-${revision}-${canonicalHash.slice(0, 16)}`;
|
|
263
286
|
try {
|
|
264
287
|
const result = await syncAgents({
|
|
265
288
|
agentGatewayUrl: this.ctx.config.agentGatewayUrl,
|
|
266
289
|
identity: this.identity,
|
|
267
290
|
request: {
|
|
268
|
-
runtimeEpoch:
|
|
291
|
+
runtimeEpoch: lease.runtimeEpoch,
|
|
269
292
|
revision: String(revision),
|
|
270
293
|
snapshotId,
|
|
271
294
|
canonicalHash,
|
|
272
295
|
agentsComplete: true,
|
|
273
296
|
skillsComplete: true,
|
|
274
297
|
agents,
|
|
275
|
-
runtimeLeaseCredential:
|
|
298
|
+
runtimeLeaseCredential: lease.runtimeLeaseCredential,
|
|
276
299
|
},
|
|
277
300
|
fetchImpl: this.opts.fetchImpl,
|
|
278
301
|
now: this.opts.now,
|
|
279
302
|
randomUUID: this.opts.randomUUID,
|
|
280
303
|
});
|
|
281
|
-
if (!this.isCurrent(
|
|
304
|
+
if (!this.isCurrent())
|
|
282
305
|
return;
|
|
283
306
|
if (!result.accepted) {
|
|
284
307
|
const acceptedRevision = Number(result.revision);
|
|
285
|
-
if (result.runtimeEpoch !==
|
|
308
|
+
if (result.runtimeEpoch !== lease.runtimeEpoch) {
|
|
286
309
|
this.ctx.log.warn({
|
|
287
|
-
runtimeEpoch:
|
|
310
|
+
runtimeEpoch: lease.runtimeEpoch,
|
|
288
311
|
acceptedRuntimeEpoch: result.runtimeEpoch,
|
|
289
312
|
}, "agent-sync: projection fenced by a newer runtime epoch");
|
|
290
|
-
this.
|
|
291
|
-
this.scheduleRegister(REGISTER_RETRY_MS, generation);
|
|
313
|
+
this.restartRegistration();
|
|
292
314
|
return;
|
|
293
315
|
}
|
|
294
316
|
if (Number.isSafeInteger(acceptedRevision) && acceptedRevision > 0) {
|
|
@@ -297,10 +319,10 @@ export class AgentSyncPlugin {
|
|
|
297
319
|
}
|
|
298
320
|
if (result.canonicalHash !== canonicalHash) {
|
|
299
321
|
if (allowConflictRetry) {
|
|
300
|
-
await this.syncProjection(
|
|
322
|
+
await this.syncProjection(true, false);
|
|
301
323
|
}
|
|
302
324
|
else {
|
|
303
|
-
this.scheduleSyncRetry(
|
|
325
|
+
this.scheduleSyncRetry();
|
|
304
326
|
}
|
|
305
327
|
}
|
|
306
328
|
return;
|
|
@@ -311,21 +333,28 @@ export class AgentSyncPlugin {
|
|
|
311
333
|
created: result.created,
|
|
312
334
|
updated: result.updated,
|
|
313
335
|
deactivated: result.deactivated,
|
|
314
|
-
runtimeEpoch:
|
|
336
|
+
runtimeEpoch: lease.runtimeEpoch,
|
|
315
337
|
revision: this.acceptedRevision,
|
|
316
338
|
}, "agent-sync: runtime projection accepted");
|
|
317
339
|
}
|
|
318
340
|
catch (error) {
|
|
319
|
-
if (!this.isCurrent(
|
|
341
|
+
if (!this.isCurrent())
|
|
320
342
|
return;
|
|
321
343
|
const gatewayError = error;
|
|
322
344
|
this.ctx.log.warn({ err: gatewayError.message, status: gatewayError.status }, "agent-sync: projection failed");
|
|
345
|
+
if (gatewayError.status === 409 && allowConflictRetry) {
|
|
346
|
+
// A restart can reuse a live lease whose earlier process published a
|
|
347
|
+
// different hash at revision 1. Advance the projection, not the lease.
|
|
348
|
+
this.acceptedRevision = Math.max(this.acceptedRevision, Math.trunc(this.now()));
|
|
349
|
+
await this.syncProjection(true, false);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
323
352
|
if (gatewayError.status === 401 || gatewayError.status === 409) {
|
|
324
|
-
|
|
325
|
-
this.
|
|
353
|
+
// The lease credential was rejected; re-register rather than retrying it.
|
|
354
|
+
this.restartRegistration();
|
|
326
355
|
return;
|
|
327
356
|
}
|
|
328
|
-
this.scheduleSyncRetry(
|
|
357
|
+
this.scheduleSyncRetry();
|
|
329
358
|
}
|
|
330
359
|
}
|
|
331
360
|
toProjection(declarations, identity) {
|
|
@@ -339,6 +368,8 @@ export class AgentSyncPlugin {
|
|
|
339
368
|
...(agent.version ? { version: agent.version } : {}),
|
|
340
369
|
...(agent.avatarUrl ? { avatarUrl: agent.avatarUrl } : {}),
|
|
341
370
|
...(agent.homepage ? { homepage: agent.homepage } : {}),
|
|
371
|
+
...(this.ctx?.config.vlm?.model?.trim()
|
|
372
|
+
? { defaultModel: this.ctx.config.vlm.model.trim() } : {}),
|
|
342
373
|
capabilities: {
|
|
343
374
|
canvas: agent.capabilities?.canvas === true,
|
|
344
375
|
terminal: agent.capabilities?.terminal === true,
|
|
@@ -363,62 +394,36 @@ export class AgentSyncPlugin {
|
|
|
363
394
|
}))
|
|
364
395
|
.sort((left, right) => left.runtimeAgentUid.localeCompare(right.runtimeAgentUid));
|
|
365
396
|
}
|
|
366
|
-
|
|
367
|
-
if (!this.isCurrent(
|
|
397
|
+
restartRegistration() {
|
|
398
|
+
if (!this.isCurrent())
|
|
368
399
|
return;
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
this.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
400
|
+
this.coordinator?.stop();
|
|
401
|
+
this.coordinator = null;
|
|
402
|
+
this.syncedEpoch = null;
|
|
403
|
+
this.acceptedRevision = 0;
|
|
404
|
+
this.acceptedHash = "";
|
|
405
|
+
this.scheduleRestart(REGISTER_RETRY_MS);
|
|
375
406
|
}
|
|
376
|
-
|
|
377
|
-
if (!this.isCurrent(
|
|
407
|
+
scheduleRestart(delayMs) {
|
|
408
|
+
if (!this.isCurrent())
|
|
378
409
|
return;
|
|
379
|
-
if (this.
|
|
380
|
-
clearTimeout(this.
|
|
381
|
-
this.
|
|
382
|
-
this.
|
|
383
|
-
void this.enqueue(() => this.
|
|
410
|
+
if (this.restartTimer)
|
|
411
|
+
clearTimeout(this.restartTimer);
|
|
412
|
+
this.restartTimer = setTimeout(() => {
|
|
413
|
+
this.restartTimer = null;
|
|
414
|
+
void this.enqueue(() => this.startRegistration());
|
|
384
415
|
}, delayMs);
|
|
385
|
-
this.
|
|
416
|
+
this.restartTimer.unref?.();
|
|
386
417
|
}
|
|
387
|
-
scheduleSyncRetry(
|
|
388
|
-
if (!this.isCurrent(
|
|
418
|
+
scheduleSyncRetry() {
|
|
419
|
+
if (!this.isCurrent() || this.syncTimer)
|
|
389
420
|
return;
|
|
390
421
|
this.syncTimer = setTimeout(() => {
|
|
391
422
|
this.syncTimer = null;
|
|
392
|
-
void this.enqueue(() => this.syncProjection(
|
|
423
|
+
void this.enqueue(() => this.syncProjection(true));
|
|
393
424
|
}, SYNC_RETRY_MS);
|
|
394
425
|
this.syncTimer.unref?.();
|
|
395
426
|
}
|
|
396
|
-
scheduleExpiry(generation) {
|
|
397
|
-
if (this.expiryTimer)
|
|
398
|
-
clearTimeout(this.expiryTimer);
|
|
399
|
-
const expected = this.lease;
|
|
400
|
-
if (!expected)
|
|
401
|
-
return;
|
|
402
|
-
const remaining = Math.max(0, Date.parse(expected.leaseExpiresAt) - this.now());
|
|
403
|
-
this.expiryTimer = setTimeout(() => {
|
|
404
|
-
this.expiryTimer = null;
|
|
405
|
-
if (!this.isCurrent(generation) || this.lease !== expected)
|
|
406
|
-
return;
|
|
407
|
-
this.ctx?.log.warn("runtime-authority: lease expired");
|
|
408
|
-
this.dropLease();
|
|
409
|
-
this.scheduleRegister(REGISTER_RETRY_MS, generation);
|
|
410
|
-
}, remaining);
|
|
411
|
-
this.expiryTimer.unref?.();
|
|
412
|
-
}
|
|
413
|
-
registrationId() {
|
|
414
|
-
if (!this.registration) {
|
|
415
|
-
throw new Error("runtime registration state is unavailable");
|
|
416
|
-
}
|
|
417
|
-
return this.registration.registrationId;
|
|
418
|
-
}
|
|
419
|
-
handlerIdentity(identity) {
|
|
420
|
-
return `device-agent:${sha256Hex(identity.publicKey).slice(0, 32)}`;
|
|
421
|
-
}
|
|
422
427
|
async ensureRegistrationState(fingerprint, manifestDigest) {
|
|
423
428
|
if (this.registration)
|
|
424
429
|
return this.registration;
|
|
@@ -441,18 +446,6 @@ export class AgentSyncPlugin {
|
|
|
441
446
|
journalGeneration,
|
|
442
447
|
});
|
|
443
448
|
}
|
|
444
|
-
async rollRegistrationState(fingerprint, manifestDigest, expectedGeneration) {
|
|
445
|
-
const current = await this.ensureRegistrationState(fingerprint, manifestDigest);
|
|
446
|
-
if (BigInt(expectedGeneration) <= BigInt(current.journalGeneration)) {
|
|
447
|
-
return false;
|
|
448
|
-
}
|
|
449
|
-
await this.publishRegistrationState({
|
|
450
|
-
...current,
|
|
451
|
-
registrationId: buildRuntimeRegistrationId(fingerprint, manifestDigest, expectedGeneration, this.nextUUID()),
|
|
452
|
-
journalGeneration: expectedGeneration,
|
|
453
|
-
});
|
|
454
|
-
return true;
|
|
455
|
-
}
|
|
456
449
|
async readRegistrationState(identityFingerprint) {
|
|
457
450
|
const file = this.registrationStateFile(identityFingerprint);
|
|
458
451
|
if (!file)
|
|
@@ -496,37 +489,35 @@ export class AgentSyncPlugin {
|
|
|
496
489
|
nextUUID() {
|
|
497
490
|
return this.opts.randomUUID ? this.opts.randomUUID() : randomUUID();
|
|
498
491
|
}
|
|
499
|
-
dropLease() {
|
|
500
|
-
this.lease = null;
|
|
501
|
-
if (this.expiryTimer)
|
|
502
|
-
clearTimeout(this.expiryTimer);
|
|
503
|
-
this.expiryTimer = null;
|
|
504
|
-
}
|
|
505
492
|
clearTimers() {
|
|
506
|
-
for (const timer of [
|
|
507
|
-
this.controlTimer,
|
|
508
|
-
this.expiryTimer,
|
|
509
|
-
this.syncTimer,
|
|
510
|
-
this.periodicSync,
|
|
511
|
-
]) {
|
|
493
|
+
for (const timer of [this.periodicSync, this.restartTimer, this.syncTimer]) {
|
|
512
494
|
if (timer)
|
|
513
495
|
clearTimeout(timer);
|
|
514
496
|
}
|
|
515
|
-
this.controlTimer = null;
|
|
516
|
-
this.expiryTimer = null;
|
|
517
|
-
this.syncTimer = null;
|
|
518
497
|
this.periodicSync = null;
|
|
498
|
+
this.restartTimer = null;
|
|
499
|
+
this.syncTimer = null;
|
|
519
500
|
}
|
|
520
501
|
now() {
|
|
521
502
|
return this.opts.now ? this.opts.now() : Date.now();
|
|
522
503
|
}
|
|
523
|
-
isCurrent(
|
|
524
|
-
return !this.stopped
|
|
504
|
+
isCurrent() {
|
|
505
|
+
return !this.stopped;
|
|
525
506
|
}
|
|
526
507
|
enqueue(work) {
|
|
527
508
|
const next = this.queue.then(work, work);
|
|
528
509
|
this.queue = next.catch(() => undefined);
|
|
529
510
|
return next;
|
|
530
511
|
}
|
|
512
|
+
/** Wait until no further work is chained onto the serial queue. */
|
|
513
|
+
async drain() {
|
|
514
|
+
let current = this.queue;
|
|
515
|
+
while (true) {
|
|
516
|
+
await current;
|
|
517
|
+
if (this.queue === current)
|
|
518
|
+
return;
|
|
519
|
+
current = this.queue;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
531
522
|
}
|
|
532
523
|
//# sourceMappingURL=index.js.map
|