@cotal-ai/manager 0.71.0 → 0.72.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/dist/manager.js CHANGED
@@ -4,7 +4,7 @@ import { credsAuthenticator } from "@nats-io/transport-node";
4
4
  import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync } from "node:fs";
5
5
  import { isAbsolute, join, dirname, resolve } from "node:path";
6
6
  import Headless from "@xterm/headless";
7
- import { CotalEndpoint, MANAGED_HANDOFF_KIND, accountFromCreds, credsClaims, DEFAULT_SERVER, DEV_OWNER, MANAGER_LEASE_RENEW_MS, STANDING_RENEWABLE_TTL_SEC, assertLifecycleToken, discardLaunchArtifacts, divergentSecretStoreNotice, parseDaemonStoreAnswer, parseSecretStoreIdentity, sameSecretStoreIdentity, agentFilePath, clearSpaceHistory, dialerFor, connectorServers, spawnEnvAllow, deprovisionAgent, DeprovisionError, isConcreteChannel, deprovisionTargetPrincipal, firstFreeName, spawnNameError, idFromCreds, inspectCredHealth, credsRenewalDelayMs, composeWirePersona, loadAgentFile, listPersonaCatalog, personaCatalogDescription, personaCatalogReadable, loadCotalConfig, modelPolicyRefusal, mintCreds, mintGeneration, mintAcceptedToken, openIssuedStore, acceptedBucket, issuedBucket, mintLifecycleUid, managedRetirementOpId, mkSecretDir, newIdentity, actionContext, parsePrincipalKey, principalKey, probeConnect, provisionAgent, provisionAgentDurables, registry, resolveAuthProvider, resolveReadAcl, runAgentBearer, saveAgentFile, subjectMatches, AUTH_ENDPOINT, EP_CMD_RETIRE_LIFECYCLE, controlServiceSubject, eventChannelPrincipal, resolveService, invokeCommand, } from "@cotal-ai/core";
7
+ import { CotalEndpoint, canonicalJson, MANAGED_HANDOFF_KIND, accountFromCreds, credsClaims, DEFAULT_SERVER, DEV_OWNER, MANAGER_LEASE_RENEW_MS, STANDING_RENEWABLE_TTL_SEC, assertLifecycleToken, discardLaunchArtifacts, divergentSecretStoreNotice, parseDaemonStoreAnswer, parseSecretStoreIdentity, sameSecretStoreIdentity, agentFilePath, clearSpaceHistory, dialerFor, connectorServers, spawnEnvAllow, deprovisionAgent, DeprovisionError, isConcreteChannel, deprovisionTargetPrincipal, firstFreeName, spawnNameError, idFromCreds, inspectCredHealth, credsRenewalDelayMs, composeWirePersona, loadAgentFile, listPersonaCatalog, personaCatalogDescription, personaCatalogReadable, loadCotalConfig, modelPolicyRefusal, mintCreds, mintGeneration, mintAcceptedToken, openIssuedStore, acceptedBucket, issuedBucket, mintLifecycleUid, managedRetirementOpId, mkSecretDir, newIdentity, actionContext, parsePrincipalKey, principalKey, probeConnect, provisionAgent, provisionAgentDurables, registry, resolveAuthProvider, resolveReadAcl, runAgentBearer, saveAgentFile, subjectMatches, AUTH_ENDPOINT, EP_CMD_RETIRE_LIFECYCLE, controlServiceSubject, eventChannelPrincipal, resolveService, invokeCommand, } from "@cotal-ai/core";
8
8
  import { agentAuthState, agentCredsDir, agentLifecycleSecretFilePaths, agentSecretFilePaths, agentSecretKeyForFile, authDir, claimManagerSiblingIdentities, connectorInstallHint, DELIVERY_CREDS_KIND, extensionConnectors, findCotalRoot, getSpaceAuth, hasUserAuthState, loadExtensionsManifest, loadManagerInstanceIdentity, loadMeshes, localTrustOfSpace, manifestExtensionNames, materializeFromManifest, materializeSecretToFile, MEMBERSHIP_RW_CREDS_KIND, mergeLaunchOptions, remintDaemonCreds, resolveAgentType, resolveOnPath, createManagerInstanceIdentity, spaceMaterialKey, SYSTEM_CREDS_FILES, userAuthStateDir, workspaceSecretStore, writeRenewalRecord } from "@cotal-ai/workspace";
9
9
  import { createRuntime, isCustodialRuntime, requireRuntimeAdopt, requireRuntimeReap, RuntimeReapUnproven, } from "./runtime/index.js";
10
10
  import { AttachEndpoint } from "./attach-endpoint.js";
@@ -22,7 +22,7 @@ import { TranscriptReceiver } from "./transcript-receive.js";
22
22
  // manager-side adapter (transport + slot orchestration + the F1 terminal) — see static-lifecycle.ts.
23
23
  import { jetstream, jetstreamManager } from "@nats-io/jetstream";
24
24
  import { Kvm } from "@nats-io/kv";
25
- import { recordsBucket, epAuthBucket, ensureAuthorityStores, ensureContractStore, createEndpointStreams, contractStoreContext, publishContractArtifact, contractArtifactCanonicalBytes, standaloneConnectOpts, STATIC_SLOT_PREFIX, rawDigest, STANDING_RENEWABLE_TTL_SEC as MANAGED_STATIC_TTL_SEC, newArtifactSigner, RotatingSigner, generationAnchor, sessionsBucket, SESSION_GRANT_MAX_TTL_MS, REMOTE_MANAGER_IDENTITY_NAMES, } from "@cotal-ai/core";
25
+ import { recordsBucket, epAuthBucket, ensureAuthorityStores, ensureContractStore, createEndpointStreams, contractStoreContext, publishContractArtifact, contractArtifactCanonicalBytes, standaloneConnectOpts, rawDigest, STANDING_RENEWABLE_TTL_SEC as MANAGED_STATIC_TTL_SEC, newArtifactSigner, RotatingSigner, generationAnchor, sessionsBucket, SESSION_GRANT_MAX_TTL_MS, REMOTE_MANAGER_IDENTITY_NAMES, } from "@cotal-ai/core";
26
26
  // P2 item 6: the manager's ONE §13.6 session plane — offer mint + one-use redeem + PTY-bridge
27
27
  // standup for `attach`, over a dedicated standing session-LEDGER connection (the byte rails ride
28
28
  // per-session credentials on their own short-lived connections).
@@ -32,7 +32,7 @@ import { ManagerSessionPlane, openSessionLedgerKv } from "./session/index.js";
32
32
  // the register/authorize/serve seams, all driven over a scoped one-shot executor connection.
33
33
  import { provisionEndpointGateOpen, endpointRegistrationBarrier, serveIssuanceGateKv, commitSiblingIssuance, markLedgerRowRevoked, epcredRowKey, epgateKey, parseEndpointGate, readEndpointGateGeneration, registerServingInstance, deregisterServiceInstance, serveEndpoint, bindGoal, createGoal, transitionGoal, commitGoalResult, requestGoalCancel, goalAlreadyTerminal, settleGoalUncertain, readGoalResult, readGoalStatus, readGoalSpec, recordGoalIndex, readGoalIndex, clearGoalIndex, listGoalIndex, mintCheckpoint, resumeCheckpoint, readCheckpointSettle, readCheckpointSpec, expireCheckpoint, GOAL_TERMINAL_STATES, goalRefOf, goalProgressTopic, epeSubject, submissionFingerprint, EpEnvelopeError, lifecycleBlocked, lifecycleBlockedFrom, renderLifecycleBlocked, } from "@cotal-ai/core";
34
34
  import { MANAGER_ENDPOINT, managerClusterArtifacts, managerCommandDefs, managerContractArtifactValues } from "./manager-service-contract.js";
35
- import { staticLifecycleTransport, activateStaticLifecycle, runStaticTerminal, readStaticSlot, casStaticSlot, recordSlotCredential, appendStaticCredentialRow, planStaticSlotResume, ownedBySibling, observeStaticSlot, listStaticSlotObservations, renderStaticSlotObservation, StaticSlotReadError, STATIC_SLOT_READ_FAILED_DETAIL, } from "./static-lifecycle.js";
35
+ import { staticLifecycleTransport, activateStaticLifecycle, runStaticTerminal, readStaticSlot, casStaticSlot, recordSlotCredential, appendStaticCredentialRow, planStaticSlotResume, ownedBySibling, observeStaticSlot, listStaticSlotObservations, walkStaticSlots, renderStaticSlotObservation, StaticSlotReadError, STATIC_SLOT_READ_FAILED_DETAIL, } from "./static-lifecycle.js";
36
36
  /** Minimum slot lifetime for rate-flooring (P4c). A slot freed (by despawn OR natural exit/reap)
37
37
  * before living this long leaves a cooling stamp that still counts toward the ceiling until it
38
38
  * expires — so churn (spawn↔despawn or spawn↔fast-exit) can't outrun the concurrency bound. */
@@ -126,9 +126,11 @@ function retireOpId(lifecycleUid) {
126
126
  * dash) — what {@link Manager.psOwnerFilter} returns for an unparseable caller so a malformed
127
127
  * principal fail-closes to an empty `ps` instead of an unbounded one. */
128
128
  const NO_OWNER_MATCHES = "-no-owner-";
