@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.
Files changed (34) hide show
  1. package/README.md +2 -2
  2. package/dist/a2a-maintenance.d.ts +10 -8
  3. package/dist/a2a-maintenance.d.ts.map +1 -1
  4. package/dist/a2a-maintenance.js +17 -25
  5. package/dist/a2a-maintenance.js.map +1 -1
  6. package/dist/a2a-maintenance.test.js +27 -5
  7. package/dist/a2a-maintenance.test.js.map +1 -1
  8. package/dist/bridge-agent.js +11 -4
  9. package/dist/bridge-agent.js.map +1 -1
  10. package/dist/chat-client.d.ts +3 -0
  11. package/dist/chat-client.d.ts.map +1 -1
  12. package/dist/chat-client.js +9 -3
  13. package/dist/chat-client.js.map +1 -1
  14. package/dist/index.js +38 -40
  15. package/dist/index.js.map +4 -4
  16. package/dist/platform-control-plane.d.ts +9 -0
  17. package/dist/platform-control-plane.d.ts.map +1 -0
  18. package/dist/platform-control-plane.js +25 -0
  19. package/dist/platform-control-plane.js.map +1 -0
  20. package/dist/platform-control-plane.test.d.ts +2 -0
  21. package/dist/platform-control-plane.test.d.ts.map +1 -0
  22. package/dist/platform-control-plane.test.js +32 -0
  23. package/dist/platform-control-plane.test.js.map +1 -0
  24. package/dist/platform-heartbeat.d.ts +6 -0
  25. package/dist/platform-heartbeat.d.ts.map +1 -1
  26. package/dist/platform-heartbeat.js +6 -1
  27. package/dist/platform-heartbeat.js.map +1 -1
  28. package/dist/voice-capability.d.ts.map +1 -1
  29. package/dist/voice-capability.js +3 -20
  30. package/dist/voice-capability.js.map +1 -1
  31. package/docs/plans/2026-09-02-chat-bound-maintenance-capability-design.md +36 -0
  32. package/docs/plans/2026-09-02-runtime-env-control-plane-design.md +15 -0
  33. package/openclaw.plugin.json +1 -1
  34. package/package.json +1 -1
