@byok-sdk/client 0.6.1 → 0.7.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.
@@ -49,8 +49,11 @@ export declare class AgentEgressController {
49
49
  private readonly latestStatus;
50
50
  private readonly reliableStatus;
51
51
  private readonly drops;
52
+ private active;
52
53
  constructor(options: AgentEgressControllerOptions);
53
54
  get policy(): Readonly<AgentEgressPolicy>;
55
+ /** Permanently fail closed after its authenticated enrollment is replaced. */
56
+ deactivate(): void;
54
57
  status(): AgentEgressStatus;
55
58
  dropReceipts(): readonly AgentEgressDropReceipt[];
56
59
  noteTransportDrop(reason: AgentEgressDropReason, agentRef?: AgentRef): void;
@@ -62,13 +62,6 @@ export interface HostedJournalConfig {
62
62
  * to a closed set instead of a re-interpretation of an existing config.
63
63
  */
64
64
  mode: 'sqlite';
65
- /**
66
- * The tenant every journal row on this device is scoped to (§12.7.2's
67
- * minimum fact set opens with tenant/product/device). Required: a hosted
68
- * daemon that cannot say which tenant its durable evidence belongs to has
69
- * evidence nobody can act on.
70
- */
71
- tenantId: string;
72
65
  /** Bound on waiting for the journal's write lock, ms. Defaults to the journal's own bound. */
73
66
  busyTimeoutMs?: number;
74
67
  /** Per-record byte bound. Defaults to the journal's own bound; oversized records are refused, never truncated. */
@@ -347,8 +340,6 @@ export interface DaemonConfig {
347
340
  deviceAssertion?: DeviceAssertionConfig;
348
341
  }
349
342
  export interface AgentEgressConfig {
350
- /** Authenticated deployment tenant bound into every local reliable record. */
351
- tenantId: string;
352
343
  /** Exact policy the daemon is willing to consume from an Agent offer. */
353
344
  policy: AgentEgressPolicy;
354
345
  /** Named redaction hook for explicit contentful trajectory only. */
@@ -1,6 +1,8 @@
1
1
  import { type EnsureSecureDirOptions } from '../util/secure-dir';
2
2
  export interface DeviceRecord {
3
3
  deviceId: string;
4
+ /** Opaque tenant binding returned by the authenticated pairing response. */
5
+ tenantId: string;
4
6
  /** Current access token (JWT), renewed via challenge/token without re-pairing (protocol §6.2). */
5
7
  accessToken: string;
6
8
  /** ISO-8601 expiry for `accessToken` (our best knowledge of it — see auth-manager.ts for how this is derived after `/byok/pair`, which reports no explicit expiry itself). */
@@ -10,6 +12,13 @@ export interface DeviceRecord {
10
12
  /** Ed25519 public key, base64url — re-sent verbatim on a post-revocation re-pair (protocol §6.3). */
11
13
  devicePublicKey: string;
12
14
  }
15
+ /**
16
+ * A durable enrollment record cannot be used by any steady-state path. Only
17
+ * the explicit pair operation may replace it with a fresh authenticated row.
18
+ */
19
+ export declare class DeviceRecordRePairRequiredError extends Error {
20
+ constructor();
21
+ }
13
22
  /**
14
23
  * Persists the device identity issued by `pair()` — deviceId, current
15
24
  * access token + its expiry, and the device's own Ed25519 keypair. This is
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { randomUUID, createHash, sign, createPrivateKey, generateKeyPairSync, randomBytes, timingSafeEqual, createHmac } from 'crypto';
2
2
  import { promises, mkdirSync, existsSync, renameSync, writeFileSync, chmodSync, statSync, readdirSync, linkSync, fstatSync, lstatSync, unlinkSync, constants, readFileSync, realpathSync } from 'fs';
3
3
  import path, { join, isAbsolute } from 'path';
4
- import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, AgentRefSchema, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, partitionAgentEvents, BYOK_SKILL_PACKS_PATH, byokSkillPackFilePath, BYOK_RECORDS_PATH, byokRecordPath, TASK_TRANSITIONS, AgentEgressPolicySchema, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentContentReceiptPayloadSchema, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, AGENT_EGRESS_POLICY_CAPABILITY, parseMessage, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS, TERMINAL_INFERENCE_USAGE_MAX_TOKENS, RESULT_DOCUMENT_MAX_BYTES, decodeEnvelope, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, UnknownMessageTypeError, BYOK_WS_PATH } from '@byok-sdk/protocol';
4
+ import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, AgentRefSchema, BYOK_PAIR_PATH, PairResponseSchema, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, partitionAgentEvents, BYOK_SKILL_PACKS_PATH, byokSkillPackFilePath, BYOK_RECORDS_PATH, byokRecordPath, TASK_TRANSITIONS, AgentEgressPolicySchema, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentContentReceiptPayloadSchema, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, AGENT_EGRESS_POLICY_CAPABILITY, parseMessage, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS, TERMINAL_INFERENCE_USAGE_MAX_TOKENS, RESULT_DOCUMENT_MAX_BYTES, decodeEnvelope, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, UnknownMessageTypeError, BYOK_WS_PATH } from '@byok-sdk/protocol';
5
5
  import { execFile, spawn } from 'child_process';
6
6
  import os6 from 'os';
7
- import { parseDeviceAssertionEnvelope, tenantId, DeviceProofProtectedClaimsSchema, deviceProofSigningInput, DEVICE_PROOF_SCHEMA_ID, SKILL_PACK_MAX_BYTES, hasCapability, parseSkillPackManifest, checkSkillPackManifest, skillPackContentHashInput, checkSkillPackFileContent, SKILL_PACK_ENTRY_PATH, checkSkillPackEntry, isSkillPackPathSafe, DEVICE_PROOF_HEADER, contentHash as contentHash$1, TRUTH_RECORD_KINDS, nonceSigningBytes, DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_MAX_TTL_MS, CONTENT_HASH_PATTERN, CapabilityDeclarationSchema, DeviceAssertionClaimsSchema, deviceAssertionSigningInput, DEVICE_ASSERTION_SCHEMA_ID } from '@byok-sdk/core';
7
+ import { parseDeviceAssertionEnvelope, tenantId, DeviceProofProtectedClaimsSchema, deviceProofSigningInput, DEVICE_PROOF_SCHEMA_ID, SKILL_PACK_MAX_BYTES, hasCapability, parseSkillPackManifest, checkSkillPackManifest, skillPackContentHashInput, checkSkillPackFileContent, SKILL_PACK_ENTRY_PATH, checkSkillPackEntry, isSkillPackPathSafe, DEVICE_PROOF_HEADER, contentHash as contentHash$1, TRUTH_RECORD_KINDS, nonceSigningBytes, DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_MAX_TTL_MS, CONTENT_HASH_PATTERN, isTenantId, CapabilityDeclarationSchema, DeviceAssertionClaimsSchema, deviceAssertionSigningInput, DEVICE_ASSERTION_SCHEMA_ID } from '@byok-sdk/core';
8
8
  import { promisify } from 'util';
9
9
  import { fileURLToPath } from 'url';
10
10
  import 'readline';
@@ -4683,6 +4683,13 @@ var ApprovalRegistry = class {
4683
4683
  }
4684
4684
  };
4685
4685
  var MAX_DEVICE_RECORD_BYTES = 256 * 1024;
4686
+ var REPAIR_REQUIRED_MESSAGE = "device enrollment record is missing or has an invalid authenticated tenant binding; re-pair required";
4687
+ var DeviceRecordRePairRequiredError = class extends Error {
4688
+ constructor() {
4689
+ super(REPAIR_REQUIRED_MESSAGE);
4690
+ this.name = "DeviceRecordRePairRequiredError";
4691
+ }
4692
+ };
4686
4693
  function sameInode(left, right) {
4687
4694
  return left.dev === right.dev && left.ino === right.ino;
4688
4695
  }
@@ -4692,18 +4699,32 @@ function sameFileState(left, right) {
4692
4699
  function sameContentState(left, right) {
4693
4700
  return sameInode(left, right) && left.size === right.size && left.mtimeNs === right.mtimeNs;
4694
4701
  }
4702
+ function assertDeviceRecord(value) {
4703
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
4704
+ throw new DeviceRecordRePairRequiredError();
4705
+ }
4706
+ const parsed = value;
4707
+ if (typeof parsed.deviceId === "string" && isTenantId(parsed.tenantId) && typeof parsed.accessToken === "string" && typeof parsed.expiresAt === "string" && typeof parsed.devicePrivateKeyPem === "string" && typeof parsed.devicePublicKey === "string") {
4708
+ return;
4709
+ }
4710
+ throw new DeviceRecordRePairRequiredError();
4711
+ }
4695
4712
  function parseDeviceRecord(raw) {
4696
- const parsed = JSON.parse(raw);
4697
- if (typeof parsed.deviceId === "string" && typeof parsed.accessToken === "string" && typeof parsed.expiresAt === "string" && typeof parsed.devicePrivateKeyPem === "string" && typeof parsed.devicePublicKey === "string") {
4698
- return {
4699
- deviceId: parsed.deviceId,
4700
- accessToken: parsed.accessToken,
4701
- expiresAt: parsed.expiresAt,
4702
- devicePrivateKeyPem: parsed.devicePrivateKeyPem,
4703
- devicePublicKey: parsed.devicePublicKey
4704
- };
4713
+ let parsed;
4714
+ try {
4715
+ parsed = JSON.parse(raw);
4716
+ } catch {
4717
+ throw new DeviceRecordRePairRequiredError();
4705
4718
  }
4706
- return void 0;
4719
+ assertDeviceRecord(parsed);
4720
+ return {
4721
+ deviceId: parsed.deviceId,
4722
+ tenantId: parsed.tenantId,
4723
+ accessToken: parsed.accessToken,
4724
+ expiresAt: parsed.expiresAt,
4725
+ devicePrivateKeyPem: parsed.devicePrivateKeyPem,
4726
+ devicePublicKey: parsed.devicePublicKey
4727
+ };
4707
4728
  }
4708
4729
  var DeviceStore = class _DeviceStore {
4709
4730
  /**
@@ -4773,6 +4794,7 @@ var DeviceStore = class _DeviceStore {
4773
4794
  }
4774
4795
  }
4775
4796
  async save(record) {
4797
+ assertDeviceRecord(record);
4776
4798
  const storeDir = path.dirname(this.filePath);
4777
4799
  await ensureSecureDir(storeDir, this.secureDirOptions);
4778
4800
  await atomicWriteFile(this.filePath, JSON.stringify(record, null, 2), { mode: 384 });
@@ -4943,7 +4965,14 @@ var AuthManager = class {
4943
4965
  this.proactiveTimer = void 0;
4944
4966
  try {
4945
4967
  return await this.runCredentialMutation(async () => {
4946
- const existing = this.record ?? await this.opts.store.load();
4968
+ let existing = this.record;
4969
+ if (!existing) {
4970
+ try {
4971
+ existing = await this.opts.store.load();
4972
+ } catch (error) {
4973
+ if (!(error instanceof DeviceRecordRePairRequiredError)) throw error;
4974
+ }
4975
+ }
4947
4976
  const keyPair = existing ? { privateKey: importPrivateKeyPem(existing.devicePrivateKeyPem), publicKeyBase64Url: existing.devicePublicKey } : generateDeviceKeyPair();
4948
4977
  const url = new URL(BYOK_PAIR_PATH, toHttpBase(this.opts.serverUrl));
4949
4978
  const res = await fetch(url, {
@@ -4958,9 +4987,10 @@ var AuthManager = class {
4958
4987
  if (!res.ok) {
4959
4988
  throw new Error(`pairing failed: HTTP ${res.status} ${await safeErrorText(res)}`.trimEnd());
4960
4989
  }
4961
- const body = await res.json();
4990
+ const body = PairResponseSchema.parse(await res.json());
4962
4991
  const record = {
4963
4992
  deviceId: body.deviceId,
4993
+ tenantId: body.tenantId,
4964
4994
  accessToken: body.accessToken,
4965
4995
  expiresAt: resolvePairExpiry(body.refreshHint),
4966
4996
  devicePrivateKeyPem: exportPrivateKeyPem(keyPair.privateKey),
@@ -12345,9 +12375,14 @@ var AgentEgressController = class {
12345
12375
  latestStatus = emptyLane();
12346
12376
  reliableStatus = emptyLane();
12347
12377
  drops = [];
12378
+ active = true;
12348
12379
  get policy() {
12349
12380
  return this.options.policy;
12350
12381
  }
12382
+ /** Permanently fail closed after its authenticated enrollment is replaced. */
12383
+ deactivate() {
12384
+ this.active = false;
12385
+ }
12351
12386
  status() {
12352
12387
  const reliable = this.reliableRecords();
12353
12388
  return Object.freeze({
@@ -12365,6 +12400,10 @@ var AgentEgressController = class {
12365
12400
  /** Project before TaskRunner builds a `task.progress` envelope. */
12366
12401
  projectLatestValue(input) {
12367
12402
  if (input.agentRef === void 0) return Object.freeze([...input.events]);
12403
+ if (!this.active) {
12404
+ this.noteDrop("latest-value", "policy_denied", input.agentRef);
12405
+ return [];
12406
+ }
12368
12407
  if (this.options.policy.activity.mode === "contentful-trajectory" && !input.serverCapabilities.includes("agent-egress-policy")) {
12369
12408
  this.noteDrop("latest-value", "capability_missing", input.agentRef);
12370
12409
  return [];
@@ -12387,7 +12426,7 @@ var AgentEgressController = class {
12387
12426
  return latest === void 0 ? [] : Object.freeze([latest]);
12388
12427
  }
12389
12428
  async appendReliable(input) {
12390
- if (this.options.tenantId === void 0) {
12429
+ if (!this.active || this.options.tenantId === void 0) {
12391
12430
  this.noteDrop("reliable", "policy_denied", input.agentRef);
12392
12431
  return { ok: false, reason: "policy_denied" };
12393
12432
  }
@@ -12425,7 +12464,7 @@ var AgentEgressController = class {
12425
12464
  * with `wireType: agent.content.receipt` before any transport attempt.
12426
12465
  */
12427
12466
  async appendContentReceipt(input) {
12428
- if (this.options.tenantId === void 0) {
12467
+ if (!this.active || this.options.tenantId === void 0) {
12429
12468
  this.noteDrop("reliable", "policy_denied", input.agentRef);
12430
12469
  return { ok: false, reason: "policy_denied" };
12431
12470
  }
@@ -12449,7 +12488,7 @@ var AgentEgressController = class {
12449
12488
  /** Retires only the record whose full Agent/tenant/revision/id/cursor tuple matches. */
12450
12489
  async acknowledge(ack) {
12451
12490
  const spool = this.spools.get(agentKey(ack.agentRef));
12452
- if (!spool || this.options.tenantId === void 0 || ack.tenantId !== this.options.tenantId) {
12491
+ if (!this.active || !spool || this.options.tenantId === void 0 || ack.tenantId !== this.options.tenantId) {
12453
12492
  this.noteDrop("reliable", "ack_mismatch", ack.agentRef);
12454
12493
  return false;
12455
12494
  }
@@ -12460,6 +12499,7 @@ var AgentEgressController = class {
12460
12499
  /** Re-open every existing Agent-local spool before retrying stable records after restart. */
12461
12500
  async recover(agentsRoot) {
12462
12501
  if (!path.isAbsolute(agentsRoot)) throw new Error("Agent egress recovery root must be absolute");
12502
+ if (!this.active) throw new Error("Agent egress recovery requires an active authenticated enrollment");
12463
12503
  if (this.options.tenantId === void 0) {
12464
12504
  throw new Error("Agent egress recovery requires one authenticated tenant authority");
12465
12505
  }
@@ -13567,9 +13607,6 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13567
13607
  if (config.agentEgress !== void 0 && config.agentHome === void 0) {
13568
13608
  throw new Error("DaemonConfig.agentEgress requires DaemonConfig.agentHome for the per-Agent local spool");
13569
13609
  }
13570
- if (config.agentEgress !== void 0 && (config.agentEgress.tenantId.length === 0 || config.agentEgress.tenantId.trim() !== config.agentEgress.tenantId)) {
13571
- throw new Error("DaemonConfig.agentEgress.tenantId must be a non-empty canonical authenticated tenant id");
13572
- }
13573
13610
  const egressPolicy = resolveAgentEgressPolicy(config.agentEgress?.policy);
13574
13611
  const egressBatcherOptions = egressPolicy.activity.mode === "contentful-trajectory" ? {
13575
13612
  ...config.progressBatch,
@@ -13600,9 +13637,8 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13600
13637
  });
13601
13638
  const agentSessionHandoffs = config.agentHome === void 0 ? void 0 : new AgentSessionHandoffStore();
13602
13639
  const agentContentReadPolicies = resolveContentReadPolicies(egressPolicy, config.agentEgress?.contentRead);
13603
- const agentEgress = new AgentEgressController({
13640
+ let agentEgress = new AgentEgressController({
13604
13641
  policy: egressPolicy,
13605
- ...config.agentEgress?.tenantId === void 0 ? {} : { tenantId: config.agentEgress.tenantId },
13606
13642
  ...config.agentEgress?.sanitizer === void 0 ? {} : { sanitizer: config.agentEgress.sanitizer }
13607
13643
  });
13608
13644
  const gitWorkspaceManager = config.gitWorkspace ? overrides.gitWorkspace?.manager ?? new GitWorkspaceManager(config.workspaceRoot, { ownerId: stableGitWorkspaceOwnerId(storeDir, config.productId) }) : void 0;
@@ -13612,11 +13648,6 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13612
13648
  if (config.hostedJournal.mode !== "sqlite") {
13613
13649
  throw new Error(`DaemonConfig.hostedJournal.mode must be "sqlite" \u2014 got ${JSON.stringify(config.hostedJournal.mode)}`);
13614
13650
  }
13615
- if (typeof config.hostedJournal.tenantId !== "string" || config.hostedJournal.tenantId.trim() === "") {
13616
- throw new Error(
13617
- "DaemonConfig.hostedJournal.tenantId must be a non-empty tenant id \u2014 a hosted journal row with no tenant is durable evidence nobody can act on"
13618
- );
13619
- }
13620
13651
  if (config.hostedJournal.storagePolicy) {
13621
13652
  resolvedStoragePolicy = resolveLocalStoragePolicy(config.hostedJournal.storagePolicy);
13622
13653
  }
@@ -13685,6 +13716,8 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13685
13716
  const approvalRegistry = new ApprovalRegistry();
13686
13717
  let connection;
13687
13718
  let connectionState = "closed";
13719
+ let daemonStarted = false;
13720
+ let tenantRebinding = false;
13688
13721
  let runner;
13689
13722
  let controlServerHandle;
13690
13723
  let daemonOwnerLease;
@@ -13737,11 +13770,31 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13737
13770
  return runLifecycleMutation(() => pairUnderLease(pairingCode));
13738
13771
  }
13739
13772
  async function pairUnderLease(pairingCode) {
13773
+ const wasRunning = daemonStarted;
13774
+ if (wasRunning) tenantRebinding = true;
13740
13775
  const acquiredHere = daemonOwnerLease === void 0;
13741
13776
  if (acquiredHere) daemonOwnerLease = await acquireDaemonOwner(storeDir, "daemon");
13777
+ let replacementPersisted = false;
13742
13778
  try {
13743
- const previous = await store.load();
13779
+ let previous;
13780
+ try {
13781
+ previous = await store.load();
13782
+ } catch (error) {
13783
+ if (!(error instanceof DeviceRecordRePairRequiredError)) throw error;
13784
+ }
13744
13785
  const record = await auth.pair(pairingCode);
13786
+ replacementPersisted = true;
13787
+ if (config.agentEgress !== void 0) {
13788
+ agentEgress.deactivate();
13789
+ agentEgress = new AgentEgressController({
13790
+ policy: egressPolicy,
13791
+ ...config.agentEgress.sanitizer === void 0 ? {} : { sanitizer: config.agentEgress.sanitizer }
13792
+ });
13793
+ }
13794
+ if (wasRunning) {
13795
+ await runShutdownSequence("re-pairing enrollment binding");
13796
+ tenantRebinding = false;
13797
+ }
13745
13798
  if (previous && previous.deviceId !== record.deviceId) {
13746
13799
  await cursorStore.clear(config.serverUrl, previous.deviceId);
13747
13800
  }
@@ -13752,6 +13805,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13752
13805
  }
13753
13806
  return record;
13754
13807
  } catch (err) {
13808
+ if (wasRunning && !replacementPersisted) tenantRebinding = false;
13755
13809
  if (acquiredHere) {
13756
13810
  await daemonOwnerLease?.release();
13757
13811
  daemonOwnerLease = void 0;
@@ -13774,6 +13828,13 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13774
13828
  if (!record) {
13775
13829
  throw new Error("device is not paired yet; call pair(pairingCode) first");
13776
13830
  }
13831
+ if (config.agentEgress !== void 0) {
13832
+ agentEgress = new AgentEgressController({
13833
+ policy: egressPolicy,
13834
+ tenantId: record.tenantId,
13835
+ ...config.agentEgress.sanitizer === void 0 ? {} : { sanitizer: config.agentEgress.sanitizer }
13836
+ });
13837
+ }
13777
13838
  await agentHomeManager?.preflight();
13778
13839
  if (config.agentEgress !== void 0 && config.agentHome !== void 0) {
13779
13840
  await agentEgress.recover(path.join(config.agentHome.hostStorageRoot, "agents"));
@@ -13834,7 +13895,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13834
13895
  config.agentEgress !== void 0,
13835
13896
  agentContentReadPolicies
13836
13897
  );
13837
- const journalIdentity = config.hostedJournal ? { tenantId: config.hostedJournal.tenantId, productId: config.productId, deviceId: record.deviceId } : void 0;
13898
+ const journalIdentity = config.hostedJournal ? { tenantId: record.tenantId, productId: config.productId, deviceId: record.deviceId } : void 0;
13838
13899
  const sendSanitizedEnvelope = activeJournal && journalIdentity ? (envelope) => {
13839
13900
  observer.handleOutboundEnvelope(envelope);
13840
13901
  const terminalKind = terminalKindOf(envelope.type);
@@ -13972,9 +14033,8 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13972
14033
  runner = new TaskRunner(deps);
13973
14034
  const handleAgentEgressEnvelope = async (envelope) => {
13974
14035
  if (envelope.type !== "agent.egress.ack") return false;
13975
- const tenantId2 = config.agentEgress?.tenantId;
13976
- if (tenantId2 === void 0) return true;
13977
- await agentEgress.acknowledge({ ...envelope.payload, tenantId: tenantId2 });
14036
+ if (config.agentEgress === void 0) return true;
14037
+ await agentEgress.acknowledge({ ...envelope.payload, tenantId: record.tenantId });
13978
14038
  return true;
13979
14039
  };
13980
14040
  const handleAgentContentReadEnvelope = async (envelope) => {
@@ -14026,7 +14086,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14026
14086
  const result = await policyEngine.read({
14027
14087
  requestId: payload.requestId,
14028
14088
  actor: payload.actor,
14029
- tenantId: config.agentEgress.tenantId,
14089
+ tenantId: record.tenantId,
14030
14090
  deviceId: record.deviceId,
14031
14091
  agentRef: payload.agentRef,
14032
14092
  surface: payload.surface,
@@ -14122,6 +14182,9 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14122
14182
  //
14123
14183
  // The no-journal branch is the ORIGINAL closure, unchanged.
14124
14184
  onEnvelope: activeJournal && journalIdentity ? async (envelope) => {
14185
+ if (tenantRebinding) {
14186
+ throw new Error("tenant enrollment is being re-paired; inbound work is blocked until restart");
14187
+ }
14125
14188
  observer.handleInboundEnvelope(envelope);
14126
14189
  if (await handleAgentEgressEnvelope(envelope)) return;
14127
14190
  if (await handleAgentContentReadEnvelope(envelope)) return;
@@ -14129,6 +14192,9 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14129
14192
  await activeJournal.appendEnvelope(toJournalEnvelopeRecord(envelope, journalIdentity));
14130
14193
  return runner?.handleEnvelope(envelope) ?? Promise.resolve();
14131
14194
  } : (envelope) => {
14195
+ if (tenantRebinding) {
14196
+ return Promise.reject(new Error("tenant enrollment is being re-paired; inbound work is blocked until restart"));
14197
+ }
14132
14198
  observer.handleInboundEnvelope(envelope);
14133
14199
  if (envelope.type === "agent.egress.ack") return handleAgentEgressEnvelope(envelope).then(() => void 0);
14134
14200
  if (envelope.type === "agent.content.read") return handleAgentContentReadEnvelope(envelope).then(() => void 0);
@@ -14160,6 +14226,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14160
14226
  dispatchReliableRecord(record2);
14161
14227
  }
14162
14228
  startPresenceProducer();
14229
+ daemonStarted = true;
14163
14230
  } catch (err) {
14164
14231
  try {
14165
14232
  await runShutdownSequence("startup failed", { drainTimeoutMs: 0 });
@@ -14304,6 +14371,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14304
14371
  if (!mutationBarrierComplete) {
14305
14372
  throw new Error("daemon shutdown mutation barrier is incomplete; ownership lease retained");
14306
14373
  }
14374
+ daemonStarted = false;
14307
14375
  }
14308
14376
  async function stop(opts = {}) {
14309
14377
  shuttingDown = true;
@@ -14625,6 +14693,9 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14625
14693
  if (config.agentEgress === void 0 || agentHomeManager === void 0) {
14626
14694
  throw new Error("Agent reliable egress is not configured");
14627
14695
  }
14696
+ if (tenantRebinding) {
14697
+ throw new Error("tenant enrollment is being re-paired; reliable egress is blocked until restart");
14698
+ }
14628
14699
  const binding = await agentHomeManager.acquire(input.agentRef);
14629
14700
  try {
14630
14701
  await agentHomeManager.initialize(binding);