129
- /** A caught value's text for a refusal. A host-supplied store or callback may reject with any value,
130
- * including one whose `message` is a Symbol or whose `message` getter or `toString` throws, and the
131
- * refusal must still be returned, so the coercion to text runs inside the guard. */
129
+ /** A caught value's text for a refusal or a log line. A host-supplied store, runtime or callback may
130
+ * reject with any value, including `null` or one whose `message` is a Symbol or whose `message`
131
+ * getter or `toString` throws. A handler that throws while building its text skips the work after
132
+ * it, and on a detached chain ends the manager with an unhandled rejection, so the coercion to text
133
+ * runs inside the guard. */
132
134
  function rejectionText(e) {
133
135
  try {
134
136
  return String(e?.message ?? e);
@@ -137,6 +139,34 @@ function rejectionText(e) {
137
139
  return "an unreadable rejection";
138
140
  }
139
141
  }
142
+ /** A caught value as a lifecycle envelope, or undefined when it is not one. A thrown Proxy can throw
143
+ * from `instanceof` itself, so the test runs inside the guard. */
144
+ function caughtEnvelope(e) {
145
+ try {
146
+ return e instanceof EpEnvelopeError ? e : undefined;
147
+ }
148
+ catch {
149
+ return undefined;
150
+ }
151
+ }
152
+ /** The failed terminal a post-accept fallback commits for `error` and `source`'s optional details.
153
+ * `commitGoalResult` refuses a payload that is not interchangeable JSON, and this terminal must commit
154
+ * whatever failed, so a lone surrogate in the text becomes U+FFFD and details it cannot read or carry
155
+ * (a Proxy's throwing getter, an accessor, a cycle) are dropped. A Proxy can answer the check and the
156
+ * serializer differently, so the detached copy is checked again before it commits. */
157
+ function failedTerminal(error, source) {
158
+ const text = error.replace(/\p{Cs}/gu, "\uFFFD");
159
+ try {
160
+ const details = source?.details;
161
+ if (details) {
162
+ const copy = JSON.parse(canonicalJson(details));
163
+ canonicalJson(copy);
164
+ return { error: text, details: copy };
165
+ }
166
+ }
167
+ catch { /* unreadable or not interchangeable */ }
168
+ return { error: text };
169
+ }
140
170
  /** Run one cleanup step, recording its failure in `failed` as `<what>: <reason>`. A step that threw
141
171
  * would otherwise skip every step after it, and a swallowed one would leave nothing to report. */
142
172
  async function attemptCleanup(failed, what, step) {
@@ -911,7 +941,7 @@ export class Manager {
911
941
  await this.attach.stop();
912
942
  throw new Error(held
913
943
  ? `manager instance ${this.managerInstanceId} already serves space "${this.space}" from ${held.root === this.leaseInfo.root ? "this" : "another"} workspace root (${held.runtime}, pid ${held.pid}, root ${held.root}) - stop it first before restarting the same instance`
914
- : `could not acquire the manager lease for space "${this.space}": ${e.message}`);
944
+ : `could not acquire the manager lease for space "${this.space}": ${rejectionText(e)}`);
915
945
  }
916
946
  this.leaseTimer = setInterval(() => { void this.renewLease(); }, MANAGER_LEASE_RENEW_MS);
917
947
  this.leaseTimer.unref?.();
@@ -948,7 +978,7 @@ export class Manager {
948
978
  // attempt returns.
949
979
  const startupReconcile = this.auth && !this.userMode ? this.reconcileStaticLifecycles() : undefined;
950
980
  if (startupReconcile)
951
- void startupReconcile.catch((e) => console.error(`! ${STARTUP_RECONCILING}: ${e.message} - no per-alias retry could be planned; a later manager start re-reads unfinished durable terminals`));
981
+ void startupReconcile.catch((e) => console.error(`! ${STARTUP_RECONCILING}: ${rejectionText(e)} - no per-alias retry could be planned; a later manager start re-reads unfinished durable terminals`));
952
982
  // P2 item 1 (1d): the manager serves NO ctl tiers - its whole control surface is the v0.4
953
983
  // service endpoint registered below. The old three-tier rail (self/manager/admin) is deleted;
954
984
  // `ctl.delivery`/`ctl.delivery-admin` (the delivery daemon) and `ctl.auth-admin` (the auth
@@ -999,13 +1029,13 @@ export class Manager {
999
1029
  await this.renewRemoteExecutor();
1000
1030
  }
1001
1031
  catch (e) {
1002
- console.error(`! remote manager renewal pass failed: ${e.message}`);
1032
+ console.error(`! remote manager renewal pass failed: ${rejectionText(e)}`);
1003
1033
  }
1004
1034
  try {
1005
1035
  await this.runHosting?.renew();
1006
1036
  }
1007
1037
  catch (e) {
1008
- console.error(`! remote run-hosting renewal pass failed: ${e.message}`);
1038
+ console.error(`! remote run-hosting renewal pass failed: ${rejectionText(e)}`);
1009
1039
  }
1010
1040
  }, intervalMs);
1011
1041
  this.credRenewTimer.unref?.();
@@ -1116,7 +1146,7 @@ export class Manager {
1116
1146
  reply = await this.ep.requestDeliveryAdmin("reloadStoreIdentity", {}, timeoutMs);
1117
1147
  }