@@ -0,0 +1,9 @@
1
+ import type { A2APluginConfig } from "./types.js";
2
+ export type PlatformControlPlane = {
3
+ apiBaseUrl: string;
4
+ agentId: string;
5
+ relayApiKey: string;
6
+ };
7
+ /** Resolve the same platform identity whether installers persisted it in config or service env. */
8
+ export declare function resolvePlatformControlPlane(config: A2APluginConfig, env?: Record<string, string | undefined>): PlatformControlPlane | null;
9
+ //# sourceMappingURL=platform-control-plane.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-control-plane.d.ts","sourceRoot":"","sources":["../src/platform-control-plane.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,mGAAmG;AACnG,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,eAAe,EACvB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,oBAAoB,GAAG,IAAI,CAgB7B"}
@@ -0,0 +1,25 @@
1
+ /** Resolve the same platform identity whether installers persisted it in config or service env. */
2
+ export function resolvePlatformControlPlane(config, env = process.env) {
3
+ const heartbeat = config.platform?.heartbeatUrl?.trim() || env.PERKOS_HEARTBEAT_URL?.trim();
4
+ const relayApiKey = config.relay?.apiKey?.trim()
5
+ || config.chat?.apiKey?.trim()
6
+ || env.A2A_RELAY_API_KEY?.trim()
7
+ || env.A2A_CHAT_API_KEY?.trim();
8
+ if (!heartbeat || !relayApiKey)
9
+ return null;
10
+ let url;
11
+ try {
12
+ url = new URL(heartbeat);
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ const match = url.pathname.match(/^(.*)\/agents\/([^/]+)\/heartbeat\/?$/u);
18
+ const agentId = config.platform?.agentId?.trim()
19
+ || (match ? decodeURIComponent(match[2]) : "")
20
+ || env.PERKOS_AGENT_ID?.trim();
21
+ if (!match || !agentId)
22
+ return null;
23
+ return { apiBaseUrl: `${url.origin}${match[1]}`, agentId, relayApiKey };
24
+ }
25
+ //# sourceMappingURL=platform-control-plane.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-control-plane.js","sourceRoot":"","sources":["../src/platform-control-plane.ts"],"names":[],"mappings":"AAQA,mGAAmG;AACnG,MAAM,UAAU,2BAA2B,CACzC,MAAuB,EACvB,MAA0C,OAAO,CAAC,GAAG;IAErD,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,oBAAoB,EAAE,IAAI,EAAE,CAAC;IAC5F,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;WAC3C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;WAC3B,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE;WAC7B,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAC;IAClC,IAAI,CAAC,SAAS,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAE5C,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QAAC,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;IACxD,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE;WAC3C,CAAC,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;WAC5C,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IACjC,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,EAAE,UAAU,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=platform-control-plane.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-control-plane.test.d.ts","sourceRoot":"","sources":["../src/platform-control-plane.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { resolvePlatformControlPlane } from "./platform-control-plane.js";
3
+ const baseConfig = {
4
+ agentName: "Athena",
5
+ port: 5050,
6
+ skills: [],
7
+ peers: {},
8
+ runtime: { kind: "hermes-api" },
9
+ };
10
+ describe("resolvePlatformControlPlane", () => {
11
+ it("uses plugin config when the complete identity is persisted there", () => {
12
+ expect(resolvePlatformControlPlane({
13
+ ...baseConfig,
14
+ relay: { enabled: true, url: "wss://dev.transport.perkos.xyz/a2a", apiKey: "rk_config" },
15
+ platform: { agentId: "athena", heartbeatUrl: "https://dev.api.perkos.xyz/agents/athena/heartbeat" },
16
+ }, {})).toEqual({ apiBaseUrl: "https://dev.api.perkos.xyz", agentId: "athena", relayApiKey: "rk_config" });
17
+ });
18
+ it("uses service env when update-hermes keeps runtime identity outside plugin JSON", () => {
19
+ expect(resolvePlatformControlPlane(baseConfig, {
20
+ PERKOS_HEARTBEAT_URL: "https://dev.api.perkos.xyz/agents/athena/heartbeat",
21
+ A2A_RELAY_API_KEY: "rk_env",
22
+ })).toEqual({ apiBaseUrl: "https://dev.api.perkos.xyz", agentId: "athena", relayApiKey: "rk_env" });
23
+ });
24
+ it("fails closed without a complete validated heartbeat identity", () => {
25
+ expect(resolvePlatformControlPlane(baseConfig, { A2A_RELAY_API_KEY: "rk_env" })).toBeNull();
26
+ expect(resolvePlatformControlPlane(baseConfig, {
27
+ PERKOS_HEARTBEAT_URL: "not-a-url",
28
+ A2A_RELAY_API_KEY: "rk_env",
29
+ })).toBeNull();
30
+ });
31
+ });
32
+ //# sourceMappingURL=platform-control-plane.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-control-plane.test.js","sourceRoot":"","sources":["../src/platform-control-plane.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAG1E,MAAM,UAAU,GAAoB;IAClC,SAAS,EAAE,QAAQ;IACnB,IAAI,EAAE,IAAI;IACV,MAAM,EAAE,EAAE;IACV,KAAK,EAAE,EAAE;IACT,OAAO,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE;CAChC,CAAC;AAEF,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,2BAA2B,CAAC;YACjC,GAAG,UAAU;YACb,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,oCAAoC,EAAE,MAAM,EAAE,WAAW,EAAE;YACxF,QAAQ,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,oDAAoD,EAAE;SACpG,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,4BAA4B,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC;IAC7G,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,CAAC,2BAA2B,CAAC,UAAU,EAAE;YAC7C,oBAAoB,EAAE,oDAAoD;YAC1E,iBAAiB,EAAE,QAAQ;SAC5B,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,4BAA4B,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,CAAC,2BAA2B,CAAC,UAAU,EAAE,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC5F,MAAM,CAAC,2BAA2B,CAAC,UAAU,EAAE;YAC7C,oBAAoB,EAAE,WAAW;YACjC,iBAAiB,EAAE,QAAQ;SAC5B,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -4,8 +4,13 @@ export type PlatformHeartbeatLogger = {
4
4
  };
