@perkos/perkos-a2a 0.12.32 → 0.12.33

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 CHANGED
@@ -170,7 +170,7 @@ openclaw gateway restart
170
170
 
171
171
  #### Official idempotent connection
172
172
 
173
- Version 0.12.32+ ships the machine-readable `perkos-a2a connect` CLI. The
173
+ Version 0.12.33+ ships the machine-readable `perkos-a2a connect` CLI. The
174
174
  official PerkOS invitation supplies the artifact URL, SHA-256, agent name, and
175
175
  agent id; it invokes the exact npm version with `npx`. The same command performs
176
176
  a first install or repairs the matching existing identity, even when an invalid
@@ -206,11 +206,25 @@ failure is emitted as one JSON object; secret values are never printed.
206
206
 
207
207
  OpenClaw plugin reloads can briefly keep two JavaScript VMs alive. Starting the
208
208
  relay socket in each VM would register the same external identity twice. Version
209
- 0.12.32 moves the relay WebSocket, PerkOS Chat socket, and platform heartbeat to
209
+ 0.12.33 moves the relay WebSocket, PerkOS Chat socket, and platform heartbeat to
210
210
  one process-external supervisor per normalized relay URL + agent id. Plugin VMs
211
211
  communicate with that owner through a private Unix socket and renew a 15-second
212
212
  lease.
213
213
 
214
+ OpenClaw transport ownership is fail-closed: a plugin VM never falls back to a
215
+ second in-process relay/chat/heartbeat client if the supervisor cannot become
216
+ READY and accept the matching claim. The supervisor records its PID, process
217
+ start identity, boot id, claim time, and transport counts in private health
218
+ evidence so `connect` can distinguish a certified owner from stale files.
219
+
220
+ For a clean first install, OpenClaw's PID 1 hot reload remains supported. When
221
+ upgrading a pre-supervisor or otherwise uncertified installation, `connect`
222
+ requires evidence that the gateway PID/start identity actually changed. If the
223
+ current environment cannot perform that restart safely, it exits with the
224
+ machine-readable action `ACTION_REQUIRED_REAL_RESTART`; rerunning the same
225
+ idempotent command after the service/container restart resumes the migration
226
+ without replacing identity or credentials.
227
+
214
228
  Each claim carries the parent PID/start identity, boot id, instance id, and
215
229
  generation. Before accepting a newer generation, the supervisor waits for
216
230
  confirmed transport shutdown; a timeout fails closed with
@@ -221,7 +235,7 @@ routing.
221
235
 