1118
1148
  catch (e) {
1119
- const msg = e.message;
1149
+ const msg = rejectionText(e);
1120
1150
  if (isAbsentDeliveryAdmin(msg))
1121
1151
  return this.absentByLeaseRow(isUnansweredDeliveryAdmin(e));
1122
1152
  const failure = `could not challenge the delivery daemon's SecretStore: ${msg}`;
@@ -1129,7 +1159,7 @@ export class Manager {
1129
1159
  answer = parseDaemonStoreAnswer(reply.data);
1130
1160
  }
1131
1161
  catch (e) {
1132
- throw new Error(`delivery daemon named an unreadable SecretStore: ${e.message}. A daemon older ` +
1162
+ throw new Error(`delivery daemon named an unreadable SecretStore: ${rejectionText(e)}. A daemon older ` +
1133
1163
  `than #1694 replies with a bare store identity and no answerer binding; this manager ` +
1134
1164
  `cannot establish which process answered, so nothing reminted - upgrade the delivery daemon`);
1135
1165
  }
@@ -1185,7 +1215,7 @@ export class Manager {
1185
1215
  }
1186
1216
  catch (e) {
1187
1217
  throw new Error(`could not read the delivery lease row to settle whether the delivery daemon is absent ` +
1188
- `(${e.message}) - absence is undetermined, nothing reminted`);
1218
+ `(${rejectionText(e)}) - absence is undetermined, nothing reminted`);
1189
1219
  }
1190
1220
  const holder = entry?.info.holder;
1191
1221
  if (holder !== undefined && holder !== "") {
@@ -1210,7 +1240,7 @@ export class Manager {
1210
1240
  this.daemonRenewalOwner = held;
1211
1241
  }
1212
1242
  catch (e) {
1213
- console.error(`! could not refresh the daemon renewal lease: ${e.message} - ownership is re-decided on the next pass`);
1243
+ console.error(`! could not refresh the daemon renewal lease: ${rejectionText(e)} - ownership is re-decided on the next pass`);
1214
1244
  }
1215
1245
  }
1216
1246
  /** Decide whether THIS manager owns the daemon's credential renewal (#1634). Both conditions are
@@ -1296,7 +1326,7 @@ export class Manager {
1296
1326
  return true;
1297
1327
  }
1298
1328
  catch (e) {
1299
- console.error(`! manager ${label} re-dial attempt failed: ${e.message}`);
1329
+ console.error(`! manager ${label} re-dial attempt failed: ${rejectionText(e)}`);
1300
1330
  }
1301
1331
  }
1302
1332
  return false;
@@ -1349,7 +1379,7 @@ export class Manager {
1349
1379
  : { ok: false, error: reply.error, detail: reply.data };
1350
1380
  }
1351
1381
  catch (e) {
1352
- adoption = { ok: false, error: `no delivery-admin responder (${e.message}) - the daemon's 75% re-read backstop adopts the re-signed file` };
1382
+ adoption = { ok: false, error: `no delivery-admin responder (${rejectionText(e)}) - the daemon's 75% re-read backstop adopts the re-signed file` };
1353
1383
  }
1354
1384
  }
1355
1385
  for (const r of results.filter((x) => !x.ok && !x.skipped))
@@ -1392,7 +1422,7 @@ export class Manager {
1392
1422
  await this.renewManagedStaticCred(a);
1393
1423
  }
1394
1424
  catch (e) {
1395
- console.error(`! managed cred renewal ${a.name}: ${e.message} - the agent dies loud at this cred's expiry unless it is reminted`);
1425
+ console.error(`! managed cred renewal ${a.name}: ${rejectionText(e)} - the agent dies loud at this cred's expiry unless it is reminted`);
1396
1426
  }
1397
1427
  }
1398
1428
  }
@@ -1420,7 +1450,7 @@ export class Manager {
1420
1450
  }
1421
1451
  }
1422
1452
  catch (e) {
1423
- console.error(`! endpoint-serve renewal: ${e.message} - the manager's service endpoint dies loud at this cred's expiry unless it is re-registered`);
1453
+ console.error(`! endpoint-serve renewal: ${rejectionText(e)} - the manager's service endpoint dies loud at this cred's expiry unless it is re-registered`);
1424
1454
  }
1425
1455
  }
1426
1456
  // The manager is also the goal-writer's renewal owner — re-mint the SAME
@@ -1439,7 +1469,7 @@ export class Manager {
1439
1469
  }
1440
1470
  }
1441
1471
  catch (e) {
1442
- console.error(`! goal-writer renewal: ${e.message} - spawn-as-action stops accepting at this cred's expiry unless the manager restarts`);
1472
+ console.error(`! goal-writer renewal: ${rejectionText(e)} - spawn-as-action stops accepting at this cred's expiry unless the manager restarts`);
1443
1473
  }
1444
1474
  }
1445
1475
  // P2 item 6: the manager is also the session-ledger's renewal owner — re-mint the SAME nkey
@@ -1461,12 +1491,12 @@ export class Manager {
1461
1491
  }
1462
1492
  }
1463
1493
  catch (e) {
1464
- console.error(`! session-ledger renewal: ${e.message} - attach stops establishing sessions at this cred's expiry unless the manager restarts`);
1494
+ console.error(`! session-ledger renewal: ${rejectionText(e)} - attach stops establishing sessions at this cred's expiry unless the manager restarts`);
1465
1495
  }
1466
1496
  }
1467
1497
  }
1468
1498
  catch (e) {
1469
- console.error(`! credential renewal pass failed: ${e.message}`);
1499
+ console.error(`! credential renewal pass failed: ${rejectionText(e)}`);
1470
1500
  }
1471
1501
  finally {
1472
1502
  release();
@@ -1484,7 +1514,7 @@ export class Manager {
1484
1514
  this.remoteExecutorCreds = await this.remoteAuthority.renewExecutor();
1485
1515
  }
1486
1516
  catch (e) {
1487
- console.error(`! remote manager executor renewal: ${e.message} - registration maintenance and clean deregistration remain unavailable until renewal succeeds`);
1517
+ console.error(`! remote manager executor renewal: ${rejectionText(e)} - registration maintenance and clean deregistration remain unavailable until renewal succeeds`);
1488
1518
  if (force)
1489
1519
  throw e;
1490
1520
  }
@@ -1555,7 +1585,7 @@ export class Manager {
1555
1585
  // endpoint's own reconnect loop retries with the adopted credential, not unadopted debt.
1556
1586
  this.remoteRenewalDebt = undefined;
1557
1587
  await Promise.all([
1558
- this.ep.reconnect().catch((e) => console.error(`! remote manager supervisor reconnect after renewal: ${e.message}`)),
1588
+ this.ep.reconnect().catch((e) => console.error(`! remote manager supervisor reconnect after renewal: ${rejectionText(e)}`)),
1559
1589
  this.pushRenewedCreds(this.serviceServe.nc),
1560
1590
  ...(this.goalWriter ? [this.pushRenewedCreds(this.goalWriter.nc)] : []),
1561
1591
  ...(this.sessionLedgerConn ? [this.pushRenewedCreds(this.sessionLedgerConn.nc)] : []),
@@ -1570,8 +1600,8 @@ export class Manager {
1570
1600
  return [];
1571
1601
  } })
1572
1602
  : [];
1573
- this.remoteRenewalDebt = { at: Date.now(), processEpoch, reason: e.message, unadoptedSubjects };
1574
- console.error(`! remote manager all-duty renewal: ${e.message} - no unsigned or local replacement is available`);
1603
+ this.remoteRenewalDebt = { at: Date.now(), processEpoch, reason: rejectionText(e), unadoptedSubjects };
1604
+ console.error(`! remote manager all-duty renewal: ${rejectionText(e)} - no unsigned or local replacement is available`);
1575
1605
  if (force)
1576
1606
  throw e;
1577
1607
  }
@@ -1643,7 +1673,7 @@ export class Manager {
1643
1673
  trackDeprovision(a, context = "") {
1644
1674
  this.lifecycleInFlight++;
1645
1675
  void this.deprovision(a)
1646
- .catch((e) => console.error(`deprovision${context ? ` ${context}` : ""} ${a.name} (${a.id}): ${e.message}`))
1676
+ .catch((e) => console.error(`deprovision${context ? ` ${context}` : ""} ${a.name} (${a.id}): ${rejectionText(e)}`))
1647
1677
  .finally(() => this.releaseLifecycle());
1648
1678
  }
1649
1679
  async awaitLifecycleDrain() {
@@ -1748,7 +1778,7 @@ export class Manager {
1748
1778
  parseResumeControlArgs({ attemptId, inventory });
1749
1779
  }
1750
1780
  catch (e) {
1751
- failures.push({ name: "<inventory>", id: attemptId, error: `prepared inventory would be rejected at resume: ${e.message}` });
1781
+ failures.push({ name: "<inventory>", id: attemptId, error: `prepared inventory would be rejected at resume: ${rejectionText(e)}` });
1752
1782
  }
1753
1783
  this.preservationInventory = inventory;
1754
1784
  this.preservationFailures = failures;
@@ -1833,7 +1863,7 @@ export class Manager {
1833
1863
  a.handle.stop({ graceful: false });
1834
1864
  }
1835
1865
  catch (e) {
1836
- failures.push({ name: a.name, id: a.id, error: `stop failed: ${e.message}` });
1866
+ failures.push({ name: a.name, id: a.id, error: `stop failed: ${rejectionText(e)}` });
1837
1867
  return;
1838
1868
  }
1839
1869
  try {
@@ -1842,7 +1872,7 @@ export class Manager {
1842
1872
  this.agents.delete(a.name);
1843
1873
  }
1844
1874
  catch (e) {
1845
- failures.push({ name: a.name, id: a.id, error: e.message });
1875
+ failures.push({ name: a.name, id: a.id, error: rejectionText(e) });
1846
1876
  }
1847
1877
  }));
1848
1878
  if (failures.length === 0)
@@ -1887,7 +1917,7 @@ export class Manager {
1887
1917
  return `retained reference is not a regular non-symlink file: ${path}`;
1888
1918
  }
1889
1919
  catch (e) {
1890
- return `retained reference unavailable: ${path} (${e.message})`;
1920
+ return `retained reference unavailable: ${path} (${rejectionText(e)})`;
1891
1921
  }
1892
1922
  }
1893
1923
  if (process.platform !== "win32") {
@@ -1916,7 +1946,7 @@ export class Manager {
1916
1946
  }
1917
1947
  }
1918
1948
  catch (e) {
1919
- return `retained reference cannot be hashed: ${e.message}`;
1949
+ return `retained reference cannot be hashed: ${rejectionText(e)}`;
1920
1950
  }
1921
1951
  return undefined;
1922
1952
  }
@@ -2031,11 +2061,11 @@ export class Manager {
2031
2061
  await this.awaitHandleExit(a.handle);
2032
2062
  }
2033
2063
  catch (e) {
2034
- failures.push(`${a.name}: ${e.message}`);
2064
+ failures.push(`${a.name}: ${rejectionText(e)}`);
2035
2065
  }
2036
2066
  }));
2037
2067
  // Deprovision EVERY snapshot entry regardless of whether its stop failed (allSettled + a loud log).
2038
- await Promise.allSettled(managed.filter((a) => !a.suppressCleanup).map((a) => this.deprovision({ ...a, ...(a.handedOff ? { delegatedHandle: a.handle } : {}) }).catch((e) => console.error(`deprovision ${a.name} (${a.id}) on shutdown: ${e.message}`))));
2068
+ await Promise.allSettled(managed.filter((a) => !a.suppressCleanup).map((a) => this.deprovision({ ...a, ...(a.handedOff ? { delegatedHandle: a.handle } : {}) }).catch((e) => console.error(`deprovision ${a.name} (${a.id}) on shutdown: ${rejectionText(e)}`))));
2039
2069
  if (failures.length)
2040
2070
  throw new Error(`manager shutdown could not prove every seat exited: ${failures.join("; ")}`);
2041
2071
  }
@@ -2052,7 +2082,7 @@ export class Manager {
2052
2082
  this.agents.delete(a.name);
2053
2083
  }
2054
2084
  catch (e) {
2055
- failures.push(`${a.name}: ${e.message}`);
2085
+ failures.push(`${a.name}: ${rejectionText(e)}`);
2056
2086
  }
2057
2087
  }
2058
2088
  return failures;
@@ -2071,7 +2101,7 @@ export class Manager {
2071
2101
  a.handle.stop({ graceful: false });
2072
2102
  }
2073
2103
  catch (e) {
2074
- failures.push(`${a.name}: stop failed: ${e.message}`);
2104
+ failures.push(`${a.name}: stop failed: ${rejectionText(e)}`);
2075
2105
  }
2076
2106
  try {
2077
2107
  await this.awaitHandleExit(a.handle);
@@ -2079,7 +2109,7 @@ export class Manager {
2079
2109
  this.agents.delete(a.name);
2080
2110
  }
2081
2111
  catch (e) {
2082
- failures.push(`${a.name}: ${e.message}`);
2112
+ failures.push(`${a.name}: ${rejectionText(e)}`);
2083
2113
  }
2084
2114
  }));
2085
2115
  if (failures.length)
@@ -2144,7 +2174,7 @@ export class Manager {
2144
2174
  }
2145
2175
  }
2146
2176
  catch (e) {
2147
- seatFailure = e;
2177
+ seatFailure = rejectionText(e);
2148
2178
  }
2149
2179
  // Every step below runs even when an earlier one fails, so a failed stop still closes this
2150
2180
  // process's connections and listener (#2290). Failures are collected and thrown at the end.
@@ -2154,12 +2184,12 @@ export class Manager {
2154
2184
  await run();
2155
2185
  }
2156
2186
  catch (e) {
2157
- failures.push(`${name}: ${e.message}`);
2187
+ failures.push(`${name}: ${rejectionText(e)}`);
2158
2188
  }
2159
2189
  };
2160
2190
  // A seat that may still be alive keeps this instance's leases and registration (see
2161
2191
  // teardownManagedAgents): they lapse on their TTL instead of being handed to a successor.
2162
- if (!seatFailure) {
2192
+ if (seatFailure === undefined) {
2163
2193
  await step("release manager lease", () => this.releaseOwnManagerLease());
2164
2194
  // #1634: hand the renewal lease back on a clean stop so a sibling picks it up on its next pass
2165
2195
  // rather than waiting out the bucket TTL. A non-owner holds no revision and this is a no-op.
@@ -2175,15 +2205,15 @@ export class Manager {
2175
2205
  // released run's status write is the last thing this incarnation says about it.
2176
2206
  await step("stop run hosting", () => this.runHosting?.stop());
2177
2207
  this.runHosting = undefined;
2178
- if (registrationRevision !== undefined && !seatFailure)
2208
+ if (registrationRevision !== undefined && seatFailure === undefined)
2179
2209
  await step("deregister service", () => this.deregisterServiceOnStop(registrationRevision));
2180
2210
  await step("stop goal writer", () => this.stopGoalWriter());
2181
2211
  await step("stop session plane", () => this.stopSessionPlane());
2182
2212
  await step("stop endpoint", () => this.ep.stop());
2183
2213
  await step("stop attach endpoint", () => this.attach.stop());
2184
2214
  const also = failures.length ? `; teardown also failed: ${failures.join("; ")}` : "";
2185
- if (seatFailure)
2186
- throw new Error(`${seatFailure.message}${also}`);
2215
+ if (seatFailure !== undefined)
2216
+ throw new Error(`${seatFailure}${also}`);
2187
2217
  if (releaseFailures.length)
2188
2218
  throw new Error(`manager shutdown could not release every detachable seat: ${releaseFailures.join("; ")}${also}`);
2189
2219
  if (failures.length)
@@ -2258,7 +2288,7 @@ export class Manager {
2258
2288
  // `absent` is silent: there was nothing registered to remove, which is not news at shutdown.
2259
2289
  }
2260
2290
  catch (e) {
2261
- console.error(`! could not deregister manager instance ${iid}: ${e.message}\n` +
2291
+ console.error(`! could not deregister manager instance ${iid}: ${rejectionText(e)}\n` +
2262
2292
  ` Its registration now outlives this process and will be frozen into every class scatter in space "${this.space}", each paying the full deadline.\n` +
2263
2293
  ` NEXT: remove it with \`cotal deregister-instance --instance ${iid}\` once this process is gone.`);
2264
2294
  }
@@ -2333,7 +2363,7 @@ export class Manager {
2333
2363
  return;
2334
2364
  }
2335
2365
  catch (renewError) {
2336
- const why = renewError.message;
2366
+ const why = rejectionText(renewError);
2337
2367
  const verdict = await this.reconcileLease();
2338
2368
  switch (verdict.kind) {
2339
2369
  case "held":
@@ -2358,7 +2388,7 @@ export class Manager {
2358
2388
  }
2359
2389
  catch (e) {
2360
2390
  this.leaseState = before;
2361
- this.noteLease("gone", `found its liveness lease key gone (renew: ${why}) and could not re-acquire it yet (${e.message}); serving, retrying`);
2391
+ this.noteLease("gone", `found its liveness lease key gone (renew: ${why}) and could not re-acquire it yet (${rejectionText(e)}); serving, retrying`);
2362
2392
  }
2363
2393
  return;
2364
2394
  }
@@ -2397,7 +2427,7 @@ export class Manager {
2397
2427
  current = await this.ep.readOwnManagerLease(this.managerInstanceId);
2398
2428
  }
2399
2429
  catch (e) {
2400
- return { kind: "unknown", why: e.message };
2430
+ return { kind: "unknown", why: rejectionText(e) };
2401
2431
  }
2402
2432
  if (current === undefined)
2403
2433
  return { kind: "gone" };
@@ -2414,7 +2444,7 @@ export class Manager {
2414
2444
  args = parseResumeFinalizeArgs(rawArgs);
2415
2445
  }
2416
2446
  catch (e) {
2417
- return { ok: false, error: e.message };
2447
+ return { ok: false, error: rejectionText(e) };
2418
2448
  }
2419
2449
  if (!this.resumeAttemptId || this.resumeAttemptId !== args.attemptId)
2420
2450
  return { ok: false, error: `manager expects resume attempt ${this.resumeAttemptId ?? "<none>"}, not ${args.attemptId}` };
@@ -2432,7 +2462,7 @@ export class Manager {
2432
2462
  inactive = this.resumeLivenessErrors(inventory, this.ep.getRoster());
2433
2463
  }
2434
2464
  catch (e) {
2435
- return { ok: false, error: `resume attempt ${args.attemptId} cannot verify live principals at finalize: ${e.message}` };
2465
+ return { ok: false, error: `resume attempt ${args.attemptId} cannot verify live principals at finalize: ${rejectionText(e)}` };
2436
2466
  }
2437
2467
  if (inactive.length)
2438
2468
  return { ok: false, error: `resume attempt ${args.attemptId} is not live at finalize: ${inactive.join("; ")}` };
@@ -2451,7 +2481,7 @@ export class Manager {
2451
2481
  // retiredPrincipals, so a copied JWT is refused). Best-effort + loud: a sweep failure must
2452
2482
  // not fail the finalize (the next non-resume boot re-drives it), but it is never swallowed.
2453
2483
  if (this.auth && !this.userMode)
2454
- await this.reconcileStaticLifecycles(true).catch((e) => console.error(`! post-resume static reconcile: ${e.message} - no per-alias retry could be planned; a later manager start re-reads unfinished durable terminals`));
2484
+ await this.reconcileStaticLifecycles(true).catch((e) => console.error(`! post-resume static reconcile: ${rejectionText(e)} - no per-alias retry could be planned; a later manager start re-reads unfinished durable terminals`));
2455
2485
  this.resumeFinalized = true;
2456
2486
  this.resumeRequired = false;
2457
2487
  return { ok: true, data: { attemptId: args.attemptId, state: "active" } };
@@ -2464,7 +2494,7 @@ export class Manager {
2464
2494
  attemptId = parseResumeCommitArgs(rawArgs).attemptId;
2465
2495
  }
2466
2496
  catch (e) {
2467
- return { ok: false, error: e.message };
2497
+ return { ok: false, error: rejectionText(e) };
2468
2498
  }
2469
2499
  if (!this.resumeAttemptId || this.resumeAttemptId !== attemptId)
2470
2500
  return { ok: false, error: `manager expects resume attempt ${this.resumeAttemptId ?? "<none>"}, not ${attemptId}` };
@@ -2521,7 +2551,7 @@ export class Manager {
2521
2551
  : { ok: false, data, error: result.error ?? "retained-agent resume failed" };
2522
2552
  }
2523
2553
  catch (e) {
2524
- return { ok: false, error: e.message };
2554
+ return { ok: false, error: rejectionText(e) };
2525
2555
  }
2526
2556
  }
2527
2557
  }
@@ -2548,7 +2578,7 @@ export class Manager {
2548
2578
  };
2549
2579
  }
2550
2580
  catch (e) {
2551
- return { ok: false, error: e.message };
2581
+ return { ok: false, error: rejectionText(e) };
2552
2582
  }
2553
2583
  }
2554
2584
  /** The ONE shared control-admission chokepoint (P2 item 1, checklist 3/8) BOTH dispatch doors
@@ -2779,7 +2809,7 @@ export class Manager {
2779
2809
  observed = await observeStaticSlot(recordsKv, DEV_OWNER, name, this.managerInstanceId);
2780
2810
  }
2781
2811
  catch (error) {
2782
- throw new EpEnvelopeError("unavailable", `the durable static slot state for "${name}" could not be read: ${error.message}`, [
2812
+ throw new EpEnvelopeError("unavailable", `the durable static slot state for "${name}" could not be read: ${rejectionText(error)}`, [
2783
2813
  { kind: STATIC_SLOT_READ_FAILED_DETAIL, name, record: error instanceof StaticSlotReadError ? error.record : "slot-or-head", operation: "read" },
2784
2814
  ]);
2785
2815
  }
@@ -2816,7 +2846,7 @@ export class Manager {
2816
2846
  observed = await listStaticSlotObservations(recordsKv, DEV_OWNER, this.managerInstanceId);
2817
2847
  }
2818
2848
  catch (error) {
2819
- throw new EpEnvelopeError("unavailable", `the durable static slot state could not be scanned: ${error.message}`, [
2849
+ throw new EpEnvelopeError("unavailable", `the durable static slot state could not be scanned: ${rejectionText(error)}`, [
2820
2850
  { kind: STATIC_SLOT_READ_FAILED_DETAIL, name: "*", record: error instanceof StaticSlotReadError ? error.record : "slot-or-head", operation: "scan" },
2821
2851
  ]);
2822
2852
  }
@@ -2954,7 +2984,7 @@ export class Manager {
2954
2984
  return undefined;
2955
2985
  }
2956
2986
  catch (e) {
2957
- return `${entry.name}: ${e.message}`;
2987
+ return `${entry.name}: ${rejectionText(e)}`;
2958
2988
  }
2959
2989
  }));
2960
2990
  const drift = authority.filter((error) => error !== undefined);
@@ -2965,7 +2995,7 @@ export class Manager {
2965
2995
  inactive = this.resumeLivenessErrors(inventory, this.ep.getRoster());
2966
2996
  }
2967
2997
  catch (e) {
2968
- return { ok: false, error: `resume attempt ${attemptId} cannot verify live principals: ${e.message}` };
2998
+ return { ok: false, error: `resume attempt ${attemptId} cannot verify live principals: ${rejectionText(e)}` };
2969
2999
  }
2970
3000
  if (inactive.length)
2971
3001
  return { ok: false, error: `resume attempt ${attemptId} is not live at commit: ${inactive.join("; ")}` };
@@ -3058,7 +3088,7 @@ export class Manager {
3058
3088
  }
3059
3089
  }
3060
3090
  catch (e) {
3061
- inactive.push(`${entry.name} runtime status failed: ${e.message}`);
3091
+ inactive.push(`${entry.name} runtime status failed: ${rejectionText(e)}`);
3062
3092
  continue;
3063
3093
  }
3064
3094
  if (!roster.some((presence) => presence.card.id === expectedPrincipal && presence.card.name === entry.name && presence.status !== "offline" &&
@@ -3114,7 +3144,7 @@ export class Manager {
3114
3144
  a.handle.stop({ graceful });
3115
3145
  }
3116
3146
  catch (e) {
3117
- console.error(`stop ${a.name} (${a.id}): ${e.message}`);
3147
+ console.error(`stop ${a.name} (${a.id}): ${rejectionText(e)}`);
3118
3148
  }
3119
3149
  }
3120
3150
  /** Keep an accepted stop inside the lifecycle drain until the runtime proves the child is gone,
@@ -3157,7 +3187,7 @@ export class Manager {
3157
3187
  id: a.id,
3158
3188
  handle: a.handle,
3159
3189
  authoritative: true,
3160
- error: `accepted stop could not prove exit: ${e.message}`,
3190
+ error: `accepted stop could not prove exit: ${rejectionText(e)}`,
3161
3191
  });
3162
3192
  })
3163
3193
  .finally(() => this.releaseLifecycle());
@@ -3197,7 +3227,7 @@ export class Manager {
3197
3227
  provider = resolveAuthProvider();
3198
3228
  }
3199
3229
  catch (e) {
3200
- return { error: e.message };
3230
+ return { error: rejectionText(e) };
3201
3231
  }
3202
3232
  const dir = userAuthStateDir(this.workspaceRoot, this.space);
3203
3233
  // The agent's capability scope rides its ledger row (act.scope in every bearer) — same
@@ -3305,7 +3335,7 @@ export class Manager {
3305
3335
  provider = resolveAuthProvider();
3306
3336
  }
3307
3337
  catch (e) {
3308
- return { error: e.message };
3338
+ return { error: rejectionText(e) };
3309
3339
  }
3310
3340
  const secrets = this.secrets;
3311
3341
  // The token the agent will present to `POST /exchange` for the rest of its life. Generated
@@ -3463,7 +3493,7 @@ export class Manager {
3463
3493
  catch (e) {
3464
3494
  // A runtime that throws while being asked has told us something real; it must not take the
3465
3495
  // log line (or the free path it sits on) down with it.
3466
- detail = `exit detail unreadable from runtime "${a.handle.kind}": ${e.message}`;
3496
+ detail = `exit detail unreadable from runtime "${a.handle.kind}": ${rejectionText(e)}`;
3467
3497
  }
3468
3498
  console.error(`seat reaped: ${a.name} (${a.id}, uid ${a.lifecycleUid}${a.handle.pid !== undefined ? `, pid ${a.handle.pid}` : ""}) - ${causeText}; ${detail}`);
3469
3499
  }
@@ -3683,8 +3713,8 @@ export class Manager {
3683
3713
  catch (e) {
3684
3714
  const h = this.retiring.get(a.name);
3685
3715
  if (h && h.lifecycleUid === a.lifecycleUid)
3686
- h.lastError = `the agent's standing mint authority could not be revoked (${e.message}); the name stays held so a copied actor token cannot mint fresh credentials. NEXT: a same-name spawn re-drives the full teardown (including the revoke), or recover the auth state.`;
3687
- console.error(`revoke agent grant ${a.name}: ${e.message}`);
3716
+ h.lastError = `the agent's standing mint authority could not be revoked (${rejectionText(e)}); the name stays held so a copied actor token cannot mint fresh credentials. NEXT: a same-name spawn re-drives the full teardown (including the revoke), or recover the auth state.`;
3717
+ console.error(`revoke agent grant ${a.name}: ${rejectionText(e)}`);
3688
3718
  }
3689
3719
  }
3690
3720
  const shredFailure = unshredded.length ? `could not shred "${a.name}": ${unshredded.join("; ")}` : undefined;
@@ -3697,7 +3727,7 @@ export class Manager {
3697
3727
  catch (e) {
3698
3728
  const h = this.retiring.get(a.name);
3699
3729
  if (h && h.lifecycleUid === a.lifecycleUid) {
3700
- h.lastError = e.message;
3730
+ h.lastError = rejectionText(e);
3701
3731
  if (e instanceof DeprovisionError)
3702
3732
  h.lastResources = e.accounting;
3703
3733
  }
@@ -3843,7 +3873,7 @@ export class Manager {
3843
3873
  }
3844
3874
  }
3845
3875
  catch (e) {
3846
- const copy = uncertain(e.message);
3876
+ const copy = uncertain(rejectionText(e));
3847
3877
  if (held)
3848
3878
  held.lastError = copy;
3849
3879
  console.error(`despawn ${a.name}: ${copy}`);
@@ -3937,7 +3967,7 @@ export class Manager {
3937
3967
  return { complete: true, channels: data.channels };
3938
3968
  }
3939
3969
  catch (e) {
3940
- return { complete: false, channels: [], reason: e.message };
3970
+ return { complete: false, channels: [], reason: rejectionText(e) };
3941
3971
  }
3942
3972
  }
3943
3973
  /** Reap a parent's children on its exit (P4b). Every descendant remains managed until the runtime's
@@ -3959,7 +3989,7 @@ export class Manager {
3959
3989
  parsed = JSON.parse(readFileSync(path, "utf8"));
3960
3990
  }
3961
3991
  catch (error) {
3962
- throw new Error(`cannot read connector session state ${path}: ${error.message}`);
3992
+ throw new Error(`cannot read connector session state ${path}: ${rejectionText(error)}`);
3963
3993
  }
3964
3994
  const state = parsed;
3965
3995
  if (state.version !== 1 || typeof state.sessionId !== "string" || !state.sessionId.trim() || state.sessionId.length > 4096 ||
@@ -3986,7 +4016,7 @@ export class Manager {
3986
4016
  }
3987
4017
  catch (error) {
3988
4018
  throw new Error(`retained ${connector.name} agent ${entry.name} has no sessionId in its older inventory and no usable upgrade state at ${path} ` +
3989
- `(${error.message}). Reload the live Pi seat before the preservation cut; refusing to resume fresh and lose its context.`);
4019
+ `(${rejectionText(error)}). Reload the live Pi seat before the preservation cut; refusing to resume fresh and lose its context.`);
3990
4020
  }
3991
4021
  }
3992
4022
  /** A resumed seat's fork provenance (#1500). The seat forks after it launches, so this reads the
@@ -4071,7 +4101,7 @@ export class Manager {
4071
4101
  this.recordContinuity(a, this.readManagedSession(a));
4072
4102
  }
4073
4103
  catch (error) {
4074
- console.error(`! ${a.name}: continuity: exact assignment not updated at ${boundary}: ${error.message}`);
4104
+ console.error(`! ${a.name}: continuity: exact assignment not updated at ${boundary}: ${rejectionText(error)}`);
4075
4105
  }
4076
4106
  }
4077
4107
  async awaitManagedSessionState(a) {
@@ -4084,7 +4114,7 @@ export class Manager {
4084
4114
  return this.readManagedSessionState(a);
4085
4115
  }
4086
4116
  catch (error) {
4087
- last = error.message;
4117
+ last = rejectionText(error);
4088
4118
  }
4089
4119
  await new Promise((resolve) => setTimeout(resolve, 100));
4090
4120
  }
@@ -4117,7 +4147,7 @@ export class Manager {
4117
4147
  return;
4118
4148
  }
4119
4149
  catch (error) {
4120
- last = error.message;
4150
+ last = rejectionText(error);
4121
4151
  }
4122
4152
  }
4123
4153
  await new Promise((resolve) => setTimeout(resolve, 100));
@@ -4222,7 +4252,7 @@ export class Manager {
4222
4252
  tail = await this.tail((replacement ?? a.handle).attach());
4223
4253
  }
4224
4254
  catch { /* runtime has no readable tail */ }