5
5
  export type PlatformHeartbeatHandle = {
6
6
  stop(): void;
7
+ reportNow(): void;
7
8
  };
8
9
  export type PlatformRuntimeStatus = "healthy" | "unreachable" | "unknown";
10
+ export type PlatformMaintenanceCapability = {
11
+ protocolVersion: 1;
12
+ bridgeInstanceId: string;
13
+ };
9
14
  /**
10
15
  * Keep the PerkOS agent presence record fresh while the bridge is alive.
11
16
  *
@@ -23,6 +28,7 @@ export declare function startPlatformHeartbeat(opts: {
23
28
  relayKey?: string;
24
29
  runtimeKind?: "openclaw" | "hermes" | "custom";
25
30
  runtimeStatus?: PlatformRuntimeStatus | (() => PlatformRuntimeStatus | Promise<PlatformRuntimeStatus>);
31
+ maintenanceCapability?: PlatformMaintenanceCapability | null | (() => PlatformMaintenanceCapability | null);
26
32
  version?: string;
27
33
  onResult?: (result: {
28
34
  ok: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"platform-heartbeat.d.ts","sourceRoot":"","sources":["../src/platform-heartbeat.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,IAAI,IAAI,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;AAK1E;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,MAAM,EAAE,uBAAuB,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC/C,aAAa,CAAC,EAAE,qBAAqB,GAAG,CAAC,MAAM,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC;IACvG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClF,GAAG,uBAAuB,CAgF1B"}
1
+ {"version":3,"file":"platform-heartbeat.d.ts","sourceRoot":"","sources":["../src/platform-heartbeat.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,IAAI,IAAI,CAAC;IACb,SAAS,IAAI,IAAI,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;AAE1E,MAAM,MAAM,6BAA6B,GAAG;IAC1C,eAAe,EAAE,CAAC,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAKF;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,MAAM,EAAE,uBAAuB,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC/C,aAAa,CAAC,EAAE,qBAAqB,GAAG,CAAC,MAAM,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC;IACvG,qBAAqB,CAAC,EAAE,6BAA6B,GAAG,IAAI,GAAG,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC,CAAC;IAC5G,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClF,GAAG,uBAAuB,CAqF1B"}
@@ -14,7 +14,7 @@ export function startPlatformHeartbeat(opts) {
14
14
  const url = opts.url?.trim() || env.PERKOS_HEARTBEAT_URL?.trim();
15
15
  const relayKey = opts.relayKey?.trim() || env.A2A_RELAY_API_KEY?.trim();
16
16
  if (!url || !relayKey)
17
- return { stop: () => undefined };
17
+ return { stop: () => undefined, reportNow: () => undefined };
18
18
  const configuredInterval = Number(opts.intervalMs ?? env.PERKOS_HEARTBEAT_INTERVAL_MS ?? DEFAULT_INTERVAL_MS);
19
19
  const intervalMs = Number.isFinite(configuredInterval)
20
20
  ? Math.max(MIN_INTERVAL_MS, configuredInterval)
@@ -36,6 +36,9 @@ export function startPlatformHeartbeat(opts) {
36
36
  const runtimeStatus = typeof opts.runtimeStatus === "function"
37
37
  ? await opts.runtimeStatus()
38
38
  : opts.runtimeStatus ?? "unknown";
39
+ const maintenanceCapability = typeof opts.maintenanceCapability === "function"
40
+ ? opts.maintenanceCapability()
41
+ : opts.maintenanceCapability ?? null;
39
42
  const res = await fetcher(url, {
40
43
  method: "POST",
41
44
  headers: {
@@ -48,6 +51,7 @@ export function startPlatformHeartbeat(opts) {
48
51
  version: opts.version?.trim() || env.PERKOS_A2A_VERSION?.trim() || "0.12.51",
49
52
  ts: Date.now(),
50
53
  runtimeStatus,
54
+ ...(opts.maintenanceCapability !== undefined ? { maintenanceCapability } : {}),
51
55
  }),
52
56
  });
53
57
  if (res.ok) {
@@ -72,6 +76,7 @@ export function startPlatformHeartbeat(opts) {
72
76
  const timer = setInterval(() => void report(), intervalMs);
73
77
  timer.unref?.();
74
78
  return {
79
+ reportNow: () => void report(),
75
80
  stop: () => {
76
81
  stopped = true;
77
82
  clearInterval(timer);
@@ -1 +1 @@
1
- {"version":3,"file":"platform-heartbeat.js","sourceRoot":"","sources":["../src/platform-heartbeat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform-heartbeat.js","sourceRoot":"","sources":["../src/platform-heartbeat.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"voice-capability.d.ts","sourceRoot":"","sources":["../src/voice-capability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,YAAY,CAAC;AAE/D,eAAO,MAAM,yBAAyB,EAAG,oBAA6B,CAAC;AACvE,eAAO,MAAM,0BAA0B,EAAG,qBAA8B,CAAC;AAEzE,KAAK,WAAW,GAAG;IACjB,oBAAoB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACpG,2BAA2B,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxG,CAAC;AAEF,KAAK,iBAAiB,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;AAsCpD;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,eAAe,EACvB,SAAS,GAAE,iBAAiC,GAC3C,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBxB"}
1
+ {"version":3,"file":"voice-capability.d.ts","sourceRoot":"","sources":["../src/voice-capability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,YAAY,CAAC;AAG/D,eAAO,MAAM,yBAAyB,EAAG,oBAA6B,CAAC;AACvE,eAAO,MAAM,0BAA0B,EAAG,qBAA8B,CAAC;AAEzE,KAAK,WAAW,GAAG;IACjB,oBAAoB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACpG,2BAA2B,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxG,CAAC;AAEF,KAAK,iBAAiB,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;AA0BpD;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,eAAe,EACvB,SAAS,GAAE,iBAAiC,GAC3C,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBxB"}
@@ -1,3 +1,4 @@
1
+ import { resolvePlatformControlPlane } from "./platform-control-plane.js";
1
2
  export const PERKOS_VOICE_PROBE_MARKER = "PERKOS_VOICE_PROBE";
2
3
  export const PERKOS_VOICE_ENROLL_MARKER = "PERKOS_VOICE_ENROLL";
3
4
  function runtime(runtimeKind) {
@@ -9,28 +10,10 @@ function runtime(runtimeKind) {
9
10
  return "zeroclaw";
10
11
  return null;
11
12
  }
12
- function controlPlane(config) {
13
- const heartbeat = config.platform?.heartbeatUrl;
14
- const relayApiKey = config.relay?.apiKey || config.chat?.apiKey;
15
- if (!heartbeat || !relayApiKey)
16
- return null;
17
- let url;
18
- try {
19
- url = new URL(heartbeat);
20
- }
21
- catch {
22
- return null;
23
- }
24
- const match = url.pathname.match(/^(.*)\/agents\/([^/]+)\/heartbeat\/?$/u);
25
- const agentId = config.platform?.agentId || (match ? decodeURIComponent(match[2]) : "");
26
- if (!match || !agentId)
27
- return null;
28
- return { apiBaseUrl: `${url.origin}${match[1]}`, agentId, relayApiKey };
29
- }
30
13
  async function reportPluginMissing(message, config) {
31
14
  if (message !== PERKOS_VOICE_PROBE_MARKER)
32
15
  return "PERKOS_VOICE_FAILED:voice_plugin_missing";
33
- const target = controlPlane(config);
16
+ const target = resolvePlatformControlPlane(config);
34
17
  if (!target)
35
18
  return "PERKOS_VOICE_FAILED:a2a_identity_unavailable";
36
19
  const response = await fetch(`${target.apiBaseUrl}/agents/${encodeURIComponent(target.agentId)}/voice-credential/capability-report`, {
@@ -53,7 +36,7 @@ export async function tryHandleInstalledVoiceMarker(message, config, loadVoice =
53
36
  const marker = message.trim();
54
37
  if (marker !== PERKOS_VOICE_PROBE_MARKER && marker !== PERKOS_VOICE_ENROLL_MARKER)
55
38
  return null;
56
- const target = controlPlane(config);
39
+ const target = resolvePlatformControlPlane(config);
57
40
  if (!target)
58
41
  return "PERKOS_VOICE_FAILED:a2a_identity_unavailable";
59
42
  const runtimeKind = runtime(config.runtime?.kind);
@@ -1 +1 @@
1
- {"version":3,"file":"voice-capability.js","sourceRoot":"","sources":["../src/voice-capability.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"voice-capability.js","sourceRoot":"","sources":["../src/voice-capability.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAE1E,MAAM,CAAC,MAAM,yBAAyB,GAAG,oBAA6B,CAAC;AACvE,MAAM,CAAC,MAAM,0BAA0B,GAAG,qBAA8B,CAAC;AASzE,SAAS,OAAO,CAAC,WAAoC;IACnD,IAAI,WAAW,KAAK,QAAQ,IAAI,WAAW,KAAK,YAAY;QAAE,OAAO,QAAQ,CAAC;IAC9E,IAAI,WAAW,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAClD,IAAI,WAAW,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAClD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,OAAe,EAAE,MAAuB;IACzE,IAAI,OAAO,KAAK,yBAAyB;QAAE,OAAO,0CAA0C,CAAC;IAC7F,MAAM,MAAM,GAAG,2BAA2B,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM;QAAE,OAAO,8CAA8C,CAAC;IACnE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,UAAU,WAAW,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,qCAAqC,EAAE;QACnI,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,CAAC,WAAW,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC9F,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,sBAAsB,EAAE,CAAC;KACnF,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,+CAA+C,CAAC,CAAC,CAAC,+CAA+C,CAAC;AAC1H,CAAC;AAED,KAAK,UAAU,aAAa;IAC1B,MAAM,SAAS,GAAG,0BAA0B,CAAC;IAC7C,OAAO,MAAM,CAAC,SAAS,CAAyB,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAe,EACf,MAAuB,EACvB,YAA+B,aAAa;IAE5C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,MAAM,KAAK,yBAAyB,IAAI,MAAM,KAAK,0BAA0B;QAAE,OAAO,IAAI,CAAC;IAC/F,MAAM,MAAM,GAAG,2BAA2B,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM;QAAE,OAAO,8CAA8C,CAAC;IACnE,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,WAAW;QAAE,OAAO,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7D,IAAI,KAAkB,CAAC;IACvB,IAAI,CAAC;QAAC,KAAK,GAAG,MAAM,SAAS,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,CAAC;IACxF,IAAI,OAAO,KAAK,CAAC,oBAAoB,KAAK,UAAU,IAAI,OAAO,KAAK,CAAC,2BAA2B,KAAK,UAAU,EAAE,CAAC;QAChH,OAAO,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,KAAK,CAAC,oBAAoB,CAAC,MAAM,EAAE;QACxC,GAAG,MAAM;QACT,OAAO,EAAE,WAAW;QACpB,SAAS,EAAE,KAAK,CAAC,2BAA2B;KAC7C,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,36 @@
1
+ # Chat-bound maintenance capability
2
+
3
+ ## Problem
4
+
5
+ The platform inferred managed-update support from the reported A2A package
6
+ version. A bridge could therefore publish `0.12.63` while another legacy
7
+ process owned the active Chat registration. The App offered an update, but the
8
+ marker remained pending because no active consumer claimed it.
9
+
10
+ ## Decision
11
+
12
+ Managed maintenance is fail-closed and requires all of the following:
13
+
14
+ 1. Hermes runtime and A2A package `>=0.12.64`.
15
+ 2. Protocol-v1 capability in a recent heartbeat.
16
+ 3. A UUID `bridgeInstanceId` generated once per bridge process.
17
+ 4. An authenticated Chat connection owned by that same process.
18
+ 5. A request and claim addressed to the same `bridgeInstanceId`.
19
+
20
+ The standalone bridge reports capability immediately after Chat authentication
21
+ and revokes it immediately after disconnect. The API leases the evidence for
22
+ 125 seconds using server time. Every ordinary heartbeat without the capability
23
+ clears it. The App only renders the managed action while that lease is valid.
24
+
25
+ ## Failure behavior
26
+
27
+ - A compatible semver without fresh capability shows bootstrap required.
28
+ - A duplicate or superseded process cannot claim a marker addressed elsewhere.
29
+ - Expired capability prevents request creation with `BOOTSTRAP_REQUIRED`.
30
+ - Voice remains an independent optional capability.
31
+
32
+ ## Migration
33
+
34
+ Version `0.12.63` cannot advertise this stronger contract. Existing Hermes
35
+ agents need the one-time `update-hermes` bootstrap to `0.12.64`; subsequent
36
+ updates can use the managed marker flow.
@@ -0,0 +1,15 @@
1
+ # Identidad del control plane desde config o entorno
2
+
3
+ ## Hallazgo
4
+
5
+ El bridge Hermes instalado por `update-hermes` puede conservar `PERKOS_HEARTBEAT_URL` y `A2A_RELAY_API_KEY` en el entorno del servicio. Heartbeat ya lee ambos orígenes, pero los handlers de mantenimiento y Voice sólo consultaban el JSON del plugin. Así, una instancia podía anunciar una capability Chat-bound válida y responder `a2a_identity_unavailable` al recibir el marcador.
6
+
7
+ ## Decisión
8
+
9
+ Un resolvedor compartido aplica una precedencia única: configuración explícita del plugin y luego variables del servicio. Valida una URL de heartbeat con la ruta exacta `/agents/<id>/heartbeat`, deriva el API base y nunca registra ni devuelve la credencial fuera del proceso. Mantenimiento y Voice consumen el mismo resolvedor para impedir nuevas divergencias.
10
+
11
+ ## Seguridad y verificación
12
+
13
+ La ausencia de URL o credencial continúa fallando cerrado. La capability sigue ligada a la conexión Chat y a `bridgeInstanceId`; este cambio sólo hace accesible al handler la misma identidad que ya usa heartbeat. Las pruebas cubren identidad completa en config, fallback completo por entorno y rechazo de identidad incompleta o URL inválida.
14
+
15
+ > Relacionado: [[PerkOS-Projects/README]] · [[PerkOS-A2A]] · [[PerkOS-API]] · [[PerkOS]] · [[PerkOS-Voice]]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "perkos-a2a",
3
3
  "name": "PerkOS A2A",
4
- "version": "0.12.63",
4
+ "version": "0.12.65",
5
5
  "description": "Agent-to-Agent (A2A) protocol communication for OpenClaw agents",
6
6
  "author": "PerkOS",
7
7
  "configSchema": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@perkos/perkos-a2a",
3
- "version": "0.12.63",
3
+ "version": "0.12.65",
4
4
  "description": "A2A Protocol communication plugin for OpenClaw — Agent-to-Agent protocol implementation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",