@cotal-ai/manager 0.71.0 → 0.72.1
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.d.ts +1 -1
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +181 -153
- package/dist/manager.js.map +1 -1
- package/dist/static-lifecycle.d.ts +13 -2
- package/dist/static-lifecycle.d.ts.map +1 -1
- package/dist/static-lifecycle.js +21 -25
- package/dist/static-lifecycle.js.map +1 -1
- package/package.json +12 -12
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,
|
|
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
|
|
130
|
-
* including one whose `message` is a Symbol or whose `message`
|
|
131
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1574
|
-
console.error(`! remote manager all-duty renewal: ${e
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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 &&
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|
|
@@ -2844,10 +2874,9 @@ export class Manager {
|
|
|
2844
2874
|
// P2 item 2: `spawn` is an ACTION - accept a goal + reply the acceptance floor payload, drive
|
|
2845
2875
|
// progress + terminal off-handler (no ~30s block). The blocking reply path is gone (pin 8).
|
|
2846
2876
|
// #373: arming the event plane (events: true, or the omission that arms it by default)
|
|
2847
|
-
//
|
|
2848
|
-
//
|
|
2849
|
-
//
|
|
2850
|
-
// true and nothing changes.
|
|
2877
|
+
// follows owner-domain reach on a user mesh. Resolve the caller's admin tier once for
|
|
2878
|
+
// the residual cross-owner case; opStart decides before connector resolution or grant
|
|
2879
|
+
// mutation. Static meshes resolve true and nothing changes.
|
|
2851
2880
|
spawn: (ctx) => this.serveGated(ctx, async () => this.serveSpawnGoal(ctx, async (h) => this.opStart(args(ctx), callerOf(ctx), await this.epAdminReach(ctx.subject.caller), h, ctx.subject.route))),
|
|
2852
2881
|
despawn: (ctx) => this.serveGated(ctx, async () => {
|
|
2853
2882
|
const a = targetAgent(ctx);
|
|
@@ -2954,7 +2983,7 @@ export class Manager {
|
|
|
2954
2983
|
return undefined;
|
|
2955
2984
|
}
|
|
2956
2985
|
catch (e) {
|
|
2957
|
-
return `${entry.name}: ${e
|
|
2986
|
+
return `${entry.name}: ${rejectionText(e)}`;
|
|
2958
2987
|
}
|
|
2959
2988
|
}));
|
|
2960
2989
|
const drift = authority.filter((error) => error !== undefined);
|
|
@@ -2965,7 +2994,7 @@ export class Manager {
|
|
|
2965
2994
|
inactive = this.resumeLivenessErrors(inventory, this.ep.getRoster());
|
|
2966
2995
|
}
|
|
2967
2996
|
catch (e) {
|
|
2968
|
-
return { ok: false, error: `resume attempt ${attemptId} cannot verify live principals: ${e
|
|
2997
|
+
return { ok: false, error: `resume attempt ${attemptId} cannot verify live principals: ${rejectionText(e)}` };
|
|
2969
2998
|
}
|
|
2970
2999
|
if (inactive.length)
|
|
2971
3000
|
return { ok: false, error: `resume attempt ${attemptId} is not live at commit: ${inactive.join("; ")}` };
|
|
@@ -3058,7 +3087,7 @@ export class Manager {
|
|
|
3058
3087
|
}
|
|
3059
3088
|
}
|
|
3060
3089
|
catch (e) {
|
|
3061
|
-
inactive.push(`${entry.name} runtime status failed: ${e
|
|
3090
|
+
inactive.push(`${entry.name} runtime status failed: ${rejectionText(e)}`);
|
|
3062
3091
|
continue;
|
|
3063
3092
|
}
|
|
3064
3093
|
if (!roster.some((presence) => presence.card.id === expectedPrincipal && presence.card.name === entry.name && presence.status !== "offline" &&
|
|
@@ -3114,7 +3143,7 @@ export class Manager {
|
|
|
3114
3143
|
a.handle.stop({ graceful });
|
|
3115
3144
|
}
|
|
3116
3145
|
catch (e) {
|
|
3117
|
-
console.error(`stop ${a.name} (${a.id}): ${e
|
|
3146
|
+
console.error(`stop ${a.name} (${a.id}): ${rejectionText(e)}`);
|
|
3118
3147
|
}
|
|
3119
3148
|
}
|
|
3120
3149
|
/** Keep an accepted stop inside the lifecycle drain until the runtime proves the child is gone,
|
|
@@ -3157,7 +3186,7 @@ export class Manager {
|
|
|
3157
3186
|
id: a.id,
|
|
3158
3187
|
handle: a.handle,
|
|
3159
3188
|
authoritative: true,
|
|
3160
|
-
error: `accepted stop could not prove exit: ${e
|
|
3189
|
+
error: `accepted stop could not prove exit: ${rejectionText(e)}`,
|
|
3161
3190
|
});
|
|
3162
3191
|
})
|
|
3163
3192
|
.finally(() => this.releaseLifecycle());
|
|
@@ -3197,7 +3226,7 @@ export class Manager {
|
|
|
3197
3226
|
provider = resolveAuthProvider();
|
|
3198
3227
|
}
|
|
3199
3228
|
catch (e) {
|
|
3200
|
-
return { error: e
|
|
3229
|
+
return { error: rejectionText(e) };
|
|
3201
3230
|
}
|
|
3202
3231
|
const dir = userAuthStateDir(this.workspaceRoot, this.space);
|
|
3203
3232
|
// The agent's capability scope rides its ledger row (act.scope in every bearer) — same
|
|
@@ -3305,7 +3334,7 @@ export class Manager {
|
|
|
3305
3334
|
provider = resolveAuthProvider();
|
|
3306
3335
|
}
|
|
3307
3336
|
catch (e) {
|
|
3308
|
-
return { error: e
|
|
3337
|
+
return { error: rejectionText(e) };
|
|
3309
3338
|
}
|
|
3310
3339
|
const secrets = this.secrets;
|
|
3311
3340
|
// The token the agent will present to `POST /exchange` for the rest of its life. Generated
|
|
@@ -3463,7 +3492,7 @@ export class Manager {
|
|
|
3463
3492
|
catch (e) {
|
|
3464
3493
|
// A runtime that throws while being asked has told us something real; it must not take the
|
|
3465
3494
|
// log line (or the free path it sits on) down with it.
|
|
3466
|
-
detail = `exit detail unreadable from runtime "${a.handle.kind}": ${e
|
|
3495
|
+
detail = `exit detail unreadable from runtime "${a.handle.kind}": ${rejectionText(e)}`;
|
|
3467
3496
|
}
|
|
3468
3497
|
console.error(`seat reaped: ${a.name} (${a.id}, uid ${a.lifecycleUid}${a.handle.pid !== undefined ? `, pid ${a.handle.pid}` : ""}) - ${causeText}; ${detail}`);
|
|
3469
3498
|
}
|
|
@@ -3683,8 +3712,8 @@ export class Manager {
|
|
|
3683
3712
|
catch (e) {
|
|
3684
3713
|
const h = this.retiring.get(a.name);
|
|
3685
3714
|
if (h && h.lifecycleUid === a.lifecycleUid)
|
|
3686
|
-
h.lastError = `the agent's standing mint authority could not be revoked (${e
|
|
3687
|
-
console.error(`revoke agent grant ${a.name}: ${e
|
|
3715
|
+
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.`;
|
|
3716
|
+
console.error(`revoke agent grant ${a.name}: ${rejectionText(e)}`);
|
|
3688
3717
|
}
|
|
3689
3718
|
}
|
|
3690
3719
|
const shredFailure = unshredded.length ? `could not shred "${a.name}": ${unshredded.join("; ")}` : undefined;
|
|
@@ -3697,7 +3726,7 @@ export class Manager {
|
|
|
3697
3726
|
catch (e) {
|
|
3698
3727
|
const h = this.retiring.get(a.name);
|
|
3699
3728
|
if (h && h.lifecycleUid === a.lifecycleUid) {
|
|
3700
|
-
h.lastError = e
|
|
3729
|
+
h.lastError = rejectionText(e);
|
|
3701
3730
|
if (e instanceof DeprovisionError)
|
|
3702
3731
|
h.lastResources = e.accounting;
|
|
3703
3732
|
}
|
|
@@ -3843,7 +3872,7 @@ export class Manager {
|
|
|
3843
3872
|
}
|
|
3844
3873
|
}
|
|
3845
3874
|
catch (e) {
|
|
3846
|
-
const copy = uncertain(e
|
|
3875
|
+
const copy = uncertain(rejectionText(e));
|
|
3847
3876
|
if (held)
|
|
3848
3877
|
held.lastError = copy;
|
|
3849
3878
|
console.error(`despawn ${a.name}: ${copy}`);
|
|
@@ -3937,7 +3966,7 @@ export class Manager {
|
|
|
3937
3966
|
return { complete: true, channels: data.channels };
|
|
3938
3967
|
}
|
|
3939
3968
|
catch (e) {
|
|
3940
|
-
return { complete: false, channels: [], reason: e
|
|
3969
|
+
return { complete: false, channels: [], reason: rejectionText(e) };
|
|
3941
3970
|
}
|
|
3942
3971
|
}
|
|
3943
3972
|
/** Reap a parent's children on its exit (P4b). Every descendant remains managed until the runtime's
|
|
@@ -3959,7 +3988,7 @@ export class Manager {
|
|
|
3959
3988
|
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
3960
3989
|
}
|
|
3961
3990
|
catch (error) {
|
|
3962
|
-
throw new Error(`cannot read connector session state ${path}: ${error
|
|
3991
|
+
throw new Error(`cannot read connector session state ${path}: ${rejectionText(error)}`);
|
|
3963
3992
|
}
|
|
3964
3993
|
const state = parsed;
|
|
3965
3994
|
if (state.version !== 1 || typeof state.sessionId !== "string" || !state.sessionId.trim() || state.sessionId.length > 4096 ||
|
|
@@ -3986,7 +4015,7 @@ export class Manager {
|
|
|
3986
4015
|
}
|
|
3987
4016
|
catch (error) {
|
|
3988
4017
|
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
|
|
4018
|
+
`(${rejectionText(error)}). Reload the live Pi seat before the preservation cut; refusing to resume fresh and lose its context.`);
|
|
3990
4019
|
}
|
|
3991
4020
|
}
|
|
3992
4021
|
/** A resumed seat's fork provenance (#1500). The seat forks after it launches, so this reads the
|
|
@@ -4071,7 +4100,7 @@ export class Manager {
|
|
|
4071
4100
|
this.recordContinuity(a, this.readManagedSession(a));
|
|
4072
4101
|
}
|
|
4073
4102
|
catch (error) {
|
|
4074
|
-
console.error(`! ${a.name}: continuity: exact assignment not updated at ${boundary}: ${error
|
|
4103
|
+
console.error(`! ${a.name}: continuity: exact assignment not updated at ${boundary}: ${rejectionText(error)}`);
|
|
4075
4104
|
}
|
|
4076
4105
|
}
|
|
4077
4106
|
async awaitManagedSessionState(a) {
|
|
@@ -4084,7 +4113,7 @@ export class Manager {
|
|
|
4084
4113
|
return this.readManagedSessionState(a);
|
|
4085
4114
|
}
|
|
4086
4115
|
catch (error) {
|
|
4087
|
-
last = error
|
|
4116
|
+
last = rejectionText(error);
|
|
4088
4117
|
}
|
|
4089
4118
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4090
4119
|
}
|
|
@@ -4117,7 +4146,7 @@ export class Manager {
|
|
|
4117
4146
|
return;
|
|
4118
4147
|
}
|
|
4119
4148
|
catch (error) {
|
|
4120
|
-
last = error
|
|
4149
|
+
last = rejectionText(error);
|
|
4121
4150
|
}
|
|
4122
4151
|
}
|
|
4123
4152
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -4222,7 +4251,7 @@ export class Manager {
|
|
|
4222
4251
|
tail = await this.tail((replacement ?? a.handle).attach());
|
|
4223
4252
|
}
|
|
4224
4253
|
catch { /* runtime has no readable tail */ }
|
|
4225
|
-
console.error(`! ${a.name}: ${supervised ? "supervised restart" : "Pi session recovery"} failed: ${error
|
|
4254
|
+
console.error(`! ${a.name}: ${supervised ? "supervised restart" : "Pi session recovery"} failed: ${rejectionText(error)}${tail ? ` - last output: ${tail}` : ""} - retiring the managed seat`);
|
|
4226
4255
|
// The replacement may be alive but unable to prove readiness. Stop it BEFORE
|
|
4227
4256
|
// retiring credentials/durables; otherwise an untracked process survives under torn auth.
|
|
4228
4257
|
try {
|
|
@@ -4262,7 +4291,7 @@ export class Manager {
|
|
|
4262
4291
|
}
|
|
4263
4292
|
}
|
|
4264
4293
|
catch (error) {
|
|
4265
|
-
console.error(`! ${a.name}: cannot classify Pi process exit for recovery: ${error
|
|
4294
|
+
console.error(`! ${a.name}: cannot classify Pi process exit for recovery: ${rejectionText(error)} - retiring the seat`);
|
|
4266
4295
|
}
|
|
4267
4296
|
}
|
|
4268
4297
|
this.freeSlot(a, true, "process-exit");
|
|
@@ -4389,7 +4418,7 @@ export class Manager {
|
|
|
4389
4418
|
shareTools = strList(args.shareTools, "shareTools");
|
|
4390
4419
|
}
|
|
4391
4420
|
catch (e) {
|
|
4392
|
-
return Promise.resolve({ ok: false, error: e
|
|
4421
|
+
return Promise.resolve({ ok: false, error: rejectionText(e) });
|
|
4393
4422
|
}
|
|
4394
4423
|
// #373: the event-plane admission gate. `startAgent` resolves `eventsRequired || opts.events
|
|
4395
4424
|
// !== false`, so omission ARMS the plane — a gate on the explicit bit alone is bypassed by
|
|
@@ -4398,7 +4427,14 @@ export class Manager {
|
|
|
4398
4427
|
const requestedEvents = typeof args.events === "boolean" ? args.events : undefined;
|
|
4399
4428
|
let eventsNotice;
|
|
4400
4429
|
let events = requestedEvents;
|
|
4401
|
-
|
|
4430
|
+
// Imperative spawns have no payload-selected owner. Local provisioning derives it from
|
|
4431
|
+
// the authenticated u_-owner caller; hosted enrollment fixes it to the manager's owner.
|
|
4432
|
+
// Match the same owner-domain boundary as authorizeNamed, without treating the spawn's
|
|
4433
|
+
// spawner relation as authority over a different owner's session.
|
|
4434
|
+
const callerOwner = parsePrincipalKey(caller)?.owner;
|
|
4435
|
+
const childOwner = this.remoteAuthority?.owner ?? (callerOwner?.startsWith("u_") ? callerOwner : undefined);
|
|
4436
|
+
const ownsChild = this.userMode && callerOwner !== undefined && callerOwner === childOwner;
|
|
4437
|
+
if (!admin && !ownsChild) {
|
|
4402
4438
|
if (requestedEvents === true)
|
|
4403
4439
|
return Promise.resolve({
|
|
4404
4440
|
ok: false,
|
|
@@ -4450,14 +4486,14 @@ export class Manager {
|
|
|
4450
4486
|
cwd = realpathSync(resolved);
|
|
4451
4487
|
}
|
|
4452
4488
|
catch (error) {
|
|
4453
|
-
return { refusal: `path cannot be resolved: ${error
|
|
4489
|
+
return { refusal: `path cannot be resolved: ${rejectionText(error)}` };
|
|
4454
4490
|
}
|
|
4455
4491
|
try {
|
|
4456
4492
|
if (!statSync(cwd).isDirectory())
|
|
4457
4493
|
return { refusal: "path is not a directory" };
|
|
4458
4494
|
}
|
|
4459
4495
|
catch (error) {
|
|
4460
|
-
return { refusal: error
|
|
4496
|
+
return { refusal: rejectionText(error) };
|
|
4461
4497
|
}
|
|
4462
4498
|
return { cwd, host: hostname() };
|
|
4463
4499
|
}
|
|
@@ -4505,7 +4541,7 @@ export class Manager {
|
|
|
4505
4541
|
declared = loadExtensionsManifest().extensions.flatMap((ext) => extensionConnectors(ext));
|
|
4506
4542
|
}
|
|
4507
4543
|
catch (error) {
|
|
4508
|
-
const reason = error
|
|
4544
|
+
const reason = rejectionText(error);
|
|
4509
4545
|
console.error(`! manager boot: connector inventory unavailable - ${reason}`);
|
|
4510
4546
|
this.connectorStatuses = [{ agent: "(inventory)", state: "unavailable", binaries: {}, reason }];
|
|
4511
4547
|
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 +4625,7 @@ export class Manager {
|
|
|
4589
4625
|
return { agent: connector.name, supported: true, ...catalog };
|
|
4590
4626
|
}
|
|
4591
4627
|
catch (e) {
|
|
4592
|
-
return { agent: connector.name, supported: true, models: [], error: e
|
|
4628
|
+
return { agent: connector.name, supported: true, models: [], error: rejectionText(e) };
|
|
4593
4629
|
}
|
|
4594
4630
|
};
|
|
4595
4631
|
if (requested) {
|
|
@@ -4598,7 +4634,7 @@ export class Manager {
|
|
|
4598
4634
|
connector = await this.resolveConnector(requested);
|
|
4599
4635
|
}
|
|
4600
4636
|
catch (e) {
|
|
4601
|
-
return { ok: false, error: e
|
|
4637
|
+
return { ok: false, error: rejectionText(e) };
|
|
4602
4638
|
}
|
|
4603
4639
|
const result = await one(connector);
|
|
4604
4640
|
return result.error ? { ok: false, error: result.error } : { ok: true, data: result };
|
|
@@ -4612,7 +4648,7 @@ export class Manager {
|
|
|
4612
4648
|
connector = await this.resolveConnector(name);
|
|
4613
4649
|
}
|
|
4614
4650
|
catch (e) {
|
|
4615
|
-
return { agent: name, supported: false, models: [], error: e
|
|
4651
|
+
return { agent: name, supported: false, models: [], error: rejectionText(e) };
|
|
4616
4652
|
}
|
|
4617
4653
|
return one(connector);
|
|
4618
4654
|
}));
|
|
@@ -4674,7 +4710,7 @@ export class Manager {
|
|
|
4674
4710
|
persistLaunchSpec(this.workspaceRoot, spec);
|
|
4675
4711
|
}
|
|
4676
4712
|
catch (e) {
|
|
4677
|
-
return { ok: false, error: e
|
|
4713
|
+
return { ok: false, error: rejectionText(e) };
|
|
4678
4714
|
}
|
|
4679
4715
|
}
|
|
4680
4716
|
else {
|
|
@@ -4682,7 +4718,7 @@ export class Manager {
|
|
|
4682
4718
|
spec = launchSpecForRun(this.workspaceRoot, runId);
|
|
4683
4719
|
}
|
|
4684
4720
|
catch (e) {
|
|
4685
|
-
return { ok: false, error: e
|
|
4721
|
+
return { ok: false, error: rejectionText(e) };
|
|
4686
4722
|
}
|
|
4687
4723
|
}
|
|
4688
4724
|
const la = spec.agents.find((a) => a.name === name);
|
|
@@ -4705,7 +4741,7 @@ export class Manager {
|
|
|
4705
4741
|
configPath = materializePersona(this.workspaceRoot, runId, la);
|
|
4706
4742
|
}
|
|
4707
4743
|
catch (e) {
|
|
4708
|
-
return { ok: false, error: e
|
|
4744
|
+
return { ok: false, error: rejectionText(e) };
|
|
4709
4745
|
}
|
|
4710
4746
|
const reply = await this.startAgent({ ...launchAgentToStartOpts(la, configPath, spec.owner, runId), route }, caller, hooks);
|
|
4711
4747
|
if (reply.ok)
|
|
@@ -4789,7 +4825,7 @@ export class Manager {
|
|
|
4789
4825
|
def = loadAgentFile(configPath);
|
|
4790
4826
|
}
|
|
4791
4827
|
catch (e) {
|
|
4792
|
-
return { ok: false, error: e
|
|
4828
|
+
return { ok: false, error: rejectionText(e) };
|
|
4793
4829
|
}
|
|
4794
4830
|
}
|
|
4795
4831
|
const agent = resolveAgentType({ flag: opts.agent, pin: def?.agent, callerDefault: opts.defaultAgent });
|
|
@@ -4803,7 +4839,7 @@ export class Manager {
|
|
|
4803
4839
|
connector = await this.resolveConnector(agent);
|
|
4804
4840
|
}
|
|
4805
4841
|
catch (e) {
|
|
4806
|
-
return { ok: false, error: e
|
|
4842
|
+
return { ok: false, error: rejectionText(e) };
|
|
4807
4843
|
}
|
|
4808
4844
|
const readinessTimeoutMs = connector.readinessTimeoutMs ?? this.readinessTimeoutMs;
|
|
4809
4845
|
if (!Number.isSafeInteger(readinessTimeoutMs) || readinessTimeoutMs <= 0)
|
|
@@ -4842,7 +4878,7 @@ export class Manager {
|
|
|
4842
4878
|
carried = this.transcripts.peek(opts.resumeClaim, opts.resume);
|
|
4843
4879
|
}
|
|
4844
4880
|
catch (e) {
|
|
4845
|
-
return { ok: false, error: e
|
|
4881
|
+
return { ok: false, error: rejectionText(e) };
|
|
4846
4882
|
}
|
|
4847
4883
|
}
|
|
4848
4884
|
// A restart policy this host cannot honour is refused at accept, never accepted and ignored.
|
|
@@ -4865,7 +4901,7 @@ export class Manager {
|
|
|
4865
4901
|
this.delegatedMeshRecord();
|
|
4866
4902
|
}
|
|
4867
4903
|
catch (e) {
|
|
4868
|
-
return { ok: false, error: e
|
|
4904
|
+
return { ok: false, error: rejectionText(e) };
|
|
4869
4905
|
}
|
|
4870
4906
|
if (opts.resume)
|
|
4871
4907
|
return { ok: false, error: `${delegatedBy}, so it cannot resume a session held on this host (resume)` };
|
|
@@ -4954,7 +4990,7 @@ export class Manager {
|
|
|
4954
4990
|
allowSubscribe = resolveReadAcl(subscribe ?? [], allowSubscribe);
|
|
4955
4991
|
}
|
|
4956
4992
|
catch (e) {
|
|
4957
|
-
return { ok: false, error: opts.resolved ? `launch agent: ${e
|
|
4993
|
+
return { ok: false, error: opts.resolved ? `launch agent: ${rejectionText(e)}` : `persona ${configPath}: ${rejectionText(e)}` };
|
|
4958
4994
|
}
|
|
4959
4995
|
// Checked after every path has resolved its selectors: a direct `startAgent` call skips the
|
|
4960
4996
|
// `start` op's checks, and no connector may receive a blank one (#2862).
|
|
@@ -5005,7 +5041,7 @@ export class Manager {
|
|
|
5005
5041
|
});
|
|
5006
5042
|
}
|
|
5007
5043
|
catch (e) {
|
|
5008
|
-
return { ok: false, error: e
|
|
5044
|
+
return { ok: false, error: rejectionText(e) };
|
|
5009
5045
|
}
|
|
5010
5046
|
if (policyRefusal)
|
|
5011
5047
|
return { ok: false, error: policyRefusal };
|
|
@@ -5036,7 +5072,7 @@ export class Manager {
|
|
|
5036
5072
|
reopenSession = readContinuityAssignment(this.workspaceRoot, { space: this.space, name: identityName, connector: agent, cwd: resolvedCwd ?? this.workspaceRoot });
|
|
5037
5073
|
}
|
|
5038
5074
|
catch (e) {
|
|
5039
|
-
return { ok: false, error: e
|
|
5075
|
+
return { ok: false, error: rejectionText(e) };
|
|
5040
5076
|
}
|
|
5041
5077
|
}
|
|
5042
5078
|
// The child takes launch options as `--opt k=v`, so only string values can cross to it.
|
|
@@ -5529,7 +5565,7 @@ export class Manager {
|
|
|
5529
5565
|
}
|
|
5530
5566
|
}
|
|
5531
5567
|
catch (error) {
|
|
5532
|
-
const detail = `${managed.name} joined, but its exact host session could not be bound for supervised recovery: ${error
|
|
5568
|
+
const detail = `${managed.name} joined, but its exact host session could not be bound for supervised recovery: ${rejectionText(error)}`;
|
|
5533
5569
|
this.stopHandle(managed, false);
|
|
5534
5570
|
this.freeSlot(managed, true, "session-bind-failed");
|
|
5535
5571
|
await hooks?.onOutcome?.({ kind: "failed", data: { error: detail } });
|
|
@@ -5646,7 +5682,7 @@ export class Manager {
|
|
|
5646
5682
|
: principalKey(DEV_OWNER, entry.identity.id).key;
|
|
5647
5683
|
}
|
|
5648
5684
|
catch (e) {
|
|
5649
|
-
return { ok: false, agents: [], error: `invalid retained principal for ${entry.name}: ${e
|
|
5685
|
+
return { ok: false, agents: [], error: `invalid retained principal for ${entry.name}: ${rejectionText(e)}` };
|
|
5650
5686
|
}
|
|
5651
5687
|
if (principals.has(principal))
|
|
5652
5688
|
return { ok: false, agents: [], error: `resume inventory contains duplicate principal "${principal}"` };
|
|
@@ -5722,7 +5758,7 @@ export class Manager {
|
|
|
5722
5758
|
discardLaunchArtifacts(spec.artifacts);
|
|
5723
5759
|
}
|
|
5724
5760
|
catch (e) {
|
|
5725
|
-
console.error(`! resume: ${e
|
|
5761
|
+
console.error(`! resume: ${rejectionText(e)}`);
|
|
5726
5762
|
}
|
|
5727
5763
|
}
|
|
5728
5764
|
release();
|
|
@@ -5801,7 +5837,7 @@ export class Manager {
|
|
|
5801
5837
|
throw new Error(`retained credential identity ${actual} does not match inventory principal ${entry.identity.id}`);
|
|
5802
5838
|
}
|
|
5803
5839
|
catch (e) {
|
|
5804
|
-
throw new Error(`retained credential for ${entry.name} is unusable: ${e
|
|
5840
|
+
throw new Error(`retained credential for ${entry.name} is unusable: ${rejectionText(e)}`);
|
|
5805
5841
|
}
|
|
5806
5842
|
const accepted = await this.probeStaticCredential(credentialText);
|
|
5807
5843
|
if (!accepted.ok)
|
|
@@ -5889,7 +5925,7 @@ export class Manager {
|
|
|
5889
5925
|
};
|
|
5890
5926
|
}
|
|
5891
5927
|
catch (e) {
|
|
5892
|
-
throw new Error(`retained user principal ${entry.identity.owner}.${entry.identity.actor} could not be reused: ${e
|
|
5928
|
+
throw new Error(`retained user principal ${entry.identity.owner}.${entry.identity.actor} could not be reused: ${rejectionText(e)}`);
|
|
5893
5929
|
}
|
|
5894
5930
|
}
|
|
5895
5931
|
/** The seat reference behind a live retained principal, when that seat is provably one a lost
|
|
@@ -5922,7 +5958,7 @@ export class Manager {
|
|
|
5922
5958
|
console.error(`resume: reaped orphaned seat ${reference.kind}:${reference.id} of retained principal "${principal}" (${evidence.detail})`);
|
|
5923
5959
|
}
|
|
5924
5960
|
catch (e) {
|
|
5925
|
-
return `retained principal "${principal}" is already live and its recorded seat ${reference.kind}:${reference.id} could not be reaped: ${e
|
|
5961
|
+
return `retained principal "${principal}" is already live and its recorded seat ${reference.kind}:${reference.id} could not be reaped: ${rejectionText(e)}`;
|
|
5926
5962
|
}
|
|
5927
5963
|
const deadline = Date.now() + ORPHAN_PRESENCE_CLEAR_MS;
|
|
5928
5964
|
while (this.ep.getRoster().some((p) => p.card.id === principal && p.status !== "offline")) {
|
|
@@ -5958,7 +5994,7 @@ export class Manager {
|
|
|
5958
5994
|
await this.validateRetainedAuthority(entry);
|
|
5959
5995
|
}
|
|
5960
5996
|
catch (e) {
|
|
5961
|
-
return { ok: false, error: e
|
|
5997
|
+
return { ok: false, error: rejectionText(e) };
|
|
5962
5998
|
}
|
|
5963
5999
|
return this.launchPreparedResume(entry, cached, batchReserved);
|
|
5964
6000
|
}
|
|
@@ -5968,7 +6004,7 @@ export class Manager {
|
|
|
5968
6004
|
return { ok: false, error: `retained cwd is not a real directory: ${entry.launch.cwd}` };
|
|
5969
6005
|
}
|
|
5970
6006
|
catch (e) {
|
|
5971
|
-
return { ok: false, error: `retained cwd unavailable: ${entry.launch.cwd} (${e
|
|
6007
|
+
return { ok: false, error: `retained cwd unavailable: ${entry.launch.cwd} (${rejectionText(e)})` };
|
|
5972
6008
|
}
|
|
5973
6009
|
let connector;
|
|
5974
6010
|
try {
|
|
@@ -5982,7 +6018,7 @@ export class Manager {
|
|
|
5982
6018
|
connector = await this.resolveConnector(entry.launch.connector);
|
|
5983
6019
|
}
|
|
5984
6020
|
catch (e) {
|
|
5985
|
-
return { ok: false, error: e
|
|
6021
|
+
return { ok: false, error: rejectionText(e) };
|
|
5986
6022
|
}
|
|
5987
6023
|
const harness = this.launchHarness(connector);
|
|
5988
6024
|
if ("refusal" in harness)
|
|
@@ -5992,7 +6028,7 @@ export class Manager {
|
|
|
5992
6028
|
retainedSession = this.retainedSessionId(entry, connector);
|
|
5993
6029
|
}
|
|
5994
6030
|
catch (error) {
|
|
5995
|
-
return { ok: false, error: error
|
|
6031
|
+
return { ok: false, error: rejectionText(error) };
|
|
5996
6032
|
}
|
|
5997
6033
|
if (entry.launch.forkSource && !retainedSession && !connector.supportsResume)
|
|
5998
6034
|
return { ok: false, error: `${connector.name} connector does not support session fork (resume)` };
|
|
@@ -6012,7 +6048,7 @@ export class Manager {
|
|
|
6012
6048
|
spec = source.agents.find((a) => a.name === launchSource.requested);
|
|
6013
6049
|
}
|
|
6014
6050
|
catch (e) {
|
|
6015
|
-
return { ok: false, error: e
|
|
6051
|
+
return { ok: false, error: rejectionText(e) };
|
|
6016
6052
|
}
|
|
6017
6053
|
if (!spec || spec.hash !== launchSource.hash)
|
|
6018
6054
|
return { ok: false, error: `retained manifest agent ${launchSource.requested} is missing or its hash changed; refusing same-principal resume` };
|
|
@@ -6024,7 +6060,7 @@ export class Manager {
|
|
|
6024
6060
|
launchOptions = loadAgentFile(entry.launch.source.configPath).launchOptions;
|
|
6025
6061
|
}
|
|
6026
6062
|
catch (e) {
|
|
6027
|
-
return { ok: false, error: e
|
|
6063
|
+
return { ok: false, error: rejectionText(e) };
|
|
6028
6064
|
}
|
|
6029
6065
|
}
|
|
6030
6066
|
// Asked once `exact` is known: an exact seat reopens its retained session or fails, like its
|
|
@@ -6037,7 +6073,7 @@ export class Manager {
|
|
|
6037
6073
|
authority = await this.validateRetainedAuthority(entry);
|
|
6038
6074
|
}
|
|
6039
6075
|
catch (e) {
|
|
6040
|
-
return { ok: false, error: e
|
|
6076
|
+
return { ok: false, error: rejectionText(e) };
|
|
6041
6077
|
}
|
|
6042
6078
|
try {
|
|
6043
6079
|
const resumeConfig = loadCotalConfig(this.workspaceRoot);
|
|
@@ -6086,7 +6122,7 @@ export class Manager {
|
|
|
6086
6122
|
return this.launchPreparedResume(entry, value, batchReserved);
|
|
6087
6123
|
}
|
|
6088
6124
|
catch (e) {
|
|
6089
|
-
return { ok: false, error: e
|
|
6125
|
+
return { ok: false, error: rejectionText(e) };
|
|
6090
6126
|
}
|
|
6091
6127
|
}
|
|
6092
6128
|
finally {
|
|
@@ -6169,7 +6205,7 @@ export class Manager {
|
|
|
6169
6205
|
await this.recordSlotRuntime(managed);
|
|
6170
6206
|
}
|
|
6171
6207
|
catch (error) {
|
|
6172
|
-
return await this.stopFailedResume(managed, "resume-custody-unrecorded", `${managed.name} resumed, but ${error
|
|
6208
|
+
return await this.stopFailedResume(managed, "resume-custody-unrecorded", `${managed.name} resumed, but ${rejectionText(error)}`);
|
|
6173
6209
|
}
|
|
6174
6210
|
const readiness = await this.awaitReadiness(managed, readinessTimeoutMs);
|
|
6175
6211
|
if (!readiness.ok && !readiness.uncertain)
|
|
@@ -6196,7 +6232,7 @@ export class Manager {
|
|
|
6196
6232
|
}
|
|
6197
6233
|
}
|
|
6198
6234
|
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
|
|
6235
|
+
return await this.stopFailedResume(managed, "resume-session-rebind-failed", `${managed.name} resumed, but its exact host session could not be rebound: ${rejectionText(error)}`);
|
|
6200
6236
|
}
|
|
6201
6237
|
}
|
|
6202
6238
|
if (!this.resumeAttemptId)
|
|
@@ -6210,7 +6246,7 @@ export class Manager {
|
|
|
6210
6246
|
};
|
|
6211
6247
|
}
|
|
6212
6248
|
catch (e) {
|
|
6213
|
-
return { ok: false, error: e
|
|
6249
|
+
return { ok: false, error: rejectionText(e) };
|
|
6214
6250
|
}
|
|
6215
6251
|
finally {
|
|
6216
6252
|
if (!batchReserved) {
|
|
@@ -6229,7 +6265,7 @@ export class Manager {
|
|
|
6229
6265
|
}
|
|
6230
6266
|
catch (exit) {
|
|
6231
6267
|
this.watchExit(managed);
|
|
6232
|
-
return { ok: false, error: `${detail}; it stays managed because its stop is unproven: ${exit
|
|
6268
|
+
return { ok: false, error: `${detail}; it stays managed because its stop is unproven: ${rejectionText(exit)}` };
|
|
6233
6269
|
}
|
|
6234
6270
|
this.freeSlot(managed, true, cause, true);
|
|
6235
6271
|
return { ok: false, error: detail };
|
|
@@ -6981,7 +7017,7 @@ export class Manager {
|
|
|
6981
7017
|
// One registration operation for this boot. Fresh executors may replace a dead connection, but
|
|
6982
7018
|
// they must resume THIS freeze rather than minting a new op that discards Phase-2 progress.
|
|
6983
7019
|
const registrationOpId = mintLifecycleUid();
|
|
6984
|
-
const executorExpired = (e) => /closed connection/i.test(
|
|
7020
|
+
const executorExpired = (e) => /closed connection/i.test(rejectionText(e));
|
|
6985
7021
|
const retryExpiredExecutor = async (label, fn) => {
|
|
6986
7022
|
for (;;) {
|
|
6987
7023
|
try {
|
|
@@ -7588,14 +7624,14 @@ export class Manager {
|
|
|
7588
7624
|
await this.reconcileOneGoal(entry.ref, entry.iid);
|
|
7589
7625
|
}
|
|
7590
7626
|
catch (e) {
|
|
7591
|
-
console.error(`! goal reconcile for ${entry.ref.goalId}: ${e
|
|
7627
|
+
console.error(`! goal reconcile for ${entry.ref.goalId}: ${rejectionText(e)}`);
|
|
7592
7628
|
}
|
|
7593
7629
|
}
|
|
7594
7630
|
if (entries.length)
|
|
7595
7631
|
console.error(`goal-index boot reconcile: swept ${entries.length} inherited goal(s)`);
|
|
7596
7632
|
}
|
|
7597
7633
|
catch (e) {
|
|
7598
|
-
console.error(`! goal-index boot reconcile failed: ${e
|
|
7634
|
+
console.error(`! goal-index boot reconcile failed: ${rejectionText(e)} - accepted goals from a predecessor may stay unsettled until the next restart`);
|
|
7599
7635
|
}
|
|
7600
7636
|
finally {
|
|
7601
7637
|
this.goalReconcileDone = true;
|
|
@@ -7645,7 +7681,7 @@ export class Manager {
|
|
|
7645
7681
|
return;
|
|
7646
7682
|
}
|
|
7647
7683
|
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
|
|
7684
|
+
const t = setTimeout(() => { settle().catch((e) => console.error(`! goal reconcile settle ${ref.goalId}: ${rejectionText(e)}`)); }, remaining + 100);
|
|
7649
7685
|
t.unref?.();
|
|
7650
7686
|
}
|
|
7651
7687
|
/** P2 item 2: publish a goal PROGRESS event on the caller-scoped epe subtree, over the SERVE
|
|
@@ -7661,7 +7697,7 @@ export class Manager {
|
|
|
7661
7697
|
nc.publish(epeSubject(this.space, MANAGER_ENDPOINT, this.managerInstanceId, epoch, goalProgressTopic(ref)), new TextEncoder().encode(JSON.stringify({ v: 1, goalId: ref.goalId, ...event })));
|
|
7662
7698
|
}
|
|
7663
7699
|
catch (e) {
|
|
7664
|
-
console.error(`! goal progress emit for ${ref.goalId} failed: ${e
|
|
7700
|
+
console.error(`! goal progress emit for ${ref.goalId} failed: ${rejectionText(e)}`);
|
|
7665
7701
|
}
|
|
7666
7702
|
}
|
|
7667
7703
|
/** The own-gate currency belt: before the goal-writer commits a terminal fact, the manager reads
|
|
@@ -7800,7 +7836,7 @@ export class Manager {
|
|
|
7800
7836
|
// ENTRY. This leg is infrastructure-class and converges through the reconcile index at the
|
|
7801
7837
|
// next boot, which is why the index is NOT cleared above on this path. Do not "close" it
|
|
7802
7838
|
// here with a retry loop.
|
|
7803
|
-
console.error(`! goal terminal commit for ${goalId} failed: ${e
|
|
7839
|
+
console.error(`! goal terminal commit for ${goalId} failed: ${rejectionText(e)}`);
|
|
7804
7840
|
}
|
|
7805
7841
|
};
|
|
7806
7842
|
const bg = run({
|
|
@@ -7885,7 +7921,7 @@ export class Manager {
|
|
|
7885
7921
|
// the caller follows epe to a terminal that never comes, and the reconcile index that would
|
|
7886
7922
|
// settle it is only swept at BOOT, so a manager that stays up never converges it.
|
|
7887
7923
|
if (reply.ok === false && !terminalEntered)
|
|
7888
|
-
return onOutcome({ kind: "failed", data:
|
|
7924
|
+
return onOutcome({ kind: "failed", data: failedTerminal(reply.error ?? "spawn failed after accept", reply) });
|
|
7889
7925
|
}).catch((e) => {
|
|
7890
7926
|
if (acceptance === undefined) {
|
|
7891
7927
|
rejectAccept(e);
|
|
@@ -7893,9 +7929,9 @@ export class Manager {
|
|
|
7893
7929
|
}
|
|
7894
7930
|
// Same obligation for a genuine rejection (one that escaped `run`'s own catch).
|
|
7895
7931
|
if (!terminalEntered)
|
|
7896
|
-
return onOutcome({ kind: "failed", data:
|
|
7897
|
-
console.error(`! spawn-as-action async body for ${goalId}: ${
|
|
7898
|
-
}).catch((e) => console.error(`! goal terminal fallback for ${goalId}: ${
|
|
7932
|
+
return onOutcome({ kind: "failed", data: failedTerminal(rejectionText(e), caughtEnvelope(e)) });
|
|
7933
|
+
console.error(`! spawn-as-action async body for ${goalId}: ${rejectionText(e)}`);
|
|
7934
|
+
}).catch((e) => console.error(`! goal terminal fallback for ${goalId}: ${rejectionText(e)}`));
|
|
7899
7935
|
return acceptP;
|
|
7900
7936
|
}
|
|
7901
7937
|
/** H2: an acceptance served from a WINNER'S durable acceptance floor (the goal-index entry it
|
|
@@ -8038,7 +8074,7 @@ export class Manager {
|
|
|
8038
8074
|
// The accept is inline (no launch closure to fail later), so a post-bind throw unwinds HERE:
|
|
8039
8075
|
// commit the failed terminal this attempt owns, clear the index, and refuse the accept — an
|
|
8040
8076
|
// accepted-but-unanswered goal must never be left for the boot sweep to find (H1's rule).
|
|
8041
|
-
const msg =
|
|
8077
|
+
const msg = rejectionText(e);
|
|
8042
8078
|
try {
|
|
8043
8079
|
await this.assertGoalWriterEpochCurrent(executor.epoch);
|
|
8044
8080
|
// The goal is TARGET-PINNED, so its completion proves the seat's currency exactly as the
|
|
@@ -8054,9 +8090,9 @@ export class Manager {
|
|
|
8054
8090
|
await clearGoalIndex(gw.ctx, ref);
|
|
8055
8091
|
}
|
|
8056
8092
|
catch (e2) {
|
|
8057
|
-
console.error(`! turn accept unwind for ${goalId}: ${e2
|
|
8093
|
+
console.error(`! turn accept unwind for ${goalId}: ${rejectionText(e2)}`);
|
|
8058
8094
|
}
|
|
8059
|
-
throw e
|
|
8095
|
+
throw caughtEnvelope(e) ?? new EpEnvelopeError("internal", `turn accept for ${goalId} failed: ${msg}`);
|
|
8060
8096
|
}
|
|
8061
8097
|
const pending = {
|
|
8062
8098
|
ref, goalId,
|
|
@@ -8256,7 +8292,7 @@ export class Manager {
|
|
|
8256
8292
|
await clearGoalIndex(gw.ctx, p.ref);
|
|
8257
8293
|
}
|
|
8258
8294
|
catch (e) {
|
|
8259
|
-
console.error(`! turn deadline terminal for ${p.goalId}: ${e
|
|
8295
|
+
console.error(`! turn deadline terminal for ${p.goalId}: ${rejectionText(e)}`);
|
|
8260
8296
|
}
|
|
8261
8297
|
this.maybeStopTurnSweep();
|
|
8262
8298
|
}
|
|
@@ -8288,7 +8324,7 @@ export class Manager {
|
|
|
8288
8324
|
ensureTurnSweep() {
|
|
8289
8325
|
if (this.turnSweepTimer !== undefined)
|
|
8290
8326
|
return;
|
|
8291
|
-
const t = setInterval(() => { void this.sweepTurnDeadlines().catch((e) => console.error(`! turn deadline sweep: ${e
|
|
8327
|
+
const t = setInterval(() => { void this.sweepTurnDeadlines().catch((e) => console.error(`! turn deadline sweep: ${rejectionText(e)}`)); }, TURN_SWEEP_MS);
|
|
8292
8328
|
t.unref?.();
|
|
8293
8329
|
this.turnSweepTimer = t;
|
|
8294
8330
|
}
|
|
@@ -8357,7 +8393,7 @@ export class Manager {
|
|
|
8357
8393
|
await this.commitTurnDeadline(p);
|
|
8358
8394
|
}
|
|
8359
8395
|
catch (e) {
|
|
8360
|
-
console.error(`! turn deadline sweep for ${p.goalId}: ${e
|
|
8396
|
+
console.error(`! turn deadline sweep for ${p.goalId}: ${rejectionText(e)}`);
|
|
8361
8397
|
}
|
|
8362
8398
|
}
|
|
8363
8399
|
}
|
|
@@ -8488,7 +8524,7 @@ export class Manager {
|
|
|
8488
8524
|
});
|
|
8489
8525
|
}
|
|
8490
8526
|
catch (e) {
|
|
8491
|
-
throw new Error(`could not record the custody reference ${runtime.kind}:${runtime.id} on the static slot of "${a.name}": ${e
|
|
8527
|
+
throw new Error(`could not record the custody reference ${runtime.kind}:${runtime.id} on the static slot of "${a.name}": ${rejectionText(e)}`);
|
|
8492
8528
|
}
|
|
8493
8529
|
}
|
|
8494
8530
|
/** Mint the custody reference for a seat this manager is about to spawn, BEFORE the processes
|
|
@@ -8534,7 +8570,7 @@ export class Manager {
|
|
|
8534
8570
|
disposal =
|
|
8535
8571
|
e instanceof RuntimeReapUnproven
|
|
8536
8572
|
? `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
|
|
8573
|
+
: `the spawned seat could NOT be reaped: ${rejectionText(e)}`;
|
|
8538
8574
|
}
|
|
8539
8575
|
}
|
|
8540
8576
|
throw new Error(`runtime "${this.runtime.kind}" reserved custody ${reserved.kind}:${reserved.id} for "${name}" but spawned ` +
|
|
@@ -8635,8 +8671,8 @@ export class Manager {
|
|
|
8635
8671
|
catch (e) {
|
|
8636
8672
|
const h = this.retiring.get(a.name);
|
|
8637
8673
|
if (h && h.lifecycleUid === a.lifecycleUid)
|
|
8638
|
-
h.lastError = `the static retirement did not complete (${e
|
|
8639
|
-
console.error(`static retirement ${a.name} (${a.id}): ${e
|
|
8674
|
+
h.lastError = `the static retirement did not complete (${rejectionText(e)}); the name stays held - a same-name spawn retries the same terminal (op ${opId})`;
|
|
8675
|
+
console.error(`static retirement ${a.name} (${a.id}): ${rejectionText(e)}`);
|
|
8640
8676
|
if (surfaceFailure)
|
|
8641
8677
|
throw e;
|
|
8642
8678
|
}
|
|
@@ -8739,7 +8775,7 @@ export class Manager {
|
|
|
8739
8775
|
await this.retryStaticReconcile(key);
|
|
8740
8776
|
};
|
|
8741
8777
|
void run().catch((error) => {
|
|
8742
|
-
item.lastError = `retry driver failed before the exact terminal: ${error
|
|
8778
|
+
item.lastError = `retry driver failed before the exact terminal: ${rejectionText(error)}`;
|
|
8743
8779
|
item.disposition = "retry-exhausted";
|
|
8744
8780
|
item.remedy = "restart this manager for a fresh per-process retry budget";
|
|
8745
8781
|
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 +8816,7 @@ export class Manager {
|
|
|
8780
8816
|
row = await this.readStaticReconcileSlot(item.alias);
|
|
8781
8817
|
}
|
|
8782
8818
|
catch (error) {
|
|
8783
|
-
item.lastError = `durable slot re-read failed: ${error
|
|
8819
|
+
item.lastError = `durable slot re-read failed: ${rejectionText(error)}`;
|
|
8784
8820
|
if (item.attempts >= item.maxAttempts) {
|
|
8785
8821
|
item.disposition = "retry-exhausted";
|
|
8786
8822
|
item.remedy = "restart this manager for a fresh per-process retry budget";
|
|
@@ -8864,7 +8900,7 @@ export class Manager {
|
|
|
8864
8900
|
return true;
|
|
8865
8901
|
}
|
|
8866
8902
|
catch (error) {
|
|
8867
|
-
item.lastError = error
|
|
8903
|
+
item.lastError = rejectionText(error);
|
|
8868
8904
|
try {
|
|
8869
8905
|
const fresh = await this.readStaticReconcileSlot(item.alias);
|
|
8870
8906
|
if (fresh?.lifecycleUid === item.lifecycleUid && fresh.actor === item.actor)
|
|
@@ -8963,16 +8999,8 @@ export class Manager {
|
|
|
8963
8999
|
const kvm = new Kvm(nc);
|
|
8964
9000
|
await ensureAuthorityStores(jsm, kvm, this.space);
|
|
8965
9001
|
const recordsKv = await kvm.open(recordsBucket(this.space));
|
|
8966
|
-
const
|
|
8967
|
-
|
|
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
|
-
}
|
|
9002
|
+
for (const slot of await walkStaticSlots(recordsKv, DEV_OWNER))
|
|
9003
|
+
slotRows.push(slot.row);
|
|
8976
9004
|
}
|
|
8977
9005
|
catch (error) {
|
|
8978
9006
|
sweep.completedAt = new Date().toISOString();
|
|
@@ -9221,7 +9249,7 @@ export class Manager {
|
|
|
9221
9249
|
return { ok: true, data: result };
|
|
9222
9250
|
}
|
|
9223
9251
|
catch (e) {
|
|
9224
|
-
return { ok: false, error: e
|
|
9252
|
+
return { ok: false, error: rejectionText(e) };
|
|
9225
9253
|
}
|
|
9226
9254
|
}
|
|
9227
9255
|
/** Persist a peer-defined persona as config. After this, `start name` auto-discovers
|
|
@@ -9253,7 +9281,7 @@ export class Manager {
|
|
|
9253
9281
|
existing = loadAgentFile(path);
|
|
9254
9282
|
}
|
|
9255
9283
|
catch (e) {
|
|
9256
|
-
return { ok: false, error: e
|
|
9284
|
+
return { ok: false, error: rejectionText(e) };
|
|
9257
9285
|
}
|
|
9258
9286
|
if (!admin && existing.owner !== caller) {
|
|
9259
9287
|
const owner = existing.owner ? `owned by ${existing.owner}` : "operator-owned (legacy file - no agent owner)";
|
|
@@ -9280,13 +9308,13 @@ export class Manager {
|
|
|
9280
9308
|
}, existing);
|
|
9281
9309
|
}
|
|
9282
9310
|
catch (e) {
|
|
9283
|
-
return { ok: false, error: e
|
|
9311
|
+
return { ok: false, error: rejectionText(e) };
|
|
9284
9312
|
}
|
|
9285
9313
|
try {
|
|
9286
9314
|
saveAgentFile(path, def);
|
|
9287
9315
|
}
|
|
9288
9316
|
catch (e) {
|
|
9289
|
-
return { ok: false, error: e
|
|
9317
|
+
return { ok: false, error: rejectionText(e) };
|
|
9290
9318
|
}
|
|
9291
9319
|
return { ok: true, data: { name, path } };
|
|
9292
9320
|
}
|
|
@@ -9444,7 +9472,7 @@ export class Manager {
|
|
|
9444
9472
|
accepted = await write(part.data);
|
|
9445
9473
|
}
|
|
9446
9474
|
catch (error) {
|
|
9447
|
-
throw new EpEnvelopeError("unavailable", `input for seat "${a.name}" failed: ${error
|
|
9475
|
+
throw new EpEnvelopeError("unavailable", `input for seat "${a.name}" failed: ${rejectionText(error)}`);
|
|
9448
9476
|
}
|
|
9449
9477
|
if (!Number.isSafeInteger(accepted)) {
|
|
9450
9478
|
throw new EpEnvelopeError("unavailable", `input for seat "${a.name}" failed: runtime accepted ${String(accepted)} of ${intendedBytes} bytes`);
|
|
@@ -9495,7 +9523,7 @@ export class Manager {
|
|
|
9495
9523
|
}
|
|
9496
9524
|
catch (e) {
|
|
9497
9525
|
slot.release();
|
|
9498
|
-
return { ok: false, error: e
|
|
9526
|
+
return { ok: false, error: rejectionText(e) };
|
|
9499
9527
|
}
|
|
9500
9528
|
// establishAttach releases the claim it was handed on every exit; nothing to unwind here.
|
|
9501
9529
|
const { grant } = await this.sessionPlane.establishAttach(caller, { name: a.name, lifecycleUid: a.lifecycleUid }, session, slot);
|