4225
- console.error(`! ${a.name}: ${supervised ? "supervised restart" : "Pi session recovery"} failed: ${error.message}${tail ? ` - last output: ${tail}` : ""} - retiring the managed seat`);
4255
+ console.error(`! ${a.name}: ${supervised ? "supervised restart" : "Pi session recovery"} failed: ${rejectionText(error)}${tail ? ` - last output: ${tail}` : ""} - retiring the managed seat`);
4226
4256
  // The replacement may be alive but unable to prove readiness. Stop it BEFORE
4227
4257
  // retiring credentials/durables; otherwise an untracked process survives under torn auth.
4228
4258
  try {
@@ -4262,7 +4292,7 @@ export class Manager {
4262
4292
  }
4263
4293
  }
4264
4294
  catch (error) {
4265
- console.error(`! ${a.name}: cannot classify Pi process exit for recovery: ${error.message} - retiring the seat`);
4295
+ console.error(`! ${a.name}: cannot classify Pi process exit for recovery: ${rejectionText(error)} - retiring the seat`);
4266
4296
  }
4267
4297
  }
4268
4298
  this.freeSlot(a, true, "process-exit");
@@ -4389,7 +4419,7 @@ export class Manager {
4389
4419
  shareTools = strList(args.shareTools, "shareTools");
4390
4420
  }
4391
4421
  catch (e) {
4392
- return Promise.resolve({ ok: false, error: e.message });
4422
+ return Promise.resolve({ ok: false, error: rejectionText(e) });
4393
4423
  }
4394
4424
  // #373: the event-plane admission gate. `startAgent` resolves `eventsRequired || opts.events
4395
4425
  // !== false`, so omission ARMS the plane — a gate on the explicit bit alone is bypassed by
@@ -4450,14 +4480,14 @@ export class Manager {
4450
4480
  cwd = realpathSync(resolved);
4451
4481
  }
4452
4482
  catch (error) {
4453
- return { refusal: `path cannot be resolved: ${error.message}` };
4483
+ return { refusal: `path cannot be resolved: ${rejectionText(error)}` };
4454
4484
  }
4455
4485
  try {
4456
4486
  if (!statSync(cwd).isDirectory())
4457
4487
  return { refusal: "path is not a directory" };
4458
4488
  }
4459
4489
  catch (error) {
4460
- return { refusal: error.message };
4490
+ return { refusal: rejectionText(error) };
4461
4491
  }
4462
4492
  return { cwd, host: hostname() };
4463
4493
  }
@@ -4505,7 +4535,7 @@ export class Manager {
4505
4535
  declared = loadExtensionsManifest().extensions.flatMap((ext) => extensionConnectors(ext));
4506
4536
  }
4507
4537
  catch (error) {
4508
- const reason = error.message;
4538
+ const reason = rejectionText(error);
4509
4539
  console.error(`! manager boot: connector inventory unavailable - ${reason}`);
4510
4540
  this.connectorStatuses = [{ agent: "(inventory)", state: "unavailable", binaries: {}, reason }];
4511
4541
  console.error("! manager boot: no connector available - spawn and launch stay on this instance rail only so a sibling that can launch them can take the class queue");
@@ -4589,7 +4619,7 @@ export class Manager {
4589
4619
  return { agent: connector.name, supported: true, ...catalog };
4590
4620
  }
4591
4621
  catch (e) {
4592
- return { agent: connector.name, supported: true, models: [], error: e.message };
4622
+ return { agent: connector.name, supported: true, models: [], error: rejectionText(e) };
4593
4623
  }
4594
4624
  };
4595
4625
  if (requested) {
@@ -4598,7 +4628,7 @@ export class Manager {
4598
4628
  connector = await this.resolveConnector(requested);
4599
4629
  }
4600
4630
  catch (e) {
4601
- return { ok: false, error: e.message };
4631
+ return { ok: false, error: rejectionText(e) };
4602
4632
  }
4603
4633
  const result = await one(connector);
4604
4634
  return result.error ? { ok: false, error: result.error } : { ok: true, data: result };
@@ -4612,7 +4642,7 @@ export class Manager {
4612
4642
  connector = await this.resolveConnector(name);
4613
4643
  }
4614
4644
  catch (e) {
4615
- return { agent: name, supported: false, models: [], error: e.message };
4645
+ return { agent: name, supported: false, models: [], error: rejectionText(e) };
4616
4646
  }
4617
4647
  return one(connector);
4618
4648
  }));
@@ -4674,7 +4704,7 @@ export class Manager {
4674
4704
  persistLaunchSpec(this.workspaceRoot, spec);
4675
4705
  }
4676
4706
  catch (e) {
4677
- return { ok: false, error: e.message };
4707
+ return { ok: false, error: rejectionText(e) };
4678
4708
  }
4679
4709
  }
4680
4710
  else {
@@ -4682,7 +4712,7 @@ export class Manager {
4682
4712
  spec = launchSpecForRun(this.workspaceRoot, runId);
4683
4713
  }
4684
4714
  catch (e) {
4685
- return { ok: false, error: e.message };
4715
+ return { ok: false, error: rejectionText(e) };
4686
4716
  }
4687
4717
  }
4688
4718
  const la = spec.agents.find((a) => a.name === name);
@@ -4705,7 +4735,7 @@ export class Manager {
4705
4735
  configPath = materializePersona(this.workspaceRoot, runId, la);
4706
4736
  }
4707
4737
  catch (e) {
4708
- return { ok: false, error: e.message };
4738
+ return { ok: false, error: rejectionText(e) };
4709
4739
  }
4710
4740
  const reply = await this.startAgent({ ...launchAgentToStartOpts(la, configPath, spec.owner, runId), route }, caller, hooks);
4711
4741
  if (reply.ok)
@@ -4789,7 +4819,7 @@ export class Manager {
4789
4819
  def = loadAgentFile(configPath);
4790
4820
  }
4791
4821
  catch (e) {
4792
- return { ok: false, error: e.message };
4822
+ return { ok: false, error: rejectionText(e) };
4793
4823
  }
4794
4824
  }
4795
4825
  const agent = resolveAgentType({ flag: opts.agent, pin: def?.agent, callerDefault: opts.defaultAgent });
@@ -4803,7 +4833,7 @@ export class Manager {
4803
4833
  connector = await this.resolveConnector(agent);
4804
4834
  }
4805
4835
  catch (e) {
4806
- return { ok: false, error: e.message };
4836
+ return { ok: false, error: rejectionText(e) };
4807
4837
  }
4808
4838
  const readinessTimeoutMs = connector.readinessTimeoutMs ?? this.readinessTimeoutMs;
4809
4839
  if (!Number.isSafeInteger(readinessTimeoutMs) || readinessTimeoutMs <= 0)
@@ -4842,7 +4872,7 @@ export class Manager {
4842
4872
  carried = this.transcripts.peek(opts.resumeClaim, opts.resume);
4843
4873
  }
4844
4874
  catch (e) {
4845
- return { ok: false, error: e.message };
4875
+ return { ok: false, error: rejectionText(e) };
4846
4876
  }
4847
4877
  }
4848
4878
  // A restart policy this host cannot honour is refused at accept, never accepted and ignored.
@@ -4865,7 +4895,7 @@ export class Manager {
4865
4895
  this.delegatedMeshRecord();
4866
4896
  }
4867
4897
  catch (e) {
4868
- return { ok: false, error: e.message };
4898
+ return { ok: false, error: rejectionText(e) };
4869
4899
  }
4870
4900
  if (opts.resume)
4871
4901
  return { ok: false, error: `${delegatedBy}, so it cannot resume a session held on this host (resume)` };
@@ -4954,7 +4984,7 @@ export class Manager {
4954
4984
  allowSubscribe = resolveReadAcl(subscribe ?? [], allowSubscribe);
4955
4985
  }
4956
4986
  catch (e) {
4957
- return { ok: false, error: opts.resolved ? `launch agent: ${e.message}` : `persona ${configPath}: ${e.message}` };
4987
+ return { ok: false, error: opts.resolved ? `launch agent: ${rejectionText(e)}` : `persona ${configPath}: ${rejectionText(e)}` };
4958
4988
  }
4959
4989
  // Checked after every path has resolved its selectors: a direct `startAgent` call skips the
4960
4990
  // `start` op's checks, and no connector may receive a blank one (#2862).
@@ -5005,7 +5035,7 @@ export class Manager {
5005
5035
  });
5006
5036
  }
5007
5037
  catch (e) {
5008
- return { ok: false, error: e.message };
5038
+ return { ok: false, error: rejectionText(e) };
5009
5039
  }
5010
5040
  if (policyRefusal)
5011
5041
  return { ok: false, error: policyRefusal };
@@ -5036,7 +5066,7 @@ export class Manager {
5036
5066
  reopenSession = readContinuityAssignment(this.workspaceRoot, { space: this.space, name: identityName, connector: agent, cwd: resolvedCwd ?? this.workspaceRoot });
5037
5067
  }
5038
5068
  catch (e) {
5039
- return { ok: false, error: e.message };
5069
+ return { ok: false, error: rejectionText(e) };
5040
5070
  }
5041
5071
  }
5042
5072
  // The child takes launch options as `--opt k=v`, so only string values can cross to it.
@@ -5529,7 +5559,7 @@ export class Manager {
5529
5559
  }
5530
5560
  }
5531
5561
  catch (error) {
5532
- const detail = `${managed.name} joined, but its exact host session could not be bound for supervised recovery: ${error.message}`;
5562
+ const detail = `${managed.name} joined, but its exact host session could not be bound for supervised recovery: ${rejectionText(error)}`;
5533
5563
  this.stopHandle(managed, false);
5534
5564
  this.freeSlot(managed, true, "session-bind-failed");
5535
5565
  await hooks?.onOutcome?.({ kind: "failed", data: { error: detail } });
@@ -5646,7 +5676,7 @@ export class Manager {
5646
5676
  : principalKey(DEV_OWNER, entry.identity.id).key;
5647
5677
  }
5648
5678
  catch (e) {
5649
- return { ok: false, agents: [], error: `invalid retained principal for ${entry.name}: ${e.message}` };
5679
+ return { ok: false, agents: [], error: `invalid retained principal for ${entry.name}: ${rejectionText(e)}` };
5650
5680
  }
5651
5681
  if (principals.has(principal))
5652
5682
  return { ok: false, agents: [], error: `resume inventory contains duplicate principal "${principal}"` };
@@ -5722,7 +5752,7 @@ export class Manager {
5722
5752
  discardLaunchArtifacts(spec.artifacts);
5723
5753
  }
5724
5754
  catch (e) {
5725
- console.error(`! resume: ${e.message}`);
5755
+ console.error(`! resume: ${rejectionText(e)}`);
5726
5756
  }
5727
5757
  }
5728
5758
  release();
@@ -5801,7 +5831,7 @@ export class Manager {
5801
5831
  throw new Error(`retained credential identity ${actual} does not match inventory principal ${entry.identity.id}`);
5802
5832
  }
5803
5833
  catch (e) {
5804
- throw new Error(`retained credential for ${entry.name} is unusable: ${e.message}`);
5834
+ throw new Error(`retained credential for ${entry.name} is unusable: ${rejectionText(e)}`);
5805
5835
  }
5806
5836
  const accepted = await this.probeStaticCredential(credentialText);
5807
5837
  if (!accepted.ok)
@@ -5889,7 +5919,7 @@ export class Manager {
5889
5919
  };
5890
5920
  }
5891
5921
  catch (e) {
5892
- throw new Error(`retained user principal ${entry.identity.owner}.${entry.identity.actor} could not be reused: ${e.message}`);
5922
+ throw new Error(`retained user principal ${entry.identity.owner}.${entry.identity.actor} could not be reused: ${rejectionText(e)}`);
5893
5923
  }
5894
5924
  }
5895
5925
  /** The seat reference behind a live retained principal, when that seat is provably one a lost
@@ -5922,7 +5952,7 @@ export class Manager {
5922
5952
  console.error(`resume: reaped orphaned seat ${reference.kind}:${reference.id} of retained principal "${principal}" (${evidence.detail})`);
5923
5953
  }
5924
5954
  catch (e) {
5925
- return `retained principal "${principal}" is already live and its recorded seat ${reference.kind}:${reference.id} could not be reaped: ${e.message}`;
5955
+ return `retained principal "${principal}" is already live and its recorded seat ${reference.kind}:${reference.id} could not be reaped: ${rejectionText(e)}`;
5926
5956
  }
5927
5957
  const deadline = Date.now() + ORPHAN_PRESENCE_CLEAR_MS;
5928
5958
  while (this.ep.getRoster().some((p) => p.card.id === principal && p.status !== "offline")) {
@@ -5958,7 +5988,7 @@ export class Manager {
5958
5988
  await this.validateRetainedAuthority(entry);
5959
5989
  }
5960
5990
  catch (e) {
5961
- return { ok: false, error: e.message };
5991
+ return { ok: false, error: rejectionText(e) };
5962
5992
  }
5963
5993
  return this.launchPreparedResume(entry, cached, batchReserved);
5964
5994
  }
@@ -5968,7 +5998,7 @@ export class Manager {
5968
5998
  return { ok: false, error: `retained cwd is not a real directory: ${entry.launch.cwd}` };
5969
5999
  }
5970
6000
  catch (e) {
5971
- return { ok: false, error: `retained cwd unavailable: ${entry.launch.cwd} (${e.message})` };
6001
+ return { ok: false, error: `retained cwd unavailable: ${entry.launch.cwd} (${rejectionText(e)})` };
5972
6002
  }
5973
6003
  let connector;
5974
6004
  try {
@@ -5982,7 +6012,7 @@ export class Manager {
5982
6012
  connector = await this.resolveConnector(entry.launch.connector);
5983
6013
  }
5984
6014
  catch (e) {
5985
- return { ok: false, error: e.message };
6015
+ return { ok: false, error: rejectionText(e) };
5986
6016
  }
5987
6017
  const harness = this.launchHarness(connector);
5988
6018
  if ("refusal" in harness)
@@ -5992,7 +6022,7 @@ export class Manager {
5992
6022
  retainedSession = this.retainedSessionId(entry, connector);
5993
6023
  }
5994
6024
  catch (error) {
5995
- return { ok: false, error: error.message };
6025
+ return { ok: false, error: rejectionText(error) };
5996
6026
  }
5997
6027
  if (entry.launch.forkSource && !retainedSession && !connector.supportsResume)
5998
6028
  return { ok: false, error: `${connector.name} connector does not support session fork (resume)` };
@@ -6012,7 +6042,7 @@ export class Manager {
6012
6042
  spec = source.agents.find((a) => a.name === launchSource.requested);
6013
6043
  }
6014
6044
  catch (e) {
6015
- return { ok: false, error: e.message };
6045
+ return { ok: false, error: rejectionText(e) };
6016
6046
  }
6017
6047
  if (!spec || spec.hash !== launchSource.hash)
6018
6048
  return { ok: false, error: `retained manifest agent ${launchSource.requested} is missing or its hash changed; refusing same-principal resume` };
@@ -6024,7 +6054,7 @@ export class Manager {
6024
6054
  launchOptions = loadAgentFile(entry.launch.source.configPath).launchOptions;
6025
6055
  }
6026
6056
  catch (e) {
6027
- return { ok: false, error: e.message };
6057
+ return { ok: false, error: rejectionText(e) };
6028
6058
  }
6029
6059
  }
6030
6060
  // Asked once `exact` is known: an exact seat reopens its retained session or fails, like its
@@ -6037,7 +6067,7 @@ export class Manager {
6037
6067
  authority = await this.validateRetainedAuthority(entry);
6038
6068
  }
6039
6069
  catch (e) {
6040
- return { ok: false, error: e.message };
6070
+ return { ok: false, error: rejectionText(e) };
6041
6071
  }
6042
6072
  try {
6043
6073
  const resumeConfig = loadCotalConfig(this.workspaceRoot);
@@ -6086,7 +6116,7 @@ export class Manager {
6086
6116
  return this.launchPreparedResume(entry, value, batchReserved);
6087
6117
  }
6088
6118
  catch (e) {
6089
- return { ok: false, error: e.message };
6119
+ return { ok: false, error: rejectionText(e) };
6090
6120
  }
6091
6121
  }
6092
6122
  finally {
@@ -6169,7 +6199,7 @@ export class Manager {
6169
6199
  await this.recordSlotRuntime(managed);
6170
6200
  }
6171
6201
  catch (error) {
6172
- return await this.stopFailedResume(managed, "resume-custody-unrecorded", `${managed.name} resumed, but ${error.message}`);
6202
+ return await this.stopFailedResume(managed, "resume-custody-unrecorded", `${managed.name} resumed, but ${rejectionText(error)}`);
6173
6203
  }
6174
6204
  const readiness = await this.awaitReadiness(managed, readinessTimeoutMs);
6175
6205
  if (!readiness.ok && !readiness.uncertain)
@@ -6196,7 +6226,7 @@ export class Manager {
6196
6226
  }
6197
6227
  }
6198
6228
  catch (error) {
6199
- return await this.stopFailedResume(managed, "resume-session-rebind-failed", `${managed.name} resumed, but its exact host session could not be rebound: ${error.message}`);
6229
+ return await this.stopFailedResume(managed, "resume-session-rebind-failed", `${managed.name} resumed, but its exact host session could not be rebound: ${rejectionText(error)}`);
6200
6230
  }
6201
6231
  }
6202
6232
  if (!this.resumeAttemptId)
@@ -6210,7 +6240,7 @@ export class Manager {
6210
6240
  };
6211
6241
  }
6212
6242
  catch (e) {
6213
- return { ok: false, error: e.message };
6243
+ return { ok: false, error: rejectionText(e) };
6214
6244
  }
6215
6245
  finally {
6216
6246
  if (!batchReserved) {
@@ -6229,7 +6259,7 @@ export class Manager {
6229
6259
  }
6230
6260
  catch (exit) {
6231
6261
  this.watchExit(managed);
6232
- return { ok: false, error: `${detail}; it stays managed because its stop is unproven: ${exit.message}` };
6262
+ return { ok: false, error: `${detail}; it stays managed because its stop is unproven: ${rejectionText(exit)}` };
6233
6263
  }
6234
6264
  this.freeSlot(managed, true, cause, true);
6235
6265
  return { ok: false, error: detail };
@@ -6981,7 +7011,7 @@ export class Manager {
6981
7011
  // One registration operation for this boot. Fresh executors may replace a dead connection, but
6982
7012
  // they must resume THIS freeze rather than minting a new op that discards Phase-2 progress.
6983
7013
  const registrationOpId = mintLifecycleUid();
6984
- const executorExpired = (e) => /closed connection/i.test(e?.message ?? String(e));
7014
+ const executorExpired = (e) => /closed connection/i.test(rejectionText(e));
6985
7015
  const retryExpiredExecutor = async (label, fn) => {
6986
7016
  for (;;) {
6987
7017
  try {
@@ -7588,14 +7618,14 @@ export class Manager {
7588
7618
  await this.reconcileOneGoal(entry.ref, entry.iid);
7589
7619
  }
7590
7620
  catch (e) {
7591
- console.error(`! goal reconcile for ${entry.ref.goalId}: ${e.message}`);
7621
+ console.error(`! goal reconcile for ${entry.ref.goalId}: ${rejectionText(e)}`);
7592
7622
  }
7593
7623
  }
7594
7624
  if (entries.length)
7595
7625
  console.error(`goal-index boot reconcile: swept ${entries.length} inherited goal(s)`);
7596
7626
  }
7597
7627
  catch (e) {
7598
- console.error(`! goal-index boot reconcile failed: ${e.message} - accepted goals from a predecessor may stay unsettled until the next restart`);
7628
+ console.error(`! goal-index boot reconcile failed: ${rejectionText(e)} - accepted goals from a predecessor may stay unsettled until the next restart`);
7599
7629
  }
7600
7630
  finally {
7601
7631
  this.goalReconcileDone = true;
@@ -7645,7 +7675,7 @@ export class Manager {
7645
7675
  return;
7646
7676
  }
7647
7677
  console.error(`goal reconcile ${ref.goalId}: within the readiness window (${remaining}ms) - arming a bounded settle`);
7648
- const t = setTimeout(() => { settle().catch((e) => console.error(`! goal reconcile settle ${ref.goalId}: ${e.message}`)); }, remaining + 100);
7678
+ const t = setTimeout(() => { settle().catch((e) => console.error(`! goal reconcile settle ${ref.goalId}: ${rejectionText(e)}`)); }, remaining + 100);
7649
7679
  t.unref?.();
7650
7680
  }
7651
7681
  /** P2 item 2: publish a goal PROGRESS event on the caller-scoped epe subtree, over the SERVE
@@ -7661,7 +7691,7 @@ export class Manager {
7661
7691
  nc.publish(epeSubject(this.space, MANAGER_ENDPOINT, this.managerInstanceId, epoch, goalProgressTopic(ref)), new TextEncoder().encode(JSON.stringify({ v: 1, goalId: ref.goalId, ...event })));
7662
7692
  }
7663
7693
  catch (e) {
7664
- console.error(`! goal progress emit for ${ref.goalId} failed: ${e.message}`);
7694
+ console.error(`! goal progress emit for ${ref.goalId} failed: ${rejectionText(e)}`);
7665
7695
  }
7666
7696
  }
7667
7697
  /** The own-gate currency belt: before the goal-writer commits a terminal fact, the manager reads
@@ -7800,7 +7830,7 @@ export class Manager {
7800
7830
  // ENTRY. This leg is infrastructure-class and converges through the reconcile index at the
7801
7831
  // next boot, which is why the index is NOT cleared above on this path. Do not "close" it
7802
7832
  // here with a retry loop.
7803
- console.error(`! goal terminal commit for ${goalId} failed: ${e.message}`);
7833
+ console.error(`! goal terminal commit for ${goalId} failed: ${rejectionText(e)}`);
7804
7834
  }
7805
7835
  };
7806
7836
  const bg = run({
@@ -7885,7 +7915,7 @@ export class Manager {
7885
7915
  // the caller follows epe to a terminal that never comes, and the reconcile index that would
7886
7916
  // settle it is only swept at BOOT, so a manager that stays up never converges it.
7887
7917
  if (reply.ok === false && !terminalEntered)
7888
- return onOutcome({ kind: "failed", data: { error: reply.error ?? "spawn failed after accept", ...(reply.details ? { details: reply.details } : {}) } });
7918
+ return onOutcome({ kind: "failed", data: failedTerminal(reply.error ?? "spawn failed after accept", reply) });
7889
7919
  }).catch((e) => {
7890
7920
  if (acceptance === undefined) {
7891
7921
  rejectAccept(e);
@@ -7893,9 +7923,9 @@ export class Manager {
7893
7923
  }
7894
7924
  // Same obligation for a genuine rejection (one that escaped `run`'s own catch).
7895
7925
  if (!terminalEntered)
7896
- return onOutcome({ kind: "failed", data: { error: e?.message ?? String(e), ...(e instanceof EpEnvelopeError && e.details ? { details: e.details } : {}) } });
7897
- console.error(`! spawn-as-action async body for ${goalId}: ${e?.message ?? String(e)}`);
7898
- }).catch((e) => console.error(`! goal terminal fallback for ${goalId}: ${e?.message ?? String(e)}`));
7926
+ return onOutcome({ kind: "failed", data: failedTerminal(rejectionText(e), caughtEnvelope(e)) });
7927
+ console.error(`! spawn-as-action async body for ${goalId}: ${rejectionText(e)}`);
7928
+ }).catch((e) => console.error(`! goal terminal fallback for ${goalId}: ${rejectionText(e)}`));
7899
7929
  return acceptP;
7900
7930
  }
7901
7931
  /** H2: an acceptance served from a WINNER'S durable acceptance floor (the goal-index entry it
@@ -8038,7 +8068,7 @@ export class Manager {
8038
8068
  // The accept is inline (no launch closure to fail later), so a post-bind throw unwinds HERE:
8039
8069
  // commit the failed terminal this attempt owns, clear the index, and refuse the accept — an
8040
8070
  // accepted-but-unanswered goal must never be left for the boot sweep to find (H1's rule).
8041
- const msg = e?.message ?? String(e);
8071
+ const msg = rejectionText(e);
8042
8072
  try {
8043
8073
  await this.assertGoalWriterEpochCurrent(executor.epoch);
8044
8074
  // The goal is TARGET-PINNED, so its completion proves the seat's currency exactly as the
@@ -8054,9 +8084,9 @@ export class Manager {
8054
8084
  await clearGoalIndex(gw.ctx, ref);
8055
8085
  }
8056
8086
  catch (e2) {
8057
- console.error(`! turn accept unwind for ${goalId}: ${e2.message}`);
8087
+ console.error(`! turn accept unwind for ${goalId}: ${rejectionText(e2)}`);
8058
8088
  }
8059
- throw e instanceof EpEnvelopeError ? e : new EpEnvelopeError("internal", `turn accept for ${goalId} failed: ${msg}`);
8089
+ throw caughtEnvelope(e) ?? new EpEnvelopeError("internal", `turn accept for ${goalId} failed: ${msg}`);
8060
8090
  }
8061
8091
  const pending = {
8062
8092
  ref, goalId,
@@ -8256,7 +8286,7 @@ export class Manager {
8256
8286
  await clearGoalIndex(gw.ctx, p.ref);
8257
8287
  }
8258
8288
  catch (e) {
8259
- console.error(`! turn deadline terminal for ${p.goalId}: ${e.message}`);
8289
+ console.error(`! turn deadline terminal for ${p.goalId}: ${rejectionText(e)}`);
8260
8290
  }
8261
8291
  this.maybeStopTurnSweep();
8262
8292
  }
@@ -8288,7 +8318,7 @@ export class Manager {
8288
8318
  ensureTurnSweep() {
8289
8319
  if (this.turnSweepTimer !== undefined)
8290
8320
  return;
8291
- const t = setInterval(() => { void this.sweepTurnDeadlines().catch((e) => console.error(`! turn deadline sweep: ${e.message}`)); }, TURN_SWEEP_MS);
8321
+ const t = setInterval(() => { void this.sweepTurnDeadlines().catch((e) => console.error(`! turn deadline sweep: ${rejectionText(e)}`)); }, TURN_SWEEP_MS);
8292
8322
  t.unref?.();
8293
8323
  this.turnSweepTimer = t;
8294
8324
  }
@@ -8357,7 +8387,7 @@ export class Manager {
8357
8387
  await this.commitTurnDeadline(p);
8358
8388
  }
8359
8389
  catch (e) {
8360
- console.error(`! turn deadline sweep for ${p.goalId}: ${e.message}`);
8390
+ console.error(`! turn deadline sweep for ${p.goalId}: ${rejectionText(e)}`);
8361
8391
  }
8362
8392
  }
8363
8393
  }
@@ -8488,7 +8518,7 @@ export class Manager {
8488
8518
  });
8489
8519
  }
8490
8520
  catch (e) {
8491
- throw new Error(`could not record the custody reference ${runtime.kind}:${runtime.id} on the static slot of "${a.name}": ${e.message}`);
8521
+ throw new Error(`could not record the custody reference ${runtime.kind}:${runtime.id} on the static slot of "${a.name}": ${rejectionText(e)}`);
8492
8522
  }
8493
8523
  }
8494
8524
  /** Mint the custody reference for a seat this manager is about to spawn, BEFORE the processes
@@ -8534,7 +8564,7 @@ export class Manager {
8534
8564
  disposal =
8535
8565
  e instanceof RuntimeReapUnproven
8536
8566
  ? `the spawned seat was NOT proved gone and may still be running as ${e.reference.kind}:${e.reference.id} (${e.message})`
8537
- : `the spawned seat could NOT be reaped: ${e.message}`;
8567
+ : `the spawned seat could NOT be reaped: ${rejectionText(e)}`;
8538
8568
  }
8539
8569
  }
8540
8570
  throw new Error(`runtime "${this.runtime.kind}" reserved custody ${reserved.kind}:${reserved.id} for "${name}" but spawned ` +
@@ -8635,8 +8665,8 @@ export class Manager {
8635
8665
  catch (e) {
8636
8666
  const h = this.retiring.get(a.name);
8637
8667
  if (h && h.lifecycleUid === a.lifecycleUid)
8638
- h.lastError = `the static retirement did not complete (${e.message}); the name stays held - a same-name spawn retries the same terminal (op ${opId})`;
8639
- console.error(`static retirement ${a.name} (${a.id}): ${e.message}`);
8668
+ h.lastError = `the static retirement did not complete (${rejectionText(e)}); the name stays held - a same-name spawn retries the same terminal (op ${opId})`;
8669
+ console.error(`static retirement ${a.name} (${a.id}): ${rejectionText(e)}`);
8640
8670
  if (surfaceFailure)
8641
8671
  throw e;
8642
8672
  }
@@ -8739,7 +8769,7 @@ export class Manager {
8739
8769
  await this.retryStaticReconcile(key);
8740
8770
  };
8741
8771
  void run().catch((error) => {
8742
- item.lastError = `retry driver failed before the exact terminal: ${error.message}`;
8772
+ item.lastError = `retry driver failed before the exact terminal: ${rejectionText(error)}`;
8743
8773
  item.disposition = "retry-exhausted";
8744
8774
  item.remedy = "restart this manager for a fresh per-process retry budget";
8745
8775
  console.error(`! static reconcile retry-exhausted alias=${item.alias} phase=${item.phase} uid=${item.lifecycleUid}: ${item.lastError}; NEXT: restart this manager for a fresh per-process retry budget`);
@@ -8780,7 +8810,7 @@ export class Manager {
8780
8810
  row = await this.readStaticReconcileSlot(item.alias);
8781
8811
  }
8782
8812
  catch (error) {
8783
- item.lastError = `durable slot re-read failed: ${error.message}`;
8813
+ item.lastError = `durable slot re-read failed: ${rejectionText(error)}`;
8784
8814
  if (item.attempts >= item.maxAttempts) {
8785
8815
  item.disposition = "retry-exhausted";
8786
8816
  item.remedy = "restart this manager for a fresh per-process retry budget";
@@ -8864,7 +8894,7 @@ export class Manager {
8864
8894
  return true;
8865
8895
  }
8866
8896
  catch (error) {
8867
- item.lastError = error.message;
8897
+ item.lastError = rejectionText(error);
8868
8898
  try {
8869
8899
  const fresh = await this.readStaticReconcileSlot(item.alias);
8870
8900
  if (fresh?.lifecycleUid === item.lifecycleUid && fresh.actor === item.actor)
@@ -8963,16 +8993,8 @@ export class Manager {
8963
8993
  const kvm = new Kvm(nc);
8964
8994
  await ensureAuthorityStores(jsm, kvm, this.space);
8965
8995
  const recordsKv = await kvm.open(recordsBucket(this.space));
8966
- const t = staticLifecycleTransport(recordsKv, recordsKv /* auth reads unused in the sweep */);
8967
- const keys = await recordsKv.keys(`${STATIC_SLOT_PREFIX}.${DEV_OWNER}.>`);
8968
- const aliases = [];
8969
- for await (const k of keys)
8970
- aliases.push(k.split(".").slice(2).join("."));
8971
- for (const alias of aliases) {
8972
- const slot = await readStaticSlot(t, DEV_OWNER, alias);
8973
- if (slot !== undefined)
8974
- slotRows.push(slot.row);
8975
- }
8996
+ for (const slot of await walkStaticSlots(recordsKv, DEV_OWNER))
8997
+ slotRows.push(slot.row);
8976
8998
  }
8977
8999
  catch (error) {
8978
9000
  sweep.completedAt = new Date().toISOString();
@@ -9221,7 +9243,7 @@ export class Manager {
9221
9243
  return { ok: true, data: result };
9222
9244
  }
9223
9245
  catch (e) {
9224
- return { ok: false, error: e.message };
9246
+ return { ok: false, error: rejectionText(e) };
9225
9247
  }
9226
9248
  }
9227
9249
  /** Persist a peer-defined persona as config. After this, `start name` auto-discovers
@@ -9253,7 +9275,7 @@ export class Manager {
9253
9275
  existing = loadAgentFile(path);
9254
9276
  }
9255
9277
  catch (e) {
9256
- return { ok: false, error: e.message };
9278
+ return { ok: false, error: rejectionText(e) };
9257
9279
  }
9258
9280
  if (!admin && existing.owner !== caller) {
9259
9281
  const owner = existing.owner ? `owned by ${existing.owner}` : "operator-owned (legacy file - no agent owner)";
@@ -9280,13 +9302,13 @@ export class Manager {
9280
9302
  }, existing);
9281
9303
  }
9282
9304
  catch (e) {
9283
- return { ok: false, error: e.message };
9305
+ return { ok: false, error: rejectionText(e) };
9284
9306
  }
9285
9307
  try {
9286
9308
  saveAgentFile(path, def);
9287
9309
  }
9288
9310
  catch (e) {
9289
- return { ok: false, error: e.message };
9311
+ return { ok: false, error: rejectionText(e) };
9290
9312
  }
9291
9313
  return { ok: true, data: { name, path } };
9292
9314
  }
@@ -9444,7 +9466,7 @@ export class Manager {
9444
9466
  accepted = await write(part.data);
9445
9467
  }
9446
9468
  catch (error) {
9447
- throw new EpEnvelopeError("unavailable", `input for seat "${a.name}" failed: ${error.message}`);
9469
+ throw new EpEnvelopeError("unavailable", `input for seat "${a.name}" failed: ${rejectionText(error)}`);
9448
9470
  }
9449
9471
  if (!Number.isSafeInteger(accepted)) {
9450
9472
  throw new EpEnvelopeError("unavailable", `input for seat "${a.name}" failed: runtime accepted ${String(accepted)} of ${intendedBytes} bytes`);
@@ -9495,7 +9517,7 @@ export class Manager {
9495
9517
  }
9496
9518
  catch (e) {
9497
9519
  slot.release();
9498
- return { ok: false, error: e.message };
9520
+ return { ok: false, error: rejectionText(e) };
9499
9521
  }
9500
9522
  // establishAttach releases the claim it was handed on every exit; nothing to unwind here.
9501
9523
  const { grant } = await this.sessionPlane.establishAttach(caller, { name: a.name, lifecycleUid: a.lifecycleUid }, session, slot);