222
236
  ```bash
223
237
  A2A_RELAY_API_KEY='<credential-from-invitation>' \
224
- npx --yes @perkos/perkos-a2a@0.12.32 connect \
238
+ npx --yes @perkos/perkos-a2a@0.12.33 connect \
225
239
  --artifact ./perkos-a2a.tgz \
226
240
  --sha256 '<sha256-from-the-official-invitation>' \
227
241
  --agent-name '<agent-name>' \
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAGV,WAAW,EAEZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,cAAc,YAAY,CAAC;AA0C3B,kEAAkE;AAClE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAItF;AAED,KAAK,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AASpD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,EACd,oBAAoB,EAAE,MAAM,GAC3B,OAAO,CA2BT;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,oBAAoB,EAC3B,eAAe,CAAC,EAAE,MAAM,GACvB,oBAAoB,CA+BtB;AAiED,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC,CAAC;IACH,IAAI,CAAC,EAAE;QACL,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;CACH,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CAYrF;AAQD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CAAE,GAClF,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuFxB;AAYD,oFAAoF;AACpF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,WAAW,EAAE,EACvB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAWR;AA4CD,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAe/D;AAED,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,GAAG,QA6yBxC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAGV,WAAW,EAEZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,cAAc,YAAY,CAAC;AA0C3B,kEAAkE;AAClE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAItF;AAED,KAAK,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AASpD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,EACd,oBAAoB,EAAE,MAAM,GAC3B,OAAO,CA2BT;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,oBAAoB,EAC3B,eAAe,CAAC,EAAE,MAAM,GACvB,oBAAoB,CA+BtB;AAiED,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC,CAAC;IACH,IAAI,CAAC,EAAE;QACL,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;CACH,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CAYrF;AAQD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CAAE,GAClF,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuFxB;AAYD,oFAAoF;AACpF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,WAAW,EAAE,EACvB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAWR;AA4CD,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAe/D;AAED,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,GAAG,QAqzBxC"}
package/dist/index.js CHANGED
@@ -27009,6 +27009,7 @@ var A2AServer = class {
27009
27009
  clientOnly = false;
27010
27010
  relayClient = null;
27011
27011
  externalRelayTransport = false;
27012
+ relayOwnership;
27012
27013
  relayHub = null;
27013
27014
  messageInjector = null;
27014
27015
  taskResultHandler = null;
@@ -27020,9 +27021,10 @@ var A2AServer = class {
27020
27021
  startPromise = null;
27021
27022
  stopPromise = null;
27022
27023
  operations = /* @__PURE__ */ new Set();
27023
- constructor(config, logger) {
27024
+ constructor(config, logger, options = {}) {
27024
27025
  this.config = config;
27025
27026
  this.logger = logger || { info: console.log, error: console.error };
27027
+ this.relayOwnership = options.relayOwnership ?? "local";
27026
27028
  this.app = (0, import_express.default)();
27027
27029
  this.app.use(import_express.default.json());
27028
27030
  this.agentCard = {
@@ -27490,6 +27492,9 @@ var A2AServer = class {
27490
27492
  this.logger.info(`[perkos-a2a] Relay transport delegated to supervisor for ${relay.url}`);
27491
27493
  return;
27492
27494
  }
27495
+ if (this.relayOwnership === "external-required") {
27496
+ throw new Error("OWNERSHIP_PRIMITIVE_UNAVAILABLE:OpenClaw relay transport requires the process-external supervisor");
27497
+ }
27493
27498
  this.relayClient = new RelayClient({
27494
27499
  agentName: this.config.agentName,
27495
27500
  relay,
@@ -27712,7 +27717,7 @@ var A2AServer = class {
27712
27717
 
27713
27718
  // src/index.ts
27714
27719
  import { randomUUID as randomUUID7 } from "crypto";
27715
- import { readFileSync as readFileSync4 } from "node:fs";
27720
+ import { readFileSync as readFileSync5 } from "node:fs";
27716
27721
  import { dirname as dirname2, resolve as resolve3 } from "node:path";
27717
27722
  import { fileURLToPath as fileURLToPath2 } from "node:url";
27718
27723
 
@@ -28334,6 +28339,7 @@ function isNotFound(err) {
28334
28339
  // src/openclaw-supervisor-client.ts
28335
28340
  import { randomUUID as randomUUID5 } from "node:crypto";
28336
28341
  import { spawn } from "node:child_process";
28342
+ import { existsSync, readFileSync as readFileSync3, unlinkSync } from "node:fs";
28337
28343
  import { homedir as homedir3 } from "node:os";
28338
28344
  import net from "node:net";
28339
28345
  import { resolve } from "node:path";
@@ -28344,7 +28350,7 @@ import { createHash as createHash2 } from "node:crypto";
28344
28350
  import { chmodSync, mkdirSync, readFileSync as readFileSync2, renameSync, writeFileSync } from "node:fs";
28345
28351
  import { tmpdir } from "node:os";
28346
28352
  import { dirname, join as join2 } from "node:path";
28347
- var SUPERVISOR_PROTOCOL_VERSION = 1;
28353
+ var SUPERVISOR_PROTOCOL_VERSION = 2;
28348
28354
  var SUPERVISOR_RENEW_MS = 5e3;
28349
28355
  function supervisorOwnerHash(lifecycleKey) {
28350
28356
  return createHash2("sha256").update(lifecycleKey).digest("hex");
@@ -28358,6 +28364,9 @@ function supervisorSocketPath(stateDir, ownerHash) {
28358
28364
  const uid = typeof process.getuid === "function" ? process.getuid() : "user";
28359
28365
  return join2(tmpdir(), `perkos-a2a-${uid}`, `${ownerHash.slice(0, 32)}.sock`);
28360
28366
  }
28367
+ function supervisorBootstrapPath(stateDir, ownerHash) {
28368
+ return join2(supervisorDir(stateDir), `${ownerHash}.bootstrap.json`);
28369
+ }
28361
28370
  function currentBootId() {
28362
28371
  try {
28363
28372
  return readFileSync2("/proc/sys/kernel/random/boot_id", "utf8").trim();
@@ -28388,6 +28397,7 @@ var OpenClawSupervisorClient = class {
28388
28397
  config;
28389
28398
  logger;
28390
28399
  handlers;
28400
+ supervisorCliPath;
28391
28401
  lease;
28392
28402
  socket = null;
28393
28403
  startPromise = null;
@@ -28403,6 +28413,7 @@ var OpenClawSupervisorClient = class {
28403
28413
  this.config = input.config;
28404
28414
  this.logger = input.logger;
28405
28415
  this.handlers = input.handlers;
28416
+ this.supervisorCliPath = input.supervisorCliPath;
28406
28417
  this.lease = {
28407
28418
  ownerKey: input.ownerHash,
28408
28419
  instanceId: input.instanceId,
@@ -28428,13 +28439,18 @@ var OpenClawSupervisorClient = class {
28428
28439
  this.stopped = false;
28429
28440
  const starting = (async () => {
28430
28441
  await this.connectOrSpawn();
28431
- await this.requestWithMessage({
28432
- protocolVersion: 1,
28442
+ const claim = await this.requestWithMessage({
28443
+ protocolVersion: 2,
28433
28444
  type: "claim",
28434
28445
  requestId: randomUUID5(),
28435
28446
  lease: this.freshLease(),
28436
28447
  config: this.config
28437
28448
  }, 15e3);
28449
+ const health = claim?.health;
28450
+ if (claim?.state !== "READY" || health?.schemaVersion !== 2 || health.authority !== "process-external-supervisor" || health.state !== "running" || health.ownerKey !== this.ownerHash || health.instanceId !== this.lease.instanceId || health.generation !== this.lease.generation || health.relayClientCount !== (this.config.relay?.enabled && this.config.relay.url ? 1 : 0) || !health.claimedAt) {
28451
+ throw new Error("OWNERSHIP_PRIMITIVE_UNAVAILABLE:supervisor claim did not return certified READY/CLAIMED health");
28452
+ }
28453
+ this.health = health;
28438
28454
  this.renewTimer = setInterval(() => this.renew(), SUPERVISOR_RENEW_MS);
28439
28455
  this.renewTimer.unref?.();
28440
28456
  this.logger.info(`[perkos-a2a] Supervisor owner ready generation=${this.lease.generation} instanceId=${this.lease.instanceId}`);
@@ -28455,7 +28471,7 @@ var OpenClawSupervisorClient = class {
28455
28471
  const requestId = randomUUID5();
28456
28472
  try {
28457
28473
  await this.requestWithMessage({
28458
- protocolVersion: 1,
28474
+ protocolVersion: 2,
28459
28475
  type: "release",
28460
28476
  requestId,
28461
28477
  lease: this.freshLease()
@@ -28484,14 +28500,14 @@ var OpenClawSupervisorClient = class {
28484
28500
  return await this.request("discover", {}, 15e3);
28485
28501
  }
28486
28502
  sendTaskResponse(original, result) {
28487
- this.write({ protocolVersion: 1, type: "relayTaskResult", lease: this.freshLease(), eventId: randomUUID5(), original, result });
28503
+ this.write({ protocolVersion: 2, type: "relayTaskResult", lease: this.freshLease(), eventId: randomUUID5(), original, result });
28488
28504
  }
28489
28505
  async sendChatReply(payload) {
28490
28506
  return await this.request("chatReply", payload, 15e3);
28491
28507
  }
28492
28508
  async request(method, payload, timeoutMs) {
28493
28509
  const requestId = randomUUID5();
28494
- return this.requestWithMessage({ protocolVersion: 1, type: "request", requestId, lease: this.freshLease(), method, payload }, timeoutMs);
28510
+ return this.requestWithMessage({ protocolVersion: 2, type: "request", requestId, lease: this.freshLease(), method, payload }, timeoutMs);
28495
28511
  }
28496
28512
  async requestWithMessage(message, timeoutMs) {
28497
28513
  return new Promise((resolveRequest, rejectRequest) => {
@@ -28516,7 +28532,7 @@ var OpenClawSupervisorClient = class {
28516
28532
  }
28517
28533
  renew() {
28518
28534
  if (this.stopped || !this.socket || this.socket.destroyed) return;
28519
- this.write({ protocolVersion: 1, type: "renew", lease: this.freshLease() });
28535
+ this.write({ protocolVersion: 2, type: "renew", lease: this.freshLease() });
28520
28536
  }
28521
28537
  async connectOrSpawn() {
28522
28538
  try {
@@ -28524,12 +28540,27 @@ var OpenClawSupervisorClient = class {
28524
28540
  return;
28525
28541
  } catch {
28526
28542
  }
28527
- const cliPath = fileURLToPath(new URL("./openclaw-supervisor-cli.js", import.meta.url));
28543
+ const cliPath = this.supervisorCliPath ?? fileURLToPath(new URL("./openclaw-supervisor-cli.js", import.meta.url));
28544
+ if (!existsSync(cliPath)) {
28545
+ throw new Error(`OWNERSHIP_PRIMITIVE_UNAVAILABLE:supervisor CLI is missing at ${cliPath}`);
28546
+ }
28547
+ const bootstrapPath = supervisorBootstrapPath(this.stateDir, this.ownerHash);
28548
+ try {
28549
+ unlinkSync(bootstrapPath);
28550
+ } catch {
28551
+ }
28552
+ let childExit;
28528
28553
  const child = spawn(process.execPath, [cliPath, "--state-dir", this.stateDir, "--owner-key", this.ownerHash], {
28529
28554
  detached: true,
28530
28555
  stdio: "ignore",
28531
28556
  env: { ...process.env }
28532
28557
  });
28558
+ child.once("error", (error) => {
28559
+ childExit = `spawn error: ${error.message}`;
28560
+ });
28561
+ child.once("exit", (code, signal) => {
28562
+ childExit = `exit code=${code ?? "null"} signal=${signal ?? "none"}`;
28563
+ });
28533
28564
  child.unref();
28534
28565
  const deadline = Date.now() + 1e4;
28535
28566
  let lastError;
@@ -28541,9 +28572,22 @@ var OpenClawSupervisorClient = class {
28541
28572
  } catch (error) {
28542
28573
  lastError = error;
28543
28574
  }
28575
+ const bootstrap = this.readBootstrapStatus(bootstrapPath);
28576
+ if (bootstrap?.state === "failed") {
28577
+ throw new Error(`OWNERSHIP_PRIMITIVE_UNAVAILABLE:${bootstrap.errorCode || "SUPERVISOR_BOOT_FAILED"}:${bootstrap.message || "supervisor failed before READY"}`);
28578
+ }
28579
+ if (childExit) throw new Error(`OWNERSHIP_PRIMITIVE_UNAVAILABLE:supervisor ${childExit}`);
28544
28580
  }
28545
28581
  throw new Error(`OWNERSHIP_PRIMITIVE_UNAVAILABLE:${lastError instanceof Error ? lastError.message : String(lastError)}`);
28546
28582
  }
28583
+ readBootstrapStatus(path) {
28584
+ try {
28585
+ const value = JSON.parse(readFileSync3(path, "utf8"));
28586
+ return value?.schemaVersion === 1 && value.ownerKey === this.ownerHash ? value : null;
28587
+ } catch {
28588
+ return null;
28589
+ }
28590
+ }
28547
28591
  connectSocket(timeoutMs) {
28548
28592
  this.closeSocket(new Error("SUPERVISOR_RECONNECT"));
28549
28593
  return new Promise((resolveConnect, rejectConnect) => {
@@ -28609,11 +28653,11 @@ var OpenClawSupervisorClient = class {
28609
28653
  if (event.type === "relayTask") {
28610
28654
  try {
28611
28655
  const result = await this.handlers.onRelayTask(event.message);
28612
- this.write({ protocolVersion: 1, type: "relayTaskResult", lease: this.freshLease(), eventId: event.eventId, original: event.message, result });
28656
+ this.write({ protocolVersion: 2, type: "relayTaskResult", lease: this.freshLease(), eventId: event.eventId, original: event.message, result });
28613
28657
  } catch (error) {
28614
28658
  const message = error instanceof Error ? error.message : String(error);
28615
28659
  this.write({
28616
- protocolVersion: 1,
28660
+ protocolVersion: 2,
28617
28661
  type: "relayTaskResult",
28618
28662
  lease: this.freshLease(),
28619
28663
  eventId: event.eventId,
@@ -28649,13 +28693,13 @@ import { createHmac } from "node:crypto";
28649
28693
  import {
28650
28694
  chmodSync as chmodSync2,
28651
28695
  mkdirSync as mkdirSync2,
28652
- readFileSync as readFileSync3,
28696
+ readFileSync as readFileSync4,
28653
28697
  renameSync as renameSync2,
28654
28698
  writeFileSync as writeFileSync2
28655
28699
  } from "node:fs";
28656
28700
  import { homedir as homedir4 } from "node:os";
28657
28701
  import { join as join3, resolve as resolve2 } from "node:path";
28658
- var RUNTIME_EVIDENCE_SCHEMA_VERSION = 1;
28702
+ var RUNTIME_EVIDENCE_SCHEMA_VERSION = 2;
28659
28703
  function canonicalize(value) {
28660
28704
  if (Array.isArray(value)) return value.map(canonicalize);
28661
28705
  if (!value || typeof value !== "object") return value;
@@ -28688,7 +28732,7 @@ function recordRuntimeLoadEvidence(input) {
28688
28732
  const evidenceDir = runtimeEvidenceDir(input.stateDir);
28689
28733
  let attempt;
28690
28734
  try {
28691
- attempt = JSON.parse(readFileSync3(runtimeAttemptPath(input.stateDir), "utf8"));
28735
+ attempt = JSON.parse(readFileSync4(runtimeAttemptPath(input.stateDir), "utf8"));
28692
28736
  } catch {
28693
28737
  input.logger.info(`[perkos-a2a] config loaded version=${input.version} agentId=${configAgentId(input.config)} attemptId=none`);
28694
28738
  return null;
@@ -28706,6 +28750,8 @@ function recordRuntimeLoadEvidence(input) {
28706
28750
  fingerprint: configFingerprint(input.config, attempt.hmacKeyHex),
28707
28751
  attemptId: attempt.attemptId,
28708
28752
  pid: process.pid,
28753
+ processStartTime: processStartIdentity(),
28754
+ bootId: currentBootId(),
28709
28755
  pluginRoot: resolve2(input.pluginRoot)
28710
28756
  };
28711
28757
  mkdirSync2(evidenceDir, { recursive: true, mode: 448 });
@@ -29149,7 +29195,7 @@ function isWebhookEvent(msg) {
29149
29195
  // src/index.ts
29150
29196
  function loadedPluginVersion() {
29151
29197
  try {
29152
- const manifest = JSON.parse(readFileSync4(new URL("../openclaw.plugin.json", import.meta.url), "utf8"));
29198
+ const manifest = JSON.parse(readFileSync5(new URL("../openclaw.plugin.json", import.meta.url), "utf8"));
29153
29199
  return typeof manifest.version === "string" ? manifest.version : "unknown";
29154
29200
  } catch {
29155
29201
  return "unknown";
@@ -29438,7 +29484,7 @@ function register(api) {
29438
29484
  pluginRoot: resolve3(dirname2(fileURLToPath2(import.meta.url)), ".."),
29439
29485
  logger
29440
29486
  });
29441
- const server = new A2AServer(pluginConfig, logger);
29487
+ const server = new A2AServer(pluginConfig, logger, { relayOwnership: "external-required" });
29442
29488
  const lifecycleKey = a2aLifecycleKey(pluginConfig);
29443
29489
  const lifecycleInstanceId = randomUUID7();
29444
29490
  const pendingTasks = [];
@@ -29731,9 +29777,17 @@ function register(api) {
29731
29777
  api.registerService({
29732
29778
  id: "perkos-a2a",
29733
29779
  start: async () => {
29734
- await supervisorClient.start();
29735
- await server.start();
29736
- logger.info(`[perkos-a2a] A2A server started for ${pluginConfig.agentName}`);
29780
+ try {
29781
+ await supervisorClient.start();
29782
+ await server.start();
29783
+ logger.info(`[perkos-a2a] A2A server started for ${pluginConfig.agentName}`);
29784
+ } catch (error) {
29785
+ await server.stop({ reason: "openclaw-service-start-failed", timeoutMs: 2e3, drainMs: 0 }).catch(() => void 0);
29786
+ await supervisorClient.stop().catch(() => void 0);
29787
+ const message = error instanceof Error ? error.message : String(error);
29788
+ logger.error(`[perkos-a2a] OWNERSHIP_START_FAILED:${message}`);
29789
+ throw error;
29790
+ }
29737
29791
  },
29738
29792
  stop: async () => {
29739
29793
  logger.info("[perkos-a2a] A2A server stopping");