@perkos/perkos-a2a 0.12.63 → 0.12.65
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 +2 -2
- package/dist/a2a-maintenance.d.ts +10 -8
- package/dist/a2a-maintenance.d.ts.map +1 -1
- package/dist/a2a-maintenance.js +17 -25
- package/dist/a2a-maintenance.js.map +1 -1
- package/dist/a2a-maintenance.test.js +27 -5
- package/dist/a2a-maintenance.test.js.map +1 -1
- package/dist/bridge-agent.js +11 -4
- package/dist/bridge-agent.js.map +1 -1
- package/dist/chat-client.d.ts +3 -0
- package/dist/chat-client.d.ts.map +1 -1
- package/dist/chat-client.js +9 -3
- package/dist/chat-client.js.map +1 -1
- package/dist/index.js +38 -40
- package/dist/index.js.map +4 -4
- package/dist/platform-control-plane.d.ts +9 -0
- package/dist/platform-control-plane.d.ts.map +1 -0
- package/dist/platform-control-plane.js +25 -0
- package/dist/platform-control-plane.js.map +1 -0
- package/dist/platform-control-plane.test.d.ts +2 -0
- package/dist/platform-control-plane.test.d.ts.map +1 -0
- package/dist/platform-control-plane.test.js +32 -0
- package/dist/platform-control-plane.test.js.map +1 -0
- package/dist/platform-heartbeat.d.ts +6 -0
- package/dist/platform-heartbeat.d.ts.map +1 -1
- package/dist/platform-heartbeat.js +6 -1
- package/dist/platform-heartbeat.js.map +1 -1
- package/dist/voice-capability.d.ts.map +1 -1
- package/dist/voice-capability.js +3 -20
- package/dist/voice-capability.js.map +1 -1
- package/docs/plans/2026-09-02-chat-bound-maintenance-capability-design.md +36 -0
- package/docs/plans/2026-09-02-runtime-env-control-plane-design.md +15 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -29244,18 +29244,10 @@ function usesHttpDelivery(runtimeKind) {
|
|
|
29244
29244
|
return runtimeKind === "hermes" || runtimeKind === "hermes-api" || runtimeKind === "zeroclaw" || runtimeKind === "custom";
|
|
29245
29245
|
}
|
|
29246
29246
|
|
|
29247
|
-
// src/
|
|
29248
|
-
|
|
29249
|
-
|
|
29250
|
-
|
|
29251
|
-
if (runtimeKind === "hermes" || runtimeKind === "hermes-api") return "hermes";
|
|
29252
|
-
if (runtimeKind === "openclaw") return "openclaw";
|
|
29253
|
-
if (runtimeKind === "zeroclaw") return "zeroclaw";
|
|
29254
|
-
return null;
|
|
29255
|
-
}
|
|
29256
|
-
function controlPlane(config) {
|
|
29257
|
-
const heartbeat = config.platform?.heartbeatUrl;
|
|
29258
|
-
const relayApiKey = config.relay?.apiKey || config.chat?.apiKey;
|
|
29247
|
+
// src/platform-control-plane.ts
|
|
29248
|
+
function resolvePlatformControlPlane(config, env = process.env) {
|
|
29249
|
+
const heartbeat = config.platform?.heartbeatUrl?.trim() || env.PERKOS_HEARTBEAT_URL?.trim();
|
|
29250
|
+
const relayApiKey = config.relay?.apiKey?.trim() || config.chat?.apiKey?.trim() || env.A2A_RELAY_API_KEY?.trim() || env.A2A_CHAT_API_KEY?.trim();
|
|
29259
29251
|
if (!heartbeat || !relayApiKey) return null;
|
|
29260
29252
|
let url;
|
|
29261
29253
|
try {
|
|
@@ -29264,13 +29256,23 @@ function controlPlane(config) {
|
|
|
29264
29256
|
return null;
|
|
29265
29257
|
}
|
|
29266
29258
|
const match = url.pathname.match(/^(.*)\/agents\/([^/]+)\/heartbeat\/?$/u);
|
|
29267
|
-
const agentId = config.platform?.agentId || (match ? decodeURIComponent(match[2]) : "");
|
|
29259
|
+
const agentId = config.platform?.agentId?.trim() || (match ? decodeURIComponent(match[2]) : "") || env.PERKOS_AGENT_ID?.trim();
|
|
29268
29260
|
if (!match || !agentId) return null;
|
|
29269
29261
|
return { apiBaseUrl: `${url.origin}${match[1]}`, agentId, relayApiKey };
|
|
29270
29262
|
}
|
|
29263
|
+
|
|
29264
|
+
// src/voice-capability.ts
|
|
29265
|
+
var PERKOS_VOICE_PROBE_MARKER = "PERKOS_VOICE_PROBE";
|
|
29266
|
+
var PERKOS_VOICE_ENROLL_MARKER = "PERKOS_VOICE_ENROLL";
|
|
29267
|
+
function runtime(runtimeKind) {
|
|
29268
|
+
if (runtimeKind === "hermes" || runtimeKind === "hermes-api") return "hermes";
|
|
29269
|
+
if (runtimeKind === "openclaw") return "openclaw";
|
|
29270
|
+
if (runtimeKind === "zeroclaw") return "zeroclaw";
|
|
29271
|
+
return null;
|
|
29272
|
+
}
|
|
29271
29273
|
async function reportPluginMissing(message, config) {
|
|
29272
29274
|
if (message !== PERKOS_VOICE_PROBE_MARKER) return "PERKOS_VOICE_FAILED:voice_plugin_missing";
|
|
29273
|
-
const target =
|
|
29275
|
+
const target = resolvePlatformControlPlane(config);
|
|
29274
29276
|
if (!target) return "PERKOS_VOICE_FAILED:a2a_identity_unavailable";
|
|
29275
29277
|
const response = await fetch(`${target.apiBaseUrl}/agents/${encodeURIComponent(target.agentId)}/voice-credential/capability-report`, {
|
|
29276
29278
|
method: "POST",
|
|
@@ -29286,7 +29288,7 @@ async function defaultLoader() {
|
|
|
29286
29288
|
async function tryHandleInstalledVoiceMarker(message, config, loadVoice = defaultLoader) {
|
|
29287
29289
|
const marker = message.trim();
|
|
29288
29290
|
if (marker !== PERKOS_VOICE_PROBE_MARKER && marker !== PERKOS_VOICE_ENROLL_MARKER) return null;
|
|
29289
|
-
const target =
|
|
29291
|
+
const target = resolvePlatformControlPlane(config);
|
|
29290
29292
|
if (!target) return "PERKOS_VOICE_FAILED:a2a_identity_unavailable";
|
|
29291
29293
|
const runtimeKind = runtime(config.runtime?.kind);
|
|
29292
29294
|
if (!runtimeKind) return reportPluginMissing(marker, config);
|
|
@@ -29314,27 +29316,14 @@ import { dirname as dirname3, join as join4 } from "node:path";
|
|
|
29314
29316
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
29315
29317
|
var PERKOS_A2A_UPDATE_PREFIX = "PERKOS_A2A_UPDATE:";
|
|
29316
29318
|
var REQUEST_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
29319
|
+
var BRIDGE_INSTANCE_ID = REQUEST_ID;
|
|
29317
29320
|
function parseA2AUpdateMarker(message) {
|
|
29318
29321
|
const marker = message.trim();
|
|
29319
29322
|
if (!marker.startsWith(PERKOS_A2A_UPDATE_PREFIX)) return null;
|
|
29320
|
-
const requestId = marker.slice(PERKOS_A2A_UPDATE_PREFIX.length);
|
|
29321
|
-
return REQUEST_ID.test(requestId) ? requestId : null;
|
|
29322
|
-
}
|
|
29323
|
-
function maintenanceControlPlane(config) {
|
|
29324
|
-
const heartbeat = config.platform?.heartbeatUrl;
|
|
29325
|
-
const relayApiKey = config.relay?.apiKey || config.chat?.apiKey;
|
|
29326
|
-
if (!heartbeat || !relayApiKey) return null;
|
|
29327
|
-
let url;
|
|
29328
|
-
try {
|
|
29329
|
-
url = new URL(heartbeat);
|
|
29330
|
-
} catch {
|
|
29331
|
-
return null;
|
|
29332
|
-
}
|
|
29333
|
-
const match = url.pathname.match(/^(.*)\/agents\/([^/]+)\/heartbeat\/?$/u);
|
|
29334
|
-
const agentId = config.platform?.agentId || (match ? decodeURIComponent(match[2]) : "");
|
|
29335
|
-
if (!match || !agentId) return null;
|
|
29336
|
-
return { apiBaseUrl: `${url.origin}${match[1]}`, agentId, relayApiKey };
|
|
29323
|
+
const [requestId, bridgeInstanceId, extra] = marker.slice(PERKOS_A2A_UPDATE_PREFIX.length).split(":");
|
|
29324
|
+
return !extra && requestId && bridgeInstanceId && REQUEST_ID.test(requestId) && BRIDGE_INSTANCE_ID.test(bridgeInstanceId) ? { requestId, bridgeInstanceId } : null;
|
|
29337
29325
|
}
|
|
29326
|
+
var maintenanceControlPlane = resolvePlatformControlPlane;
|
|
29338
29327
|
function maintenanceRoot(agentId) {
|
|
29339
29328
|
return join4(homedir5(), ".perkos", "a2a", "hermes", agentId, "maintenance");
|
|
29340
29329
|
}
|
|
@@ -29360,11 +29349,11 @@ async function authorizedJson(url, relayApiKey, init = {}) {
|
|
|
29360
29349
|
if (!response.ok || !body) throw new Error(`control_plane_${response.status}`);
|
|
29361
29350
|
return body;
|
|
29362
29351
|
}
|
|
29363
|
-
async function claimRequest(target, requestId) {
|
|
29352
|
+
async function claimRequest(target, requestId, bridgeInstanceId) {
|
|
29364
29353
|
const body = await authorizedJson(
|
|
29365
29354
|
`${target.apiBaseUrl}/agents/${encodeURIComponent(target.agentId)}/maintenance/a2a-update/${requestId}/claim`,
|
|
29366
29355
|
target.relayApiKey,
|
|
29367
|
-
{ method: "POST", body: "{}" }
|
|
29356
|
+
{ method: "POST", body: "{}", headers: { "x-perkos-bridge-instance": bridgeInstanceId } }
|
|
29368
29357
|
);
|
|
29369
29358
|
const request = body.request;
|
|
29370
29359
|
if (typeof request?.targetVersion !== "string" || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(request.targetVersion)) {
|
|
@@ -29384,15 +29373,19 @@ async function queueHermesMaintenanceJob(job) {
|
|
|
29384
29373
|
child.unref();
|
|
29385
29374
|
}
|
|
29386
29375
|
async function tryHandleA2AMaintenanceMarker(message, config, deps = {}) {
|
|
29387
|
-
const
|
|
29388
|
-
if (!
|
|
29376
|
+
const marker = parseA2AUpdateMarker(message);
|
|
29377
|
+
if (!marker) return null;
|
|
29378
|
+
const { requestId, bridgeInstanceId } = marker;
|
|
29379
|
+
if (!deps.bridgeInstanceId || deps.bridgeInstanceId !== bridgeInstanceId) {
|
|
29380
|
+
return "PERKOS_A2A_UPDATE_FAILED:bridge_instance_mismatch";
|
|
29381
|
+
}
|
|
29389
29382
|
if (config.runtime?.kind !== "hermes" && config.runtime?.kind !== "hermes-api") {
|
|
29390
29383
|
return "PERKOS_A2A_UPDATE_FAILED:runtime_unsupported";
|
|
29391
29384
|
}
|
|
29392
29385
|
const target = maintenanceControlPlane(config);
|
|
29393
29386
|
if (!target) return "PERKOS_A2A_UPDATE_FAILED:a2a_identity_unavailable";
|
|
29394
29387
|
try {
|
|
29395
|
-
const { targetVersion } = await (deps.claim ?? claimRequest)(target, requestId);
|
|
29388
|
+
const { targetVersion } = await (deps.claim ?? claimRequest)(target, requestId, bridgeInstanceId);
|
|
29396
29389
|
await (deps.queue ?? queueHermesMaintenanceJob)({
|
|
29397
29390
|
schemaVersion: 1,
|
|
29398
29391
|
requestId,
|
|
@@ -29463,6 +29456,11 @@ var ChatClient = class {
|
|
|
29463
29456
|
authed = false;
|
|
29464
29457
|
stopped = false;
|
|
29465
29458
|
generation = 0;
|
|
29459
|
+
setAuthed(value) {
|
|
29460
|
+
if (this.authed === value) return;
|
|
29461
|
+
this.authed = value;
|
|
29462
|
+
this.handlers.onConnectionStateChange?.(value);
|
|
29463
|
+
}
|
|
29466
29464
|
constructor(opts) {
|
|
29467
29465
|
this.agentName = opts.agentName;
|
|
29468
29466
|
const cfg = opts.config;
|
|
@@ -29624,7 +29622,7 @@ var ChatClient = class {
|
|
|
29624
29622
|
}
|
|
29625
29623
|
if (!this.isCurrent(generation, socket)) return;
|
|
29626
29624
|
this.connected = false;
|
|
29627
|
-
this.
|
|
29625
|
+
this.setAuthed(false);
|
|
29628
29626
|
this.clearTimers();
|
|
29629
29627
|
if (!this.stopped) {
|
|
29630
29628
|
this.logger.info(`[perkos-chat] disconnected (${code}: ${reason?.toString() || "no reason"}); reconnecting`);
|
|
@@ -29639,7 +29637,7 @@ var ChatClient = class {
|
|
|
29639
29637
|
async handleFrame(frame) {
|
|
29640
29638
|
switch (frame.type) {
|
|
29641
29639
|
case "auth_ok":
|
|
29642
|
-
this.
|
|
29640
|
+
this.setAuthed(true);
|
|
29643
29641
|
this.logger.info(`[perkos-chat] authed as agent:${this.agentName}`);
|
|
29644
29642
|
this.startHeartbeat();
|
|
29645
29643
|
return;
|
|
@@ -29850,7 +29848,7 @@ var ChatClient = class {
|
|
|
29850
29848
|
const socket = this.ws;
|
|
29851
29849
|
this.ws = null;
|
|
29852
29850
|
this.connected = false;
|
|
29853
|
-
this.
|
|
29851
|
+
this.setAuthed(false);
|
|
29854
29852
|
return socket;
|
|
29855
29853
|
}
|
|
29856
29854
|
